Backtesting libraries: vectorbt, Backtrader, QuantConnect
Verified 2026-08. This landscape moves and the naming matters — knowing which of these is still maintained is itself a signal.
The options
| Library | Model | Status 2026 | Use when |
|---|---|---|---|
| vectorbt | vectorized, NumPy/Numba | actively developed; open-source core plus a commercial PRO tier | screening thousands of parameter combinations fast |
| Backtrader | event-driven, pure Python | effectively unmaintained — no active bug fixing | you inherit an existing Backtrader codebase |
| QuantConnect / LEAN | event-driven, C# engine with a Python API | actively developed | you want research, backtest and live deployment on one platform with managed data |
| NautilusTrader | event-driven, Rust core with a Python API | actively developed | you need high-fidelity, high-performance event-driven backtesting and live trading from the same code |
| Zipline-reloaded | event-driven, community fork of Quantopian’s engine | maintained fork | you want the classic Zipline API |
| backtesting.py | event-driven, small | maintained | quick single-asset studies where you want to read the whole engine |
Saying “I’d use Backtrader” without qualification is the answer that dates you. The accurate version: it is fine to maintain, not what you would pick for something new.
vectorbt
Signals in, portfolio out, with the whole parameter grid evaluated at once.
import vectorbt as vbt
price = vbt.YFData.download('BTC-USD').get('Close')
fast = vbt.MA.run(price, window=range(5, 50), short_name='fast')
slow = vbt.MA.run(price, window=range(20, 200), short_name='slow')
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)
pf = vbt.Portfolio.from_signals(price, entries, exits, fees=0.001, slippage=0.001)
pf.total_return() # a Series indexed by every (fast, slow) combination
The strength is exactly this: a multi-dimensional sweep in one call, with results indexed by parameter. The weakness is fidelity — position sizing, margin, partial fills and intrabar logic are limited. Treat its output as a map of where to look, not as a result.
Watch the heatmap trap. A parameter surface with one sharp peak is overfitting; a broad plateau is a real effect. Reporting the peak is how backtests stop working in production.
Backtrader
Event-driven, strategy-as-a-class, with a broker and a cerebro engine.
import backtrader as bt
class SmaCross(bt.Strategy):
params = dict(fast=10, slow=30)
def __init__(self):
f = bt.ind.SMA(period=self.p.fast)
s = bt.ind.SMA(period=self.p.slow)
self.crossover = bt.ind.CrossOver(f, s)
def next(self):
if not self.position and self.crossover > 0:
self.buy()
elif self.position and self.crossover < 0:
self.close()
cerebro = bt.Cerebro()
cerebro.addstrategy(SmaCross)
cerebro.broker.setcommission(commission=0.001)
Worth knowing because the mental model — next() called once per bar, indicators pre-computed and indexed relative to now — is the shape of every event-driven engine. Its known weaknesses are speed and multi-asset handling.
QuantConnect / LEAN
An open-source engine (LEAN, C#) plus a hosted platform providing data across equities, futures, forex, crypto and options, with the same code path from backtest to paper to live.
class MyAlgo(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetCash(100000)
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
def OnData(self, data):
if not self.Portfolio.Invested:
self.SetHoldings(self.spy, 1.0)
The argument for it is operational, not algorithmic: point-in-time data with survivorship handled, a corporate-actions-aware price series, and one code path to live trading. The argument against is lock-in — the strategy is written against their API, and the data is theirs.
How to answer the choice question
Do not lead with a library. Lead with the constraint:
- Searching a wide parameter space cheaply -> vectorized (vectorbt, or your own NumPy/Polars).
- Validating a shortlist with realistic fills and capital constraints -> event-driven.
- Needing the same code to run live, on managed multi-asset data -> QuantConnect/LEAN or NautilusTrader.
- Inheriting an existing system -> whatever it is, plus an honest note on the maintenance risk.
Interview angle
- “Which backtesting framework do you use?” - answer with the two-stage workflow rather than one name: vectorized for the search, event-driven for validation. Then name the constraint that would pick a specific engine.
- “Is Backtrader a good choice today?” - it is effectively unmaintained, so not for a new system. It remains a fine reference for the event-driven model and is common in existing codebases.
- “What does vectorbt give up for its speed?” - execution realism. Position sizing, margin, partial fills and intrabar behaviour are simplified, so its numbers are optimistic relative to an event-driven run.
- “Why would you use a hosted platform like QuantConnect?” - point-in-time, survivorship-corrected, corporate-action-adjusted multi-asset data, and one code path from backtest to live. Assembling that data yourself is the expensive part of this domain.
- “Your parameter sweep found a great combination. What now?” - check whether it sits on a plateau or a spike, re-run it walk-forward, then re-run the survivors in an event-driven engine with doubled cost assumptions. A single peak in a heatmap is a fitted artefact.