Backtest engines: vectorized vs event-driven
Two fundamentally different architectures for simulating a strategy against historical data — each with real tradeoffs, not just a style preference.
Vectorized (e.g. vectorbt, or hand-rolled pandas)
Computes signals and returns across the entire dataset at once using array operations, rather than looping bar-by-bar.
import pandas as pd
signal = sma_crossover_signal(df) # chapter 80
returns = df["close"].pct_change()
strategy_returns = signal.shift(1) * returns # shift(1): trade on NEXT bar's return, not same bar
cumulative = (1 + strategy_returns).cumprod()
Pros: extremely fast (seconds for years of data across hundreds of instruments), easy to run large parameter sweeps. Cons: harder to model realistic order-level mechanics (partial fills, exact intraday sequencing of multiple signals, position-level state like a trailing stop) — you're working with returns series, not simulated orders.
Event-driven (e.g. backtrader, zipline-reloaded, or hand-rolled)
Iterates through time bar-by-bar (or tick-by-tick), maintaining explicit portfolio state, and calls into the same-shaped order logic you'd use live.
class SimplePortfolio:
def __init__(self, capital):
self.cash = capital
self.position = 0
self.entry_price = None
def on_bar(self, bar, signal):
if signal == 1 and self.position == 0:
self.position = int(self.cash * 0.95 / bar["close"])
self.entry_price = bar["close"]
self.cash -= self.position * bar["close"]
elif signal == -1 and self.position > 0:
self.cash += self.position * bar["close"]
self.position = 0
Pros: naturally handles path-dependent logic (trailing stops, multi-leg positions, order-type-specific fill assumptions) — closer to how the live bot will actually behave. Cons: much slower, more code to write and get right, easy to introduce subtle bugs in the loop itself.
Which to use when
- Vectorized for fast hypothesis screening across a large universe or parameter space — chapter 79's hypothesis, tested across hundreds of stocks quickly to see if there's anything there at all.
- Event-driven for the final validation of a specific strategy with real position-management logic (trailing stops, chapter 76; portfolio limits, chapter 74) before going live — because this is the version that actually resembles what you'll run.
A common, sound workflow: screen broadly with a vectorized approach, then validate the top candidates with an event-driven backtest that mirrors your actual live order logic closely enough to trust the result.