backtesting.lib provides composable strategy base classes and Backtest subclasses that handle common patterns — vectorized signal dispatch, ATR-based trailing stops, fractional share trading, and running on multiple instruments — so you can focus on your signal logic.
When subclassing any of these classes, always call
super().init() inside your init() override and super().next() inside your next() override. Omitting these calls disables the composable behavior.SignalStrategy
init() and let SignalStrategy handle order dispatch bar-by-bar in next().
set_signal(entry_size, exit_portion=None, *, plot=True)
init() override.
An array of floats with one value per bar:
- Positive values place a long entry order of that size.
- Negative values place a short entry order of that absolute size.
- Zero values take no action.
Strategy.buy(size=...) — values in (0, 1) are fractions of equity; values ≥ 1 are absolute unit counts.An optional array of floats with one value per bar:
- Positive values close that portion of any open long trades.
- Negative values close that portion of any open short trades.
- Zero or
Nonetakes no exit action.
Trade.close(portion=...) — use 1.0 to close the full position.When
True, the signal indicators are shown on the chart when Backtest.plot() is called.Example
TrailingStrategy
init(). The stop-loss for each open trade is updated on every bar in next().
set_atr_periods(periods=100)
init() override, after super().init().
Number of bars for the rolling ATR window. Larger values produce a smoother, more stable ATR. The default of 100 is intentionally conservative to avoid volatile stops during warm-up.
set_trailing_sl(n_atr=6)
Number of ATR units the trailing stop trails behind the current price. A larger multiple gives the trade more room; a smaller multiple triggers exits sooner.
set_trailing_pct(pct=0.05)
mean(Close * pct / atr).
Trailing stop distance as a rate (e.g.
0.05 for 5%). Must be strictly between 0 and 1.Example
The trailing stop only moves in the trade’s favor — it will never widen a stop that has already been set. Each call to
super().next() updates trade.sl for every open trade.FractionalBacktest
Backtest subclass that enables fractional share trading. It rescales OHLCV prices by fractional_unit before passing data to the engine (so integer unit sizes map to fractional amounts), then unscales trade results back to the original price units in run().
This approach works around the engine’s whole-number unit constraint without modifying strategy logic.
Constructor
OHLCV price data, same format as
Backtest. The constructor automatically rescales Open, High, Low, Close by fractional_unit and Volume by 1 / fractional_unit internally.The smallest tradable fraction. Defaults to one satoshi (
1e-8 BTC). For μBTC trading, pass 1/1e6. The engine then trades in whole multiples of this unit.*args
Additional positional arguments passed through to
Backtest.__init__().**kwargs
Additional keyword arguments passed through to
Backtest.__init__() (e.g. cash, commission, margin).run(**kwargs)
Backtest.run(). Runs the backtest on the rescaled data, then translates trade Size, EntryPrice, ExitPrice, TP, and SL fields back to the original price scale before returning results.
Example
MultiBacktest
run() uses multiprocessing for efficiency.
Constructor
A list of OHLCV DataFrames — one per instrument. Each DataFrame must have the standard
Open, High, Low, Close (and optionally Volume) columns with a DatetimeIndex.The strategy class (not an instance) to run on all instruments.
**kwargs
Keyword arguments passed to each underlying
Backtest constructor (e.g. cash, commission, margin). Applied uniformly across all instruments.run(**kwargs)
pd.DataFrame where each column corresponds to one instrument (by list index) and each row is a run() statistics field. Instruments with zero trades return None for that column.
**kwargs
Strategy parameter overrides, same as
Backtest.run(**kwargs). Applied to all instruments in the run.A DataFrame of performance statistics with instrument indexes as columns. Rows are the same fields returned by
Backtest.run() (e.g. 'Return [%]', 'Sharpe Ratio', '# Trades').optimize(**kwargs)
Backtest.optimize() on each instrument sequentially and aggregates the resulting heatmaps into a single pd.DataFrame. Use this to find parameter combinations that are robust across instruments.
**kwargs
Parameter ranges and optimization options, same as
Backtest.optimize(). return_heatmap is set to True internally.A DataFrame where each column is the optimization heatmap (
pd.Series) for one instrument. Use heatmap.mean(axis=1) to aggregate across instruments before passing to plot_heatmaps().