Last Updated on May 10, 2023
Table of contents:
- What is Backtesting.py?
- What is Backtesting.py used for?
- Why should I use Backtesting.py?
- Why shouldn’t I use Backtesting.py?
- Is Backtesting.py free?
- What are some Backtesting.py alternatives?
- How to get started with Backtesting.py?
- How to get data with Backtesting.py?
- How to use technical indicators with Backtesting.py?
- How to define entries and exits with Backtesting.py?
- What strategy types does Backtesting.py offer?
- How to code a mean-reversion strategy with Backtesting.py?
- How to perform backtesting with Backtesting.py?
- How to perform optimizations with Backtesting.py?
- How to use multiple time frames with Backtesting.py?
- Where can I learn more about Backtesting.py?
- Full code
What is Backtesting.py?
Backtesting.py is an open-source backtesting Python library that allows users to test their trading strategies via code.
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()
Open High Low Close Volume 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
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
What strategy types does Backtesting.py offer?
Backtesting.py has three main types of strategies which are the following:
- Strategy – A trading strategy base class. Extend this class and override methods
Strategy.next()to define your own strategy.
- Trailing Strategy – A strategy with automatic trailing stop-loss, trailing the current price at a distance of some multiple of the average true range (ATR).
- Signal Strategy – A simple helper strategy that operates on position entry/exit signals. This makes the backtest of the strategy simulate a vectorized backtest.
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.
from 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.
How to perform backtesting with Backtesting.py?
To perform backtesting with Backtesting.py, you will need to import the
Backtest module and pass it the data, the strategy class, set initial cash, and the trade commission value. Below is an example of how to run a backtest and view its results.
from backtesting import Backtest bt = Backtest(he, MeanReversion, cash=10000, commission=0.002) stats = bt.run() bt.plot() stats
Start 2023-01-17 09:30... End 2023-03-08 16:00... Duration 50 days 06:30:00 Exposure Time [%] 43.436499 Equity Final [$] 10104.838029 Equity Peak [$] 10291.717912 Return [%] 1.04838 Buy & Hold Return [%] -7.059514 Return (Ann.) [%] 7.573549 Volatility (Ann.) [%] 18.320732 Sharpe Ratio 0.413387 Sortino Ratio 0.748415 Calmar Ratio 1.805436 Max. Drawdown [%] -4.194859 Avg. Drawdown [%] -0.888331 Max. Drawdown Duration 8 days 02:45:00 Avg. Drawdown Duration 1 days 05:46:00 # Trades 3 Win Rate [%] 66.666667 Best Trade [%] 1.008354 Worst Trade [%] -0.502406 Avg. Trade [%] 0.34971 Max. Trade Duration 13 days 02:45:00 Avg. Trade Duration 6 days 19:20:00 Profit Factor 3.100131 Expectancy [%] 0.351706 SQN 0.776997
Have in mind that this is just an example strategy for showcasing the library. It shouldn’t be used for real trading.
How to perform optimizations with Backtesting.py?
To perform optimizations with Backtesting.py, we can utilize the
optimize function that can receive parameters to optimize and the metric to optimize for. For example, let’s optimize the rolling window size for our previous strategy.
stats = bt.optimize( roll=range(10, 60, 5), maximize="Equity Final [$]", constraint=lambda p: p.roll > 10, ) stats
Start 2023-01-17 09:30... End 2023-03-08 16:00... Duration 50 days 06:30:00 Exposure Time [%] 4.802561 Equity Final [$] 10149.401675 Equity Peak [$] 10149.401675 Return [%] 1.494017 Buy & Hold Return [%] -7.059514 Return (Ann.) [%] 10.938703 Volatility (Ann.) [%] 3.333441 Sharpe Ratio 3.281505 Sortino Ratio inf Calmar Ratio 8.567644 Max. Drawdown [%] -1.276746 Avg. Drawdown [%] -0.428759 Max. Drawdown Duration 0 days 04:30:00 Avg. Drawdown Duration 0 days 01:45:00 # Trades 2 Win Rate [%] 100.0 Best Trade [%] 1.041394 Worst Trade [%] 0.45102 Avg. Trade [%] 0.745774 Max. Trade Duration 0 days 05:30:00 Avg. Trade Duration 0 days 05:23:00 Profit Factor NaN Expectancy [%] 0.746207 SQN 2.52049
To access the final value the strategy landed on, we can access the
Be careful to not overfit the strategy and play with different buy/sell order settings.
To learn more about overfitting, check out: https://algotrading101.com/learn/what-is-overfitting-in-trading/
How to use multiple time frames with Backtesting.py?
To use multiple time series with Backtesting.py, you can use the
resample_apply() function which will allow you to resample the data to accommodate another time frame.
Here is an example of how to use them that is provided by Backtesting.py.