use both backtrader and vectorbt.
here’s when to use each.
quick verdict #
backtrader: event-driven, realistic simulation, slower
vectorbt: vectorized, fast parameter sweeps, less realistic
my usage: 60% vectorbt (research), 40% backtrader (validation)
philosophy difference #
backtrader:
simulates real trading.
processes bar by bar.
handles orders, fills, commissions realistically.
vectorbt:
math on arrays.
calculates returns vectorized.
speed over simulation accuracy.
speed comparison #
test: 5 years SPY data, simple MA crossover
import time
# backtrader
start = time.time()
cerebro = bt.Cerebro()
cerebro.addstrategy(MACrossover)
cerebro.adddata(data)
cerebro.run()
bt_time = time.time() - start
print(f"Backtrader: {bt_time:.2f}s")
# vectorbt
start = time.time()
fast_ma = vbt.MA.run(close, 10)
slow_ma = vbt.MA.run(close, 50)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(close, entries, exits)
vbt_time = time.time() - start
print(f"VectorBT: {vbt_time:.2f}s")
results:
backtrader: 12.4 seconds
vectorbt: 0.3 seconds
vectorbt is 40x faster.
parameter optimization #
backtrader approach:
import backtrader as bt
class MACrossover(bt.Strategy):
params = (
('fast', 10),
('slow', 50),
)
def __init__(self):
self.fast_ma = bt.indicators.SMA(period=self.p.fast)
self.slow_ma = bt.indicators.SMA(period=self.p.slow)
self.crossover = bt.indicators.CrossOver(self.fast_ma, self.slow_ma)
def next(self):
if self.crossover > 0:
self.buy()
elif self.crossover < 0:
self.sell()
# optimize (SLOW)
cerebro = bt.Cerebro(optreturn=False)
cerebro.optstrategy(
MACrossover,
fast=range(5, 30, 5),
slow=range(30, 100, 10)
)
cerebro.adddata(data)
results = cerebro.run() # takes minutes
vectorbt approach:
import vectorbt as vbt
import numpy as np
# parameter grid
fast_periods = np.arange(5, 30, 5)
slow_periods = np.arange(30, 100, 10)
# vectorized optimization (FAST)
fast_ma = vbt.MA.run(close, fast_periods, short_name='fast')
slow_ma = vbt.MA.run(close, slow_periods, short_name='slow')
# all parameter combinations at once
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(
close,
entries,
exits,
freq='1D'
)
# heatmap of sharpe ratios
sharpe_matrix = pf.sharpe_ratio().to_frame().unstack()
print(sharpe_matrix) # takes seconds
optimization speed:
backtrader: 8 minutes (42 parameter combinations)
vectorbt: 4 seconds (same combinations)
vectorbt is 120x faster for optimization.
realistic simulation #
where backtrader wins:
class RealisticStrategy(bt.Strategy):
def __init__(self):
self.order = None
def notify_order(self, order):
# realistic order handling
if order.status == order.Completed:
if order.isbuy():
self.log(f'BUY: {order.executed.price:.2f}')
self.log(f'Commission: {order.executed.comm:.2f}')
else:
self.log(f'SELL: {order.executed.price:.2f}')
def notify_trade(self, trade):
if trade.isclosed:
self.log(f'P&L: {trade.pnl:.2f}')
def next(self):
# can't enter if order pending
if self.order:
return
# realistic entry logic
if self.signal():
# limit order, not market
self.order = self.buy(
exectype=bt.Order.Limit,
price=self.data.close[0] * 0.999,
valid=bt.Order.DAY
)
vectorbt can’t do:
- order types (limit, stop, etc.)
- partial fills
- realistic commission structures
- position-aware logic
- multi-asset portfolio rebalancing
my workflow #
phase 1: research (vectorbt)
fast parameter sweeps.
identify promising ranges.
discard obvious failures quickly.
# quick research sweep
params = np.arange(5, 100, 5)
results = vbt.MA.run(close, params).ma_crossed_above(
vbt.MA.run(close, params * 2)
)
# find promising parameters in seconds
phase 2: validation (backtrader)
realistic simulation.
proper commission modeling.
actual order flow.
# validate promising params with realistic sim
cerebro = bt.Cerebro()
cerebro.addstrategy(Strategy, fast=15, slow=45)
cerebro.broker.setcommission(commission=0.001)
cerebro.adddata(data)
results = cerebro.run()
phase 3: live (custom)
neither framework for live trading.
custom python + broker APIs.
code examples #
vectorbt for options analysis:
import vectorbt as vbt
import pandas as pd
# options P&L analysis
def options_backtest(underlying, strikes, expirations):
results = []
for strike in strikes:
for exp in expirations:
# simplified put selling
premium = calculate_premium(underlying, strike, exp)
assignment_loss = (strike - underlying.shift(-exp)) * (underlying.shift(-exp) < strike)
pnl = premium - assignment_loss.fillna(0)
results.append({
'strike': strike,
'expiration': exp,
'total_pnl': pnl.sum(),
'sharpe': pnl.mean() / pnl.std() * np.sqrt(252)
})
return pd.DataFrame(results)
backtrader for realistic options:
class PutSelling(bt.Strategy):
def __init__(self):
self.options_position = None
def next(self):
if self.options_position is None:
# check IV rank
if self.iv_rank() > 30:
# sell put at 0.30 delta
strike = self.calculate_strike(delta=0.30)
premium = self.get_premium(strike)
# realistic entry
self.options_position = {
'strike': strike,
'premium': premium,
'expiration': self.next_monthly_exp()
}
self.broker.add_cash(premium * 100)
else:
# manage position
if self.days_to_exp() <= 5:
self.close_position()
feature comparison #
| feature | backtrader | vectorbt |
|---|---|---|
| speed | slow | very fast |
| param optimization | slow | fast |
| order types | all | basic |
| commission models | realistic | basic |
| live trading | yes (some) | no |
| visualization | basic | excellent |
| learning curve | moderate | steep |
| documentation | good | good |
when to use which #
use vectorbt when:
- initial parameter research
- quick hypothesis testing
- large parameter sweeps
- visualization/analysis
use backtrader when:
- final strategy validation
- realistic fill simulation
- complex order logic
- position-aware strategies
resources #
learned a lot from the NexusFi backtesting comparison thread - good discussion on different frameworks and their trade-offs. also the Battle of the Bots algo development thread has real-world examples.
my verdict #
vectorbt: 9/10 for research
backtrader: 8/10 for validation
workflow: vectorbt first, backtrader to validate
neither for live: custom code only
tonight (june 24, 2:42am) #
backtrader vs vectorbt deep comparison. vectorbt 40x faster for backtests, 120x faster for param optimization. backtrader more realistic (order types, commissions, fills). my workflow: vectorbt for research (60%), backtrader for validation (40%), custom for live. vectorbt 9/10 research, backtrader 8/10 validation. use both, neither for live trading.
2:42am tuesday. backtesting framework comparison. vectorbt: 40x faster backtests, 120x faster optimization, excellent for research. backtrader: realistic simulation, proper order handling, better for validation. workflow: vectorbt research → backtrader validate → custom live. use both frameworks, each has strengths.
-AK