How to navigate the backtrader documentation and build your first strategy

September 4, 2026
Facebook logo.
Twitter logo.
LinkedIn logo.
Newsletter issue count indicatorNewsletter total issues indicator
How to navigate the backtrader documentation and build your first strategy

How to navigate the backtrader documentation and build your first strategy

The backtrader documentation gives you everything you need to test trading strategies on past market data in Python, but the first read can feel dense. This guide explains the few parts you actually need first, then builds a working example you can run. If you want a broader introduction, the step-by-step backtesting guide covers the fundamentals, and the comprehensive backtrader guide goes deeper on configuration.

Backtrader is a Python framework for testing a trading strategy against historical prices to see how it would have performed. You write your buy and sell rules in Python, feed in price data, and backtrader simulates the trades and tracks your portfolio value. It's open source and has been around for years. Individual traders and small teams use it to test ideas before they commit real money.

How the Backtrader Documentation Is Organized

The official backtrader documentation is organized around the library's main components. Knowing what each component does before you read the docs makes everything more approachable.

Cerebro: The Engine That Runs Everything

Cerebro is backtrader's central engine. You create one instance, attach your data and strategy, and call cerebro.run(). It controls the simulation, sends each new price bar to your strategy, and updates cash and positions.

In practice, you'll use adddata() to attach price history and addstrategy() to load your trading rules. Later, you may also use addsizer() to control trade sizes.

Strategy: Where Your Trading Logic Lives

The Strategy class is where you write your buy and sell rules. You subclass bt.Strategy and override two methods. __init__ is where you set up indicators, such as moving averages. next runs once for each bar of data, and it's where you decide whether to buy, sell, or do nothing.

The Strategy class has many methods, but most beginners spend most of their time in next(). You'll also call buy(), sell(), and close() often.

Data Feeds: Getting Price Data In

Backtrader needs historical price data in a specific format. In practice, that means your data needs dates plus columns for open, high, low, close, and usually volume, with names the feed class can map correctly.

The docs describe several built-in data feed classes. bt.feeds.YahooFinanceCSVData reads CSV files downloaded from Yahoo Finance. bt.feeds.PandasData lets you pass in a pandas DataFrame directly, which is useful if you're pulling data from an API or database. Each data feed gives your strategy access to self.data.close, self.data.open, self.data.high, self.data.low, and self.data.volume for the current bar.

If Yahoo Finance changes its CSV column names or date format, you may need to adjust the feed settings or load the file with pandas first.

Indicators: Built-In Calculations

Backtrader includes many built-in indicators. For example, it can calculate a simple moving average, or RSI, which measures whether price strength has recently leaned up or down. The documentation lists them under the Indicators section. You create them in your strategy's __init__ method, and backtrader calculates their values before each call to next.

Those four pieces work together in every backtrader project, so the example below uses each one in a minimal setup.

Build a Strategy From the Backtrader Documentation

We'll build a moving average crossover strategy, which is a simple way to learn how to test trading rules on past market data. When a short-term moving average crosses above a long-term moving average, buy. When it crosses below, sell. This strategy is useful for learning how backtrader works, but you shouldn't assume it will make money in real markets without much more testing. For additional variations, the guide on creating and testing strategies with backtrader walks through several.

Install and Import

# pip install backtrader matplotlib
import backtrader as bt
import datetime

Define the Strategy

class SmaCross(bt.Strategy):
    params = (
        ("fast_period", 10),
        ("slow_period", 30),
    )

    def __init__(self):
        fast_ma = bt.indicators.SimpleMovingAverage(
            self.data.close, period=self.params.fast_period
        )
        slow_ma = bt.indicators.SimpleMovingAverage(
            self.data.close, period=self.params.slow_period
        )
        self.crossover = bt.indicators.CrossOver(fast_ma, slow_ma)

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

The params tuple defines adjustable settings. You can change fast_period and slow_period without editing the strategy logic. In __init__, we create two simple moving averages and a CrossOver indicator that returns 1 when the fast average crosses above the slow one, and -1 when it crosses below.

In next, self.position tells us whether we currently hold any shares. The [0] means "the current value on this bar." If we don't hold anything and the crossover just turned positive, we buy. If we're holding shares and the crossover turned negative, we close the position (sell everything).

Set Up Cerebro and Run

cerebro = bt.Cerebro()
cerebro.addstrategy(SmaCross)

data = bt.feeds.YahooFinanceCSVData(
    dataname="AAPL.csv",
    fromdate=datetime.datetime(2020, 1, 1),
    todate=datetime.datetime(2023, 12, 31),
)
cerebro.adddata(data)

cerebro.broker.setcash(100000)
cerebro.broker.setcommission(commission=0.001)

print(f"Starting portfolio value: {cerebro.broker.getvalue():.2f}")
cerebro.run()
print(f"Final portfolio value: {cerebro.broker.getvalue():.2f}")

cerebro.plot()

This code creates the Cerebro object and loads the strategy. It then reads Apple price data from a CSV file, sets starting cash to $100,000, and charges a commission (the trading cost on each buy or sell) of 0.1% per order. After the run finishes, it prints the starting and ending portfolio values and generates a chart.

You can download historical CSV data from Yahoo Finance directly, or use the yfinance Python package to fetch it and save it to a file.

Backtrader Documentation Sections Most People Skip

After you understand the basic example, a few less visible sections of the backtrader documentation become worth reading.

Sizers

A sizer controls how many shares you buy on each trade. The default buys as many shares as your cash allows. The docs describe built-in sizers such as bt.sizers.PercentSizer, which invests a fixed percentage of your portfolio on each trade. How much you put into each trade has a huge effect on your results, so this section matters more than it looks.

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

Analyzers

Analyzers calculate performance statistics after the test on past data finishes. The docs include analyzers for return quality and loss size. For example, maximum drawdown, which means the largest drop from a portfolio high to a later low, shows how bad the worst decline was.

cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name="sharpe")
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")

results = cerebro.run()
strat = results[0]

sharpe_data = strat.analyzers.sharpe.get_analysis()
drawdown_data = strat.analyzers.drawdown.get_analysis()

print("Sharpe Ratio:", sharpe_data.get("sharperatio"))
print("Max Drawdown:", drawdown_data.get("max", {}).get("drawdown"))

This version uses .get() instead of direct dictionary access, so it won't crash if the analyzer returns None for a given metric.

Observers

Observers are visual tools that add extra information to the chart. The default observers show your cash balance and portfolio value over time. The docs explain how to add custom observers or disable the defaults. For more on observer configuration, the effective backtesting with backtrader resource covers this in detail.

Common Backtrader Documentation Pitfalls

The backtrader documentation assumes you understand a few things that often trip up newcomers.

Backtrader uses negative indexing for historical data, which means [0] is the current bar and [-1] is the previous one. self.data.close[1] would be tomorrow's close, which you should never use when testing on past data because it creates look-ahead bias (using future information to make past decisions, which makes your results unrealistically good).

Indicators need a warm-up period. A 30-day moving average can't produce a value until 30 bars of data have passed. Backtrader waits until enough data exists before your indicators produce normal values. If your strategy seems to skip the first few weeks of data, this is why.

The params system uses tuples of tuples, not dictionaries. This syntax looks unusual if you're used to standard Python, but it's how backtrader handles parameter optimization internally.

By default, a market order usually fills on the next bar, not on the same bar where your code calls buy() or sell(). This often catches beginners off guard when they compare their expected trades to the actual results.

If Your Strategy Doesn't Trade

First check that your CSV dates fall inside fromdate and todate. Then add a print statement inside next to confirm the moving averages and crossover values actually change. Also check that your starting cash is high enough to buy at least one share at the stock's price.

Next Steps

The official backtrader documentation is the main reference, but it reads more like a technical function-by-function guide than a beginner lesson. Once you understand Cerebro, Strategy, data feeds, and indicators, the rest of the docs become much easier to navigate. Start with the example above, get it running, then change the parameters or swap in different indicators to see what happens. The mastering backtrader guide is a good next read after this one.

The free Algorithmic Trading With Python guides go further. They show how to build more strategies, control trade sizes, and connect code to live market data.