Skip to main content

backtrader vs vectorbt - python backtesting framework deep comparison

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

Related

vectorbt vs backtrader - python backtesting framework comparison 2025
been using both for 2+ years. here’s when to use each. quick verdict # vectorbt: fast parameter sweeps, simple strategies
position sizing with kelly criterion - python implementation
rebuilt my position sizing engine last weekend. kelly criterion with practical modifications. the problem # old approach: fixed 2% risk per trade.
thetadata review - options greeks data for algo trading
been using thetadata for 8 months now. here’s the real review for options algo traders. what is thetadata # options-focused market data provider.
coinbase advanced vs kraken - python API comparison for algo trading
been using both coinbase and kraken for 2+ years. here’s the real comparison for algo traders. quick verdict # coinbase advanced: better for fiat on/off ramp, simpler API
prop firm evaluations for algo traders - honest guide 2025
been asked about prop firms a lot lately. here’s my honest take for algo traders specifically. my situation # why i don’t use prop firms:
redis caching optimization - 40% latency reduction for market data
optimized redis caching during honeymoon downtime review. 40% latency improvement. the problem # before optimization: market data fetch: 180ms avg