Backtest class is the entry point for running and optimizing trading strategies. Initialize it with historical data and a strategy class, then call run(), optimize(), or plot().
Constructor
OHLCV price data. Must contain columns
Open, High, Low, and Close. A Volume column is optional — if omitted, it is set to NaN. The index must be a pd.DatetimeIndex (recommended) or a monotonic range index.A
Strategy subclass (the class itself, not an instance). The class must implement init() and next() methods.Initial cash balance. All trades are sized relative to this starting capital.
Constant bid-ask spread as a fraction of the entry price. Applied once at order entry. For example,
0.0002 models a 0.02% spread typical in commission-less forex trading.spread is applied at entry only. Use commission for fees that apply at both entry and exit.Commission rate applied at both trade entry and exit. Accepts three forms:
float— relative commission as a fraction of order value. E.g.0.01for 1%.(fixed, relative)tuple — fixed cash amount plus a relative fraction. E.g.(100, 0.01)for $100 + 1%.callable(order_size: int, price: float) -> float— custom commission function.order_sizeis negative for short orders. Useful for modeling rebates or tiered fee schedules.
Before v0.4.0, commission was applied only once (like
spread is now). To preserve old behavior, use spread instead.Required margin ratio for leveraged trading. The ratio is
1 / leverage. For example, 0.02 enables 50:1 leverage. No distinction is made between initial and maintenance margin.Must be between 0 (exclusive) and 1 (inclusive).When
True, market orders fill at the current bar’s closing price instead of the next bar’s open. Useful for strategies that decide to trade at bar close.When
True, allows simultaneous long and short positions. When False (default), opposite-facing orders close existing positions in FIFO order.When
True, placing a new order automatically cancels all pending orders and closes the current position, ensuring at most one trade is active at any time.When
True, any trades still open at the end of the backtest are closed on the last bar and included in the computed statistics. When False, a warning is issued for any remaining open trades.Backtest.run()
pd.Series of performance statistics.
Keyword arguments override strategy class-level parameter defaults for this run:
The backtest begins on the first bar where all declared indicators have non-NaN values. A strategy using a 200-bar moving average will not start trading until bar 201. This affects
Exposure Time, Duration, and trade counts.Return value
run() returns a pd.Series with the following fields:
Datetime of the first data bar.
Datetime of the last data bar.
Total duration of the backtest period.
Percentage of bars where at least one trade was active.
Final account equity at the end of the backtest.
Maximum equity reached during the backtest.
Total return on initial cash.
Return of a passive buy-and-hold strategy over the same period, for comparison.
Annualized return.
Annualized volatility of equity returns.
Compound Annual Growth Rate.
Risk-adjusted return relative to volatility (assumes risk-free rate of 0).
Like Sharpe, but penalizes only downside volatility.
Annualized return divided by maximum drawdown.
Excess return over buy-and-hold.
Sensitivity of strategy returns relative to the underlying asset.
Largest peak-to-trough decline in equity, as a percentage.
Average drawdown across all drawdown periods.
Longest time spent in a drawdown.
Average duration of drawdown periods.
Total number of completed trades.
Percentage of trades that were profitable.
Return of the single best trade.
Return of the single worst trade.
Average return per trade.
Duration of the longest trade.
Average duration per trade.
Ratio of gross profit to gross loss.
Average expected return per trade, accounting for win rate and average trade size.
Van Tharp’s System Quality Number. Values above 1.6 indicate a good system.
Optimal fraction of capital to risk per trade according to the Kelly formula.
The strategy instance used in the run, including all parameter values.
Full equity curve as a DataFrame, indexed by datetime.
DataFrame of all completed trades with entry/exit prices, sizes, P&L, and tags.
Backtest.optimize()
Strategy parameter names mapped to sequences of candidate values. The optimizer searches over combinations of these values.
The metric to maximize. Either:
- A string key matching a field in the
run()result series (e.g.'Return [%]','Sharpe Ratio'). - A callable
func(stats: pd.Series) -> floatthat accepts arun()result and returns a score. The higher the score, the better.
Optimization method:
'grid'— exhaustive (or randomized ifmax_triesis set) search over the Cartesian product of all parameter combinations.'sambo'— model-based optimization using SAMBO. Requires thesambopackage (pip install sambo). Efficient for large parameter spaces.
Maximum number of strategy evaluations:
None— exhaustive grid search (all combinations). Formethod='sambo', defaults to200.floatin(0, 1]— fraction of the full grid space to sample randomly.int— absolute maximum number of evaluations.
A function that accepts a dict-like object of parameter values and returns
True if the combination is worth testing. Use this to prune invalid combinations.When
True, the method returns a tuple (stats, heatmap) where heatmap is a pd.Series with a MultiIndex of all tested parameter combinations and their maximize scores. Use backtesting.lib.plot_heatmaps() to visualize.When
True and method='sambo', also returns the raw scipy.optimize.OptimizeResult object for further inspection with SAMBO’s plotting tools. Requires return_heatmap=True if you want all three return values.Integer seed for reproducible randomized grid search or SAMBO optimization.
Return value
Depending on the flags set:| Flags | Returns |
|---|---|
| (default) | pd.Series — stats of the best run |
return_heatmap=True | (pd.Series, pd.Series) — best stats + heatmap |
return_heatmap=True, return_optimization=True | (pd.Series, pd.Series, OptimizeResult) |
Backtest.plot()
A specific result series from
run() or optimize(). If None, the most recent run() result is used.File path for the output HTML file. Defaults to a strategy/parameter-dependent filename in the current working directory.
Width of the chart in pixels. When
None, the chart spans 100% of the browser width.Show an equity curve section (cash plus open position value). Equivalent to
plot_return plus the initial 100%.Show a cumulative return section. Equivalent to
plot_equity minus the initial 100%.Show a profit/loss indicator section.
Show a trade volume section.
Show a separate drawdown graph section.
Mark trade entry-to-exit spans on the chart with hash-marked tractor beams.
Interpolate the equity curve between trade closing points, removing intra-trade volatility from the visual.
Label the equity axis with return percentages instead of absolute cash values.
Overlay larger-timeframe candlesticks on the price chart. When
True, the downsampling frequency is chosen automatically (monthly for daily data, daily for hourly, etc.). Pass a Pandas offset string like '5min' to override. Requires a datetime index.Resample the OHLC data to keep the Bokeh canvas under 10,000 candles for performance. Pass a Pandas offset string to force a specific frequency. Requires a datetime index.
Plot sub-chart indicators in reverse declaration order.
Display labeled legends on each plot section.
Open the generated HTML file in the default web browser after saving.