FAQ

How to write your first, simple backtrader strategy

How to write your first, simple backtrader strategy

There are multiple libraries that you can use to backtest a strategy in Python. However, I often use Backtrader because it is a nice open-source framework that allows me to quickly test historical data, optimize strategies, and create visual plots. Backtrader comes across as being really flexible and quite easy to use – which makes it a reasonable choice, especially for beginner algorithmic traders. 

In this learning session, I will demonstrate how you can write your own simple backtrader strategy in Python. 

Step 1: Important Libraries 

The first step is to import important libraries to load some of the functionality that we are going to need (so we don’t have to write this functionality from scratch). These libraries include backtrader, backtrader analyzers (for monitoring our strategy), matplotlib (for plotting results), and datetime (for working with dates). 

import backtrader as bt
import backtrader.analyzers as btanalyzers
import matplotlib
from datetime import datetime

Step 2: Create Strategy (Class)

The next step is to create an empty class for our strategy. 

class MaCrossStrategy(bt.Strategy):

You then need to define (at least two) functions for your strategy. One function will initialize the strategy while the other one will specify logic for orders (i.e. when your engine should enter and exit positions). 

Strategy initialization function 

Note: in this demonstration, I used a moving average strategy where I can store crossover signals.

    def __init__(self):
        ma_fast = bt.ind.SMA(period = 10)
        ma_slow = bt.ind.SMA(period = 50)
         
        self.crossover = bt.ind.CrossOver(ma_fast, ma_slow)

Logic function 

In this demonstration, I created a function with a pretty basic logic implemented via conditional (if) statements. 

Logic: If you are not in position then you see a bullish crossover then you enter a long position then if you are in position and you see bullish crossover you close position. 

    def next(self):
        if not self.position:
            if self.crossover > 0:
                self.buy()
        elif self.crossover < 0:
            self.close()

Step 3: Create Engine 

Backtrader’s Cerebro engine gathers all inputs (Data Feeds), actors (Strategies), spectators (Observers), critics (Analyzers), and documenters to execute backtesting, return results, measure performance, and provide plotting facilities. 

 Here’s a one-liner to instantiate your Cerebro engine. 

cerebro = bt.Cerebro()

→ Add Data

The next step is to get data (using feeds such as Yahoo Finance) for the Cerebro engine you have created. Keep in mind that you also need to specify parameters such as the period of time over which you want to obtain data for the data. 

data = bt.feeds.YahooFinanceData(dataname = 'AAPL', fromdate = datetime(2010, 1, 1), todate = datetime(2020, 1, 1))
cerebro.adddata(data)

→ Add Strategy 

Now that you already have loaded the data, it’s time to add the strategy that you created in the second step above to your engine.

cerebro.addstrategy(MaCrossStrategy)

Note: we could run our strategy at this juncture but I prefer we add a few more useful things (below) so we make the most out of it.   

→ Set Initial Capital (beginning balance)

This can be done using the cash function.

cerebro.broker.setcash(1000000.0)

→ Change Position Size

You may change the way the engine sizes your position. By default, it only trades about one share but there’s a Celebro function to change it to the desired size.

cerebro.addsizer(bt.sizers.PercentSizer, percents = 10)

→ Add Analyzers 

Backtrader has a variety of analyzers that you may use without having to create your own. In this example, I used SharpeRatio, Transactions, and TradeAnalyzer.

cerebro.addanalyzer(btanalyzers.SharpeRatio, _name = "sharpe")
cerebro.addanalyzer(btanalyzers.Transactions, _name = "trans")
cerebro.addanalyzer(btanalyzers.TradeAnalyzer, _name = "trades")a

Note: These analyzers will monitor your traders and show detailed information about your transactions. 

Step 4: Run Engine and Assess Performance 

The final step is to get the results of our backtesting and then call the analyzers to assess for performance. 

Here is the code to accomplish this final bit of your first backtrader strategy.

back = cerebro.run()		  #collect results of backtesting 
cerebro.broker.getvalue()	 #get an interval of your basic capital 
back[0].analyzers.sharpe.get_analysis()	 #get the first element of your #backtesting and call SharpeRatio analyzer
back[0].analyzers.trans.get_analysis() 	 #get the first element of your #backtesting and call Transactions analyzer
back[0].analyzers.trades.get_analysis()	 #get the first element of your #backtesting and call TradeAnalyzer

Note: you get a lot of useful information from this analysis plus you can also try other analyzers already available in Backtrader (check documentation).

Cerebro also has a plot function that you can use to quickly plot basic information about your strategy and performance. 

cerebro.plot()

Here’s the aggregate/combined code snippet.

import backtrader as bt
import backtrader.analyzers as btanalyzers
import matplotlib
from datetime import datetime
 
class MaCrossStrategy(bt.Strategy):
 
    def __init__(self):
        ma_fast = bt.ind.SMA(period = 10)
        ma_slow = bt.ind.SMA(period = 50)
         
        self.crossover = bt.ind.CrossOver(ma_fast, ma_slow)
 
    def next(self):
        if not self.position:
            if self.crossover > 0: 
                self.buy()
        elif self.crossover < 0: 
            self.close()
 
cerebro = bt.Cerebro()
 
data = bt.feeds.YahooFinanceData(dataname = 'AAPL', fromdate = datetime(2010, 1, 1), todate = datetime(2020, 1, 1))
cerebro.adddata(data)
 
cerebro.addstrategy(MaCrossStrategy)
 
cerebro.broker.setcash(1000000.0)
 
cerebro.addsizer(bt.sizers.PercentSizer, percents = 10)
 
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name = "sharpe")
cerebro.addanalyzer(btanalyzers.Transactions, _name = "trans")
cerebro.addanalyzer(btanalyzers.TradeAnalyzer, _name = "trades")
 
back = cerebro.run()
 
cerebro.broker.getvalue()
 
back[0].analyzers.sharpe.get_analysis()
 
back[0].analyzers.trans.get_analysis()
 
back[0].analyzers.trades.get_analysis()
 
cerebro.plot()

Follow me on TradingView and YouTube.

This image has an empty alt attribute; its file name is wide.png
Pine Script Programming Courses
Pine Script Programming Courses
Learn to build your own TradingView Indicators and Strategies
Sidebar Signup Form
If you want to be the first in this business, subscribe to the latest news