Convert Backtrader strategies to vectorbt signals and compare execution results.
bt2vbt is a Python package that bridges the gap between Backtrader and vectorbt. It allows you to:
- Parse Backtrader strategy classes and extract trading logic
- Convert strategies to vectorbt-compatible signals and configuration
- Generate standalone vectorbt Python code (optional)
- Run both engines on the same dataset
- Compare results with detailed reports on signal alignment and metrics differences
Version 1 targets a well-defined subset of Backtrader strategies to ensure reliable and testable conversion:
- ✅ Indicator-based strategies (SMA, EMA, RSI, MACD, Bollinger Bands, etc.)
- ✅ Bar-close execution logic (signals evaluated at bar close)
- ✅ Simple order types (market orders)
- ✅ Single-asset strategies
- ✅ Long-only strategies (buy/sell)
| Category | Indicators |
|---|---|
| Moving Averages | SMA, EMA, WMA |
| Momentum | RSI, MACD, Stochastic, ROC, Momentum |
| Volatility | ATR, Bollinger Bands, StdDev |
| Volume | OBV |
| Trend | ADX |
| Utilities | CrossOver, CrossDown |
pip install bt2vbtOr install from source:
git clone https://github.com/bt2vbt/bt2vbt.git
cd bt2vbt
pip install -e .import backtrader as bt
from bt2vbt import StrategyConverter, StrategyComparator
# Define your Backtrader strategy
class SMACrossStrategy(bt.Strategy):
params = (("fast_period", 10), ("slow_period", 30))
def __init__(self):
self.fast_sma = bt.indicators.SMA(self.data.close, period=self.params.fast_period)
self.slow_sma = bt.indicators.SMA(self.data.close, period=self.params.slow_period)
self.crossover = bt.indicators.CrossOver(self.fast_sma, self.slow_sma)
def next(self):
if self.crossover[0] > 0:
if not self.position:
self.buy()
elif self.crossover[0] < 0:
if self.position:
self.sell()
# Load your data
import pandas as pd
data = pd.read_csv("your_data.csv", index_col="date", parse_dates=True)
# Option 1: Convert and get signals
converter = StrategyConverter()
result = converter.convert(SMACrossStrategy, data, generate_code=True)
print(f"Entry signals: {result.signals.entries.sum()}")
print(f"Exit signals: {result.signals.exits.sum()}")
# Generated vectorbt code
if result.generated_code:
with open("strategy_vbt.py", "w") as f:
f.write(result.generated_code)
# Option 2: Full comparison
comparator = StrategyComparator()
comparison = comparator.compare(SMACrossStrategy, data)
print(f"Signal match rate: {comparison.comparison.entry_signal_match_rate:.1%}")
print(f"Equity correlation: {comparison.equity_correlation:.3f}")# Parse a strategy and show extracted information
bt2vbt parse my_strategy.py
# Convert a strategy to vectorbt code
bt2vbt convert my_strategy.py --generate-code --output strategy_vbt.py
# Run full comparison and generate report
bt2vbt compare my_strategy.py --data prices.csv --format html --output report.htmlbt2vbt uses Python's AST (Abstract Syntax Tree) module to parse Backtrader strategy source code. It extracts:
- Strategy parameters (
paramstuple) - Indicator definitions from
__init__ - Trading rules from
nextmethod - Buy/sell conditions
Backtrader indicators are mapped to equivalent pandas/numpy computations:
| Backtrader | bt2vbt Implementation |
|---|---|
bt.indicators.SMA(period=20) |
series.rolling(20).mean() |
bt.indicators.EMA(period=12) |
series.ewm(span=12).mean() |
bt.indicators.RSI(period=14) |
Custom RSI using EWM |
bt.indicators.CrossOver(a, b) |
(a > b) & (a.shift(1) <= b.shift(1)) |
Trading rules are converted to boolean signal arrays:
# Backtrader
if self.sma_fast[0] > self.sma_slow[0]:
self.buy()
# Becomes
entries = sma_fast > sma_slowBoth engines run on identical data, and results are compared:
- Signal alignment: Entry/exit timing match rate
- Metrics comparison: Total return, Sharpe ratio, max drawdown, etc.
- Trade matching: Individual trade-level comparison
converter = StrategyConverter()
# Full conversion
result = converter.convert(
strategy, # Backtrader Strategy class or source code
data, # OHLCV DataFrame
params=None, # Optional parameter overrides
generate_code=False # Generate standalone Python code
)
# Parse only (no signal computation)
parsed = converter.parse_only(strategy)
# Compute signals from parsed strategy
signals = converter.compute_signals(parsed, data, params)comparator = StrategyComparator(
initial_cash=10000.0,
commission=0.001,
signal_tolerance=1, # Bars of timing tolerance
metrics_tolerance=0.05 # 5% metrics difference tolerance
)
# Full comparison
comparison = comparator.compare(strategy, data, params)
# Quick signal comparison
signal_stats = comparator.compare_signals_only(strategy, data, params)from bt2vbt.comparison import ReportGenerator
generator = ReportGenerator()
# Generate report in various formats
report = generator.generate(comparison, format="text") # or "html", "json"
# Save to file
report.save("comparison_report.html")
# Print to stdout
generator.print_report(comparison)- Stop-loss and take-profit orders
- Limit orders and order sizing
- Portfolio strategies (multiple assets)
- Short selling
- Custom indicators (must be predefined)
- Order notifications and trade management
- Position sizing based on available cash
Due to fundamental differences between Backtrader and vectorbt:
- Execution timing: Slight differences in when orders are filled
- Fee calculation: Minor differences in commission handling
- Indicator warmup: Initial NaN handling may differ
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run with coverage
pytest --cov=bt2vbt
# Format code
black src tests
ruff check src testsContributions are welcome! Please see our contributing guidelines.
MIT License - see LICENSE file for details.