← Back to Home
Easy Entry into Algorithmic Trading with Backtrader and Backtester

Easy Entry into Algorithmic Trading with Backtrader and Backtester

Algorithmic trading, the practice of using computer programs to execute trading strategies, has gained immense popularity. For Python enthusiasts looking to dive into this world, Backtrader stands out as a powerful and flexible open-source framework. It allows for the testing and implementation of trading ideas with historical or live data. To get started, understanding a minimal strategy template is crucial. This article dissections a fundamental Backtrader strategy, providing a clear path for beginners.

The provided code snippet offers a complete, albeit simple, example of a Backtrader strategy. Let’s break it down step-by-step:

Python

import backtrader as bt
import yfinance as yf
import matplotlib.pyplot as plt
# %matplotlib qt5 # This line is specific to Jupyter environments for interactive plots

class MyStrategy(bt.Strategy):
    # define any strategy parameters here (optional)
    params = (
        ('maperiod', 15),  # example parameter: moving average period
    )

    def __init__(self):
        # references to data feeds, indicators, etc.
        self.dataclose = self.datas[0].close # Accessing the closing price of the primary data feed
        # simple moving average example
        self.sma = bt.indicators.SimpleMovingAverage(
            self.datas[0], period=self.params.maperiod # Using the defined 'maperiod'
        )

    def next(self):
        # called on each new bar (data point)
        if not self.position:  # Check if not in the market
            if self.dataclose[0] > self.sma[0]: # If close is above SMA
                # enter long
                self.buy()
        else: # Already in the market
            if self.dataclose[0] < self.sma[0]: # If close is below SMA
                # exit long
                self.close()

if __name__ == '__main__':
    cerebro = bt.Cerebro() # Create a Cerebro engine instance
    cerebro.addstrategy(MyStrategy, maperiod=20)  # Add the strategy, optionally overriding params

    # Fetch data using yfinance
    ticker = yf.Ticker('BTC-USD') # Example: Bitcoin USD data
    data_df = ticker.history(period='1y') # Fetch 1 year of historical data

    # Add a data feed (Pandas DataFrame)
    data_feed = bt.feeds.PandasData(dataname=data_df)
    cerebro.adddata(data_feed)

    cerebro.broker.setcash(100000.0) # Set initial trading capital
    print('Starting Portfolio Value:', cerebro.broker.getvalue())
    cerebro.run() # Execute the backtest
    print('Final Portfolio Value:', cerebro.broker.getvalue())

    cerebro.plot(iplot=False) # Plot the results (iplot=False for non-interactive plots)

Core Components Explained:

  1. Imports:

    • backtrader as bt: This is the primary import for the Backtrader library itself.
    • yfinance as yf: A popular library to download historical market data from Yahoo Finance. This is used here to fetch sample data for backtesting.
    • matplotlib.pyplot as plt: Used by Backtrader for plotting the results of the backtest.
    • # %matplotlib qt5: This is a magic command for Jupyter Notebook or IPython environments. It specifies that Matplotlib plots should be rendered in a separate, interactive Qt window. If you’re running a standard Python script, this line might be unnecessary or could cause issues if the Qt backend isn’t configured.
  2. Strategy Definition (MyStrategy(bt.Strategy)):

    • Every trading strategy in Backtrader is a class that inherits from bt.Strategy.
    • params: This class attribute is a tuple of tuples, allowing you to define parameters for your strategy. These parameters can be easily tuned without modifying the core logic. In this example, maperiod is defined with a default value of 15, representing the period for a moving average.
    • __init__(self): This is the constructor of your strategy. It’s called once when the strategy is initialized.
      • self.datas[0]: Backtrader can handle multiple data feeds. self.datas[0] refers to the first (primary) data feed added to Cerebro.
      • self.dataclose = self.datas[0].close: This creates a convenient alias to the ‘close’ price series of the primary data feed.
      • self.sma = bt.indicators.SimpleMovingAverage(...): Here, a Simple Moving Average (SMA) indicator is instantiated. It’s calculated on the primary data feed (self.datas[0]) using the period defined in self.params.maperiod. Backtrader has a rich library of built-in indicators.
    • next(self): This is the heart of the strategy. It’s called for each new bar (e.g., daily, hourly) of data once all indicators have a calculated value.
      • if not self.position:: This checks if the strategy currently holds any open position (i.e., is “in the market”). self.position is a Backtrader object that provides information about the current market position.
      • if self.dataclose[0] > self.sma[0]: This is the entry condition for a long position. It checks if the current closing price (self.dataclose[0]) is greater than the current SMA value (self.sma[0]). The [0] refers to the current bar.
      • self.buy(): If the entry condition is met, this command places a market order to buy the asset.
      • else: ... if self.dataclose[0] < self.sma[0]: self.sell(): If the strategy is already in a position, this block checks the exit condition. If the current closing price falls below the SMA, self.sell() places a market order to sell the asset (closing the long position).
  3. Execution Block (if __name__ == '__main__':)

    • This standard Python construct ensures the code within it only runs when the script is executed directly (not when imported as a module).
    • cerebro = bt.Cerebro(): Cerebro (Spanish for “brain”) is the central engine of Backtrader. It orchestrates data feeds, strategies, brokers, analyzers, and more.
    • cerebro.addstrategy(MyStrategy, maperiod=20): This line adds an instance of our MyStrategy to Cerebro. Notice that we can override the default maperiod (which was 15) directly here, setting it to 20 for this specific backtest.
    • Data Fetching with yfinance:
      • ticker = yf.Ticker('BTC-USD'): An instance of a yfinance Ticker object is created for Bitcoin against the US Dollar.
      • data_df = ticker.history(period='1y'): The history method fetches historical data for the specified period (here, ‘1y’ for one year) and returns it as a Pandas DataFrame.
    • Adding Data Feed to Cerebro:
      • data_feed = bt.feeds.PandasData(dataname=data_df): Backtrader needs data in a specific format. bt.feeds.PandasData is a convenient way to feed data from a Pandas DataFrame. The dataname argument points to our fetched DataFrame. Backtrader automatically recognizes common column names like ‘Open’, ‘High’, ‘Low’, ‘Close’, ‘Volume’, and ‘OpenInterest’.
      • cerebro.adddata(data_feed): The prepared data feed is added to Cerebro.
    • Broker Setup:
      • cerebro.broker.setcash(100000.0): This sets the initial cash balance for the simulated broker.
    • Running the Backtest:
      • print('Starting Portfolio Value:', cerebro.broker.getvalue()): Prints the initial portfolio value (which will be the cash set).
      • cerebro.run(): This command starts the backtesting process. Cerebro will iterate through the historical data, calling the next() method of the strategy for each bar.
      • print('Final Portfolio Value:', cerebro.broker.getvalue()): After the backtest is complete, this prints the final value of the portfolio.
    • Plotting Results:
      • cerebro.plot(iplot=False): Backtrader provides a convenient plotting facility. iplot=False is often used for non-Jupyter environments or when a static plot is preferred. If iplot=True (the default if matplotlib is configured for interactive backends), it would attempt to generate an interactive plot. The plot typically shows the price data, indicators (like our SMA), and buy/sell trades.

How This Simple Strategy Works:

This template implements a basic trend-following strategy using a Simple Moving Average (SMA):

This is a very straightforward strategy and serves primarily as an educational example of how to structure code within the Backtrader framework.

I added the simple strategy class into the “Backtester” app: Pasted image 20250506222531.png

and run backtest:

Pasted image 20250506222608.png

Expanding Beyond the Minimal:

This minimal template is a fantastic starting point. From here, you can:

By understanding this foundational template, aspiring algorithmic traders can confidently begin building and testing their own sophisticated trading strategies using the versatile Backtrader library.