Having Claude Code produce this as I've been idly interested in it on and off so this is a chance to whip something up. I also saw Entire drop which seems to be $60M wrapped in a bunch of commit hooks so thought it might be interesting to check out.
Still a work in progress as interest waxes and wanes. Don't expect to get rich but OTOH who knows?
Requires Python 3.12+.
uv syncThat installs all dependencies and the backtest package itself as an editable install.
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install as editable package (includes all dependencies)
pip install -e .python examples/basic_backtest.pyRSI(14) vs Buy & Hold, SPY, and DCA on AAPL over 5 years. Prints a metric summary and final portfolio value.
python examples/compare_strategies.py # defaults: AAPL 2020–2024
python examples/compare_strategies.py MSFT 2018-01-01 2024-01-01Runs every built-in strategy on the same data and prints a table ranked by total return.
python examples/parameter_sensitivity.py
python examples/parameter_sensitivity.py SPY 2018-01-01 2024-01-01Sweeps RSI period from 5 to 30, printing Sharpe, Sortino, Calmar, and trade count at each value. Helps identify stable parameter regions before optimising.
python examples/walk_forward_analysis.py
python examples/walk_forward_analysis.py SPY 2016-01-01 2024-01-01
python examples/walk_forward_analysis.py AAPL 2016-01-01 2024-01-01 -1 # all CPUsWFA repeatedly optimises RSI parameters on a 1-year training window and validates on the
following 3-month test window. Prints per-window results and overall out-of-sample metrics.
Saves three plots to output/: chained equity curve, parameter stability, and per-window
metric bars. Pass a 4th argument to control parallel candidate evaluation (-1 = all CPUs,
2 = 2 workers, 1 = sequential).
python examples/ensemble_strategy.py
python examples/ensemble_strategy.py MSFT 2019-01-01 2024-01-01Combines RSI, MACD, and Bollinger Bands into an ensemble that signals only when at least 2 of 3 strategies agree. Compares the ensemble against each constituent and Buy & Hold.
The following metrics can be passed as the objective parameter to WalkForwardOptimizer,
or called directly from backtest.metrics:
from backtest.metrics import METRICS
print(list(METRICS.keys()))
# ['total_return', 'cagr', 'sharpe_ratio', 'sortino_ratio', 'calmar_ratio',
# 'max_drawdown', 'ulcer_index', 'profit_factor', 'win_rate', 'expectancy',
# 'recovery_factor']You can also pass any callable with the signature (portfolio_history, trades) -> float
as a custom objective.
| Class | Description |
|---|---|
ConsecutiveDaysStrategy |
Buy after N consecutive down days, sell after N consecutive up days |
MovingAverageCrossoverStrategy |
Golden/death cross on short- and long-term moving average crossovers |
RSIStrategy |
Buy when RSI is oversold, sell when overbought |
MACDStrategy |
Buy/sell on MACD line crossovers with the signal line |
BollingerBandsStrategy |
Mean reversion on Bollinger Band touches |
ParabolicSARStrategy |
Trend-following with Parabolic SAR (requires High/Low columns) |
| Class | Description |
|---|---|
BreakoutStrategy |
Enter on N-day high breakout or low breakdown |
GapStrategy |
Gap trading on overnight price discontinuities (requires Open column) |
FibonacciRetracementStrategy |
Support/resistance at 38.2%, 50%, 61.8% retracement levels (requires High/Low) |
| Class | Description |
|---|---|
MeanReversionStrategy |
Combines RSI and Bollinger Bands for mean reversion signals |
MomentumStrategy |
Rate-of-Change (ROC) momentum — buy/sell on threshold crossovers |
VolatilityStrategy |
ATR-based volatility breakout detection (requires High/Low) |
EnsembleStrategy |
Wraps multiple sub-strategies, aggregates signals via majority voting |
WalkForwardOptimizer optimises any strategy's parameters over a rolling or expanding train
window and evaluates out-of-sample on the following test window. Results include a stitched
equity curve, per-window metrics, and the most stable parameter set across all windows.
Pass n_jobs=-1 to evaluate parameter candidates in parallel using all available CPUs.
Custom callable objectives fall back to sequential automatically.
backtest.reporting turns a WalkForwardResult into three plotly figures:
from backtest.reporting import plot_equity_curve, plot_parameter_stability, plot_metrics_by_window, save_wfa_report
fig = plot_equity_curve(result, chain=True, start_capital=10_000)
fig.show()
# Or save all three plots at once:
save_wfa_report(result, output_dir="output", prefix="my_run")See Getting Started above for usage.
# Run the full test suite
pytest
# With coverage report
pytest --cov=backtest -v- All strategies operate on single-asset OHLCV data. Cross-asset strategies (pairs trading, relative momentum) are out of scope until the runner supports multi-asset event loops.
- Volatility strategies use ATR from price data only. Options-based or VIX-based approaches require a different data model and are out of scope.
- Momentum strategies use single-asset Rate-of-Change. Cross-sectional momentum (ranking multiple assets) requires runner changes and is out of scope.
MIT License