Basic Prerequisites: Backtesting Hygiene (a backtest is a search) and Market Microstructure & Order Types (spreads, queues, order types). Working Python. (these are the folder-level prerequisites for pages 02–06; page 01 states its own, smaller, entry requirements)
1. Intuition & Practical Objective
A vectorized backtest computes signal.shift(1) * returns and takes a cumulative sum. An event-driven backtest instead replays time: it pushes market data, signals, orders, and fills onto one priority queue and processes them in strict timestamp order, one at a time, exactly as a production engine must. The difference is not style - it is the difference between a number you can trade and a number you cannot.
The reason is structural. Vectorized arithmetic has no notion of when a decision became known versus when an order could act on it. Every vector operation silently assumes you can execute the entire portfolio at the exact bar close the signal was computed from, at unlimited size, for free. Each of those is a hidden +∞ in the model. An event-driven engine makes each one an explicit, testable number: a signal-to-fill delay, a spread you cross, a fraction of bar volume you are allowed to consume.
This folder is the topic-hub for event-driven backtesting engines in Kwant-Atlas. It (a) gives you the fast lookup below - the hub’s job #1 - and (b) routes you to six sub-pages that walk from raw intuition through the architecture, the event loop, the vectorized comparison, the failure modes, and the extensions.
The one-sentence essence. “A backtest is a discrete-event simulation: a priority queue ordered by timestamp, a deterministic next-event time advance, and a set of components (data handler, strategy, portfolio, execution handler) that convert market events into order events into fill events - and the only honest fill is one that happens strictly after the information that caused it, at a price you paid, in a size the market could absorb.”
The three laws of a trustworthy engine (each a first principle, not a preference):
- Time only advances forward, one event at a time. The engine never sees while processing . Every look-ahead bug is a violation of this single rule.
- Causality costs time. An order written at cannot fill before . Even a zero-latency engine pays bar, because the signal is computed from the bar you just observed (see 03 · The Event Loop).
- Every omitted friction is a free option you granted yourself. Mid-price fills, infinite size, and survivor-only universes each add P&L that does not exist.
2. Mathematical Ground Truth & Lookups
Quick-Reference Lookup (job #1). Notation: the event queue, an event with timestamp and priority , the live event count, the signal-to-fill delay, annualised volatility, a year in the same time units as , bar volume, the participation cap, annualisation factor (252 for daily bars).
| Quantity | Formula | Verified check |
|---|---|---|
| Next-event advance | (clock jumps, never steps) | pop order MARKET, ORDER, FILL, MARKET, MARKET |
| Total order | ties broken by priority, then arrival | |
| Queue cost | heap push/pop vs sorted-list insert | at (wall-clock; ) |
| Latency decomposition | structural floor bar | |
| Fill condition (passive buy limit ) | fill | - |
| Execution shortfall | delay bars bps | |
| Adverse price move over | (MC ) | |
| Capacity-limited fill | sh into bars | |
| Bars to work an order | bars trading days at bars/day | |
| Annualised Sharpe | of the engine’s equity returns | vectorized vs event-driven |
Critical caveat. The bps figures are engineering magnitudes, not guarantees: they depend on the spread, the venue, and the regime. The qualitative result is the stable one - the naive vectorized Sharpe is inflated by two orders of magnitude (the lookup table below measures ) over what the same signal achieves under honest fills. Always re-run on your own data (see 06 · Advanced Extensions).
The headline result of this folder (reproduced exactly by the runnable engine of 04 · Vectorized vs Event-Driven, mean over 400 synthetic trending paths of 1500 bars):
| Engine | Total return | Sharpe |
|---|---|---|
| Naive vectorized (signal on the same bar) | ||
| Honest vectorized (signal shifted one bar) | ||
| Event-driven (spread + capacity + structural delay) |
Read it as an indictment: the same signal, the same data, a difference in Sharpe, entirely attributable to what the model assumed about execution.
3. Computational Implementation - the event-queue core
This is the smallest object that is an event-driven engine: a lexicographically-ordered event loop, the latency-to-shortfall converter, and the heap-vs-list benchmark that justifies the data structure. Standard library only.
The ratio line is wall-clock: it lands at but swings by roughly between runs and machines. The asymptotic claim ( vs ) is the durable one; the shortfall column is pure arithmetic and reproduces exactly.
4. Failure Modes & First-Principles Breakdowns
Hub signposts - the folder’s fault analysis lives in 05 · Failure Modes & Practice. In one line each:
- Look-ahead in the event loop - any component that reads a future bar, or that lets a signal act on the bar it was computed from, manufactures returns. Measured cost on a pure random walk: naive Sharpe vs honest - the entire edge is the bug.
- Optimistic fills - mid-price fills and one-bar-complete fills understate cost and overstate capacity; a single crossed spread turns bps into bps, and a -share order into bars of work.
- Survivorship in the replay - replaying today’s index over history deletes the failures; on a 100-name universe with 5 delistings per year, survivor-only replay inflates terminal wealth by .
5. References
- Hilpisch, Yves: Python for Algorithmic Trading: From Idea to Cloud Deployment (O’Reilly, 2020)
- López de Prado, Marcos: Advances in Financial Machine Learning (Wiley, 2018)
- NautilusTrader - Official Documentation & Concepts (nautilustrader.io)
- Halls-Moore, Michael: *QuantStart
- Backtrader Documentation (backtrader.com)
- VectorBT - Vectorized Backtesting with VectorBT
6. Connected Graph Bridges
- Foundational base: Probability & Measure Theory (Brownian scaling, expectation) · Numerical Methods (discrete simulation, floating-point determinism)
- Cross-pillar, hygiene: Backtesting Hygiene (the DSR/PBO machinery that tells you a good backtest is still probably overfit)
- Cross-pillar, execution: Execution, Backtesting & Simulation (the market-impact and queue models the engine’s ExecutionHandler abstracts)
- Cross-pillar, microstructure: Queue Position & Fill Probability (where the fill model of §02/§06 comes from)
- Sibling topics: Tick-Level Databases & Time-Series (the DataHandler’s storage layer) · Concurrency & Lockless Programming (the same message-bus pattern at nanosecond scale) · FIX & Exchange Connectivity (the order state machine a backtest must imitate)
- Single-page overview: Event-Driven Backtesting Engines (flat page)
- Sub-pages (in-folder): 01 From Zero · 02 Architecture · 03 The Event Loop · 04 Vectorized vs Event-Driven · 05 Failure Modes · 06 Advanced Extensions
Beginner: start at 01 · Practitioner: start at 05
- Absolute beginner (no quant-systems background): 01 · From Zero - why the vectorized backtest you already wrote is lying to you.
- Architecture + code (undergrad / job-seeking): 02 · Architecture → 03 · The Event Loop → 04 · Vectorized vs Event-Driven.
- Robustness (practitioner / graduate): 05 · Failure Modes → 06 · Advanced Extensions.
- Forward links: The Deflated Sharpe Ratio · Queue Position & Fill Probability · Production Risk Guards & Kill Switches