Strategy Package Manual
This repository stores Backtrader strategy modules in the strategies/ package and runs them through run_backtest.py, run_all.bat, and build_dashboard.py.
Package Layout
backtrader_strategy_library/
strategies/
__init__.py
<StrategyName>.py
_catalog_talib_strategy.py
run_backtest.py
run_all.bat
build_dashboard.py
results/
strategies/ should contain strategy modules only. A strategy module should be import-safe: importing it must not download data, run Cerebro, create plots, write files, or start a backtest.
Strategy File Rules
Each runnable strategy file should expose at least one bt.Strategy subclass.
Preferred convention:
import backtrader as bt
class ExampleStrategy(bt.Strategy):
params = (
("printlog", False),
)
def __init__(self):
...
def next(self):
...
Rules:
- Put the file in
strategies/. - Match the main class name to the filename when possible:
ExampleStrategy.py->class ExampleStrategy. - Do not include
if __name__ == "__main__":blocks in strategy files. - Do not put data downloads, plotting, optimization loops, or backtest runners at module level.
- Keep backtest orchestration in
run_backtest.pyor separate root-level scripts. - Use
printlogor similar params if a strategy has logging; batch runs suppress output by default.
Shared Generated Strategy Base
strategies/_catalog_talib_strategy.py contains the shared TA-Lib-backed base used by generated catalog strategies.
Files that subclass it normally look like:
from strategies._catalog_talib_strategy import CatalogTalibStrategy
class SomeCatalogStrategy(CatalogTalibStrategy):
params = (
("profile", "trend"),
("stop_atr_mult", 2.0),
("allow_short", True),
("printlog", False),
)
The leading underscore means it is a helper module and is skipped by run_backtest.py.
Running All Strategies
Use:
run_all.bat
Current batch defaults:
set ASSETS=BTC-USD
set PERIOD=1y
set INTERVAL=1d
set BENCHMARK=BTC-USD
set FAST=1
set /a WORKERS=%NUMBER_OF_PROCESSORS%-1
FAST=1 uses the fast worker pool path. It is much faster than fully isolated mode because it avoids launching a new Python process for every strategy.
Set FAST=0 if you need maximum isolation for debugging a bad or hanging strategy.
Fast Mode
Direct fast run:
python run_backtest.py --fast --workers 8 --symbol BTC-USD --period 1y --interval 1d --benchmark BTC-USD --strategies strategies --out results
Fast mode does this:
- Downloads/caches data once per asset.
- Scans strategy files in process.
- Reuses worker processes for many strategies.
- Writes
metrics.csvper strategy for the dashboard. - Skips per-strategy plot PNGs by default.
Fast mode tradeoff:
- Fast mode does not hard-kill one stuck strategy the way isolated subprocess mode can.
- Use isolated mode (
FAST=0) when debugging unstable strategies.
Equity Plots
Dashboard Equity Plots means the number of strategy folders containing:
equity_vs_benchmark.png
Fast mode skips these by default for speed.
To create plots in fast mode:
python run_backtest.py --fast --fast-plots --fast-equity --workers 8 --symbol BTC-USD --period 1y --interval 1d --benchmark BTC-USD --strategies strategies --out results
Add --fast-plots --fast-equity to the fast command in run_all.bat if you want full plot output every run.
Targeted Runs
Run only matching strategy files:
python run_backtest.py --fast --strategy-filter macd --workers 4 --symbol BTC-USD --period 1y --interval 1d --benchmark BTC-USD
Smoke test first N matching files:
python run_backtest.py --fast --limit 5 --workers 2 --symbol BTC-USD --period 6mo --interval 1d --benchmark BTC-USD
Force a fresh data download:
python run_backtest.py --fast --refresh-data --symbol BTC-USD --period 1y --interval 1d --benchmark BTC-USD
Results
For asset BTC-USD, outputs are under:
results/BTC-USD/
Important files:
results/BTC-USD/run.log
results/BTC-USD/run_progress.json
results/BTC-USD/run_summary.json
results/BTC-USD/all_metrics.csv
results/BTC-USD/skipped_strategies.csv
results/BTC-USD/dashboard.html
results/BTC-USD/<StrategyName>/metrics.csv
If plots are enabled:
results/BTC-USD/<StrategyName>/equity_vs_benchmark.png
results/BTC-USD/<StrategyName>/drawdown_vs_benchmark.png
results/BTC-USD/<StrategyName>/daily_returns_hist.png
results/BTC-USD/<StrategyName>/rolling_return_vs_benchmark.png
results/BTC-USD/<StrategyName>/equity.csv
Dashboard
Build dashboards after a run:
python build_dashboard.py --results results --mode assets
The asset dashboard is written to:
results/<ASSET>/dashboard.html
For BTC:
results/BTC-USD/dashboard.html
Metrics
Key metric columns:
| Column | Meaning |
|---|---|
total_return_pct |
Strategy return over the test period. |
benchmark_return_pct |
Benchmark return over the same dates. |
asset_buy_hold_return_pct |
Buy-and-hold return for the tested asset. |
excess_return_vs_benchmark_pct |
Strategy return minus benchmark return. |
sharpe |
Annualized Sharpe based on equity curve returns. |
max_drawdown_pct |
Maximum drawdown from the strategy equity curve. |
total_trades |
Closed plus still-open trades reported by Backtrader. |
closed_trades |
Trades that fully closed before the end of the backtest. |
open_trades |
Trades still open at the final bar. |
won / lost |
Closed winning/losing trades. |
win_rate_pct |
Win rate based on closed trades only. |
Important: a strategy can show non-zero return with closed_trades = 0 if it has open_trades > 0. That means it entered a position and held it through the end of the backtest. The final value includes mark-to-market value of that open position.
Adding A New Strategy
- Create
strategies/NewStrategy.py. - Define
class NewStrategy(bt.Strategy). - Keep the file import-safe.
- Avoid root-level
yf.download,bt.Cerebro(),cerebro.run(), plotting, or optimization code. - Run a smoke test:
python run_backtest.py --fast --strategy-filter NewStrategy --symbol BTC-USD --period 1y --interval 1d --benchmark BTC-USD
- Rebuild the dashboard:
python build_dashboard.py --results results --mode assets
Maintenance Checks
Compile all strategy files:
python -m compileall strategies
Find accidental main blocks:
rg "if __name__\\s*==" strategies
Find accidental import-time backtest code:
rg "^\\s*(cerebro\\.run\\(|yf\\.download\\(|plt\\.show\\(|cerebro\\.plot\\()" strategies
Strategy files should pass these checks cleanly.