ml4t-run-backtest
Installation
SKILL.md
Event-Driven Backtesting
Vectorized backtests hide execution reality. Event-driven simulation processes each bar sequentially, submitting orders that fill at future prices - the only way to model what actually happens when you trade.
The Problem
Vectorized positions * returns backtests assume instant fills at known prices. In reality, you decide to trade on bar t but fill at bar t+1. Ignoring this inflates Sharpe by 0.3-0.5 or more for daily strategies. The faster the signal, the larger the gap.
The Pattern
WRONG
# Vectorized: signal and fill use the SAME bar's price
signals = compute_signal(prices) # uses close[t]
positions = np.where(signals > 0, 1, 0) # no shift!
returns = prices.pct_change()
strategy_returns = positions * returns # lookahead: traded at price used to decide
sharpe = strategy_returns.mean() / strategy_returns.std() * np.sqrt(252)