Backtesting.py – An Introductory Guide to Backtesting with Python

The article “Backtesting.py – An Introductory Guide to Backtesting with Python” first appeared on AlgoTrading101 blog.

Excerpt

What is Backtesting.py?

Backtesting.py is an open-source backtesting Python library that allows users to test their trading strategies via code.

Link: https://github.com/kernc/backtesting.py

What is Backtesting.py used for?

Algorithmic traders often use Backtesting.py to backtest, optimize, research, and improve different trading strategies. To learn more about backtesting and its benefits please read the following article:

Why should I use Backtesting.py?

  • Backtesting.py is easy to use.
  • Backtesting.py is open-sourced.
  • Is compatible with forex, crypto, stocks, futures, and more.
  • Offers interactive charts.
  • Allows for vectorized or event-based backtesting.
  • Has a built-in optimizer.
  • Is actively maintained.

Why shouldn’t I use Backtesting.py?

  • Backtesting.py could use more features.
  • Doesn’t support the use of multiple assets at the same time.
  • Data that the strategy needs is constrained (OHLCV).
  • Is heavily indicator based.
  • Complex strategies either can’t work or require hacking to work.
  • The documentation could be better.
  • The charting should be more customizable.
  • Can be easily replaced by its alternatives.

Is Backtesting.py free?

Yes, Backtesting.py is completely free and open-sourced.

What are some Backtesting.py alternatives?

Some Backtesting.py alternatives are the following:

How to get started with Backtesting.py?

To get started with Backtesting.py, you will need to install it via pip with the following command:

pip3 install backtesting

To start creating a trading strategy, you will import the Strategy object which is used within your trading algorithm classes:

from backtesting import Strategy

class Algorithm(Strategy):
      ...

For this article, I’ll be using Google Colab. Feel free to use any IDE of your choice. All code will be found on our GitHub and also at the bottom of the article. In the following sections, we’ll test out the main features that backtesting.py has to offer.

How to get data with Backtesting.py?

To import data from Backtesting.py, we will access the test module and obtain a specific asset by passing its symbol. It returns the data as a Pandas data frame.

from backtesting.test import GOOG

GOOG.head()
OpenHighLowCloseVolume
2004-08-19 100.00 104.06 95.96 100.34 22351900
2004-08-20 101.01 109.08 100.50 108.31 11428600
2004-08-23 110.75 113.48 109.05 109.40 9137200
2004-08-24 111.24 111.60 103.57 104.87 7631300
2004-08-25 104.96 108.00 103.88 106.00 4598900

Keep in mind that Backtesting only has GOOG and EURUSD for test data. Thus, you should use alternative data providers such as Yahoo Finance or Quandl.

How to use technical indicators with Backtesting.py?

To use technical indicators with Backtesting.py, you will need to import them from the test module by passing their function name. For example, if you want to obtain the Simple Moving Average indicator, you would write:

from backtesting.test import SMA

class SmaCross(Strategy):
    n1 = 20 # period of the first SMA
    n2 = 50 # period of the second SMA

    def init(self):
        close = self.data.Close # close price data
        self.sma1 = self.I(SMA, close, self.n1)
        self.sma2 = self.I(SMA, close, self.n2)

Have in mind that Backtesting.py only offers the SMA as an example, it isn’t an indicators library which means that you should build your own indicators or use a library such as TA-Lib or Tulip. Backtesting.py integrated well with both proposed libraries.

Each technical indicator can be combined with an event such as the cross and crossover.

from backtesting.lib import crossover

def next(self):
      if crossover(self.sma1, self.sma2):
          self.buy()
      elif crossover(self.sma2, self.sma1):
          self.sell()

How to define entries and exits with Backtesting.py?

Entries and exits can be defined with Backtesting.py by using conditions that can trigger a buy or sell order. An example of buy and sell order parameters are the following:

def buy(self, *, size=.9999, limit=None, stop=None, sl=None, tp=None)

def sell(self, *, size=.9999, limit=None, stop=None, sl=None, tp=None)

When an entry or exit is executed, it results in a trade. We can query existing orders through Strategy.orders.

What strategy types does Backtesting.py offer?

Backtesting.py has three main types of strategies which are the following:

class ExampleStrategy(SignalStrategy):
    def init(self):
        super().init()
        self.set_signal(sma1 > sma2, sma1 < sma2)

Knowing when to use which strategy type will help you. Keep in mind that the Strategy module is an ancestor of the Trailing and Signal strategies. Now, let’s code a pairs trade strategy as an example and backtest it.

How to code a mean-reversion strategy with Backtesting.py?

To code a mean-reversion strategy with Backtesting.py, we will first need to obtain the data of the asset we plan to trade. Then, we will lay out our strategy logic to make all the steps clear. After that, we will code it out and run the backtest.

The goal of this strategy will be to sell/short the asset if it is trading more than 3 standard deviations above the rolling mean and to buy/long the asset if it is trading more than 3 standard deviations below the rolling mean.

We will use 15-minute candles and a rolling mean of 50. The take profit will be the value of our simple moving average.

Now, let us obtain the data for the HE asset. We will do a date closer to the time of writing due to the yfinance constraints on the 15-minute data.

# Obtain OHLV data for HE
# Obtain OHLV data for HE
he = yf.download("HE", start="2023-01-15", interval="15m")[
    ["Open", "High", "Low", "Close", "Volume"]
]
he.head()

Now, let’s set up the trading strategy and the initialization logic.

rom backtesting.test import SMA

def std_3(arr, n):
    return pd.Series(arr).rolling(n).std() * 3

class MeanReversion(Strategy):
    roll = 50

    def init(self):
        self.he = self.data.Close

        self.he_mean = self.I(SMA, self.he, self.roll)
        self.he_std = self.I(std_3, self.he, self.roll)
        self.he_upper = self.he_mean + self.he_std
        self.he_lower = self.he_mean - self.he_std

        self.he_close = self.I(SMA, self.he, 1)

Above, I imported the built-in SMA indicator and coded the 3 STD indicator by hand. You can also use libraries for more complicated indicators. Then, we specified the rolling window and initialized the algorithm.

You can also see the first “hack” that needed to be introduced to adequately compare the indicator to the candle closing price. Essentially, it needed to be transformed into an indicator. There might be a better way of doing this but accessing the prices from self.he was broken at the time of writing.

Now, we will code the trading logic.

def next(self):

        if self.he_close < self.he_lower:
            self.buy(
                tp = self.he_mean,
            )

        if self.he_close > self.he_upper:
            self.sell(
                tp = self.he_mean,
            )

Now, we will wrap everything up and plot the strategy and print out its stats.

Visit AlgoTrading101 for the full tutorial.

One thought on “Backtesting.py – An Introductory Guide to Backtesting with Python”

  1. Using only SMA’s as a reference point is not enough for day traders to get an accurate estimate of price action or buy and sell zones you need to use exponential moving averages for day trading and see the actual price points where large orders are being bought and sold.

Join the Discussion

Thank you for engaging with IBKR Campus. If you have a general question, it may already be covered in our FAQs. If you have an account-specific question or concern, please reach out to Client Services.

Your email address will not be published. Required fields are marked *

Disclosure: Interactive Brokers

Information posted on IBKR Campus that is provided by third-parties does NOT constitute a recommendation that you should contract for the services of that third party. Third-party participants who contribute to IBKR Campus are independent of Interactive Brokers and Interactive Brokers does not make any representations or warranties concerning the services offered, their past or future performance, or the accuracy of the information provided by the third party. Past performance is no guarantee of future results.

This material is from AlgoTrading101 and is being posted with its permission. The views expressed in this material are solely those of the author and/or AlgoTrading101 and Interactive Brokers is not endorsing or recommending any investment or trading discussed in the material. This material is not and should not be construed as an offer to buy or sell any security. It should not be construed as research or investment advice or a recommendation to buy, sell or hold any security or commodity. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.