Basic Prerequisites: 04 · Vectorized vs Event-Driven (the headline numbers) and Backtesting Hygiene (the statistical layer).
1. Intuition & Practical Objective
An event-driven engine removes the mechanical bugs of a vectorized backtest but does not, by itself, make a backtest true. It only moves the dishonesty from invisible arithmetic into explicit modelling choices that are still wrong by default. This page names the three that matter most - look-ahead inside the loop, optimistic fills, and survivorship - states each as a first-principles violation, and measures the money each one silently invents.
The objective is a practitioner’s rule: every number a backtest cannot refuse to make an assumption about is a number you must choose deliberately. The engine forces the choice; this page tells you which way is honest.
2. Mathematical Ground Truth & Derivations
Failure 1 - look-ahead inside the event loop. The loop’s invariant (page 02) is that an event processed at may depend only on events with timestamp . Two standard violations:
- Compute-then-fill on the same event. The order emitted while processing bar fills against bar ‘s own quote. Formally it sets , i.e. , violating .
- Index-forward in the data handler. Any code path that reads
bars[t+1](to normalise a feature, to compute a “realised” label, to set a stop) is reading the future. A rolling statistic centred on () does this invisibly.
The measurable signature is the one from page 01: on a pure random walk the look-ahead backtest reports where the truth is . The correct conclusion is not “the strategy is weak”; it is “the engine is broken”.
Failure 2 - optimistic fills. The default fill assumptions and their true costs, for an order of shares at decision mid :
with realised execution shortfall in basis points
The capacity fiction is the sibling: a fill of shares against a bar of volume under participation cap can only ever be
A vectorized backtest sets and implicitly. For a -share order into a -share bar at , - the naive fill understates the working time by , and with it the market impact and the timing risk.
Failure 3 - survivorship. A replay over today’s universe holds only names that survived to today. If a fraction of the universe delists each year with loss , the survivor-only series omits a drag of roughly per year. Over years,
so the inflation compounds - it is not a flat haircut.
The practice checklist (an engine is only trustworthy if all hold):
- Signals use data with timestamp ; the engine’s clock equals the latest processed timestamp (assert it).
- for every fill (assert it - the loop guarantees it only if you do not shortcut it).
- Fills cross the spread; passive fills require the queue-ahead condition of Queue Position & Fill Probability.
- with stated and defended.
- The universe is point-in-time: delisted and added names present as of each date.
- Costs (commission, borrow, financing) are charged as events, not applied at the end.
- The run is bit-for-bit reproducible from the seed and the data file.
3. Computational Implementation - the three failures, in dollars
One script, three experiments: the fill-assumption cost, the capacity requirement, and the survivorship inflation. Standard library only.
Read the three results.
- Fills. A mid-price assumption is free; crossing a 2-cent spread is bps; realistic slippage is bps. On a strategy trading daily, 2 bps 252 round trips is 5% per year of pure friction - larger than most “edges.” The engine must be told to cross.
- Capacity. The order is 25% of ADV. The engine’s 5% cap turns it into bars of work. A vectorized backtest fills it in one bar and thereby reports a strategy that could never have been executed at that size. (The market impact of working the order is developed in Execution, Backtesting & Simulation.)
- Survivorship. Erasing 5% of names per year, all of which went to zero, inflates terminal wealth from to - a overstatement, purely from which names appear in the data file. This is the failure that lives in the DataHandler’s universe definition, not in the loop, which is why an event-driven architecture alone does not fix it.
4. Failure Modes & First-Principles Breakdowns (numbered)
- Filling on the generating event. Sets ; the most direct look-ahead in the loop. Assert at runtime.
- Centred rolling windows. A
rolling(..., center=True)feature, or any statistic that sees both sides of , leaks the future into the signal - inside a perfectly correct event loop. Audit every data derivation, not just the engine. - Mid-price fills. bps is not a conservative default; it is an assumption of a free option. Fill against the side you would actually cross.
- Infinite capacity. Every engine must cap fills by bar volume, and the cap must be stated; a backtest whose has no upper bound is not simulating a market.
- Point-in-time universe blindness. Survivorship is a data bug that no loop can detect. Use point-in-time index membership and include delisted names (see Tick-Level Databases & Time-Series for as-of joins).
- Costs applied at the end instead of per event. Commission/financing applied as a terminal adjustment misses that costs reduce the capital available to compound. Charge them as events, in the loop.
- Non-reproducible runs. A backtest that cannot be re-run bit-for-bit is a rumour. Fix the seed, fix the tie-break rule, freeze the data snapshot.
- Fixing the mechanics and stopping. With all of the above correct, the reported Sharpe is still the maximum of a search - deflate it (04 · The Deflated Sharpe Ratio).
5. References
- López de Prado, Marcos, Advances in Financial Machine Learning (Wiley, 2018)
- Halls-Moore, Michael, QuantStart event-driven series
- Almgren & Chriss, Optimal Execution of Portfolio Transactions (2001)
- Elton, Gruber & Blake, Survivorship Bias and Mutual Fund Performance (1996)
- NautilusTrader Docs
6. Connected Graph Bridges
- Back: 04 · Vectorized vs Event-Driven · Index Hub
- Continue: 06 · Advanced Extensions - impact, queue position, determinism at scale
- Statistical layer: Backtesting Hygiene · Hygiene Failure Modes
- Execution: Execution, Backtesting & Simulation · Queue Position & Fill Probability
- Data: Tick-Level Databases & Time-Series