Quant Memo
Core

The Simulation Clock and Event Loop

Every backtest engine needs an internal notion of 'now' that advances consistently as it processes market data, orders, and fills — the simulation clock and the event loop built around it are what keep a backtest from silently mixing up the order things actually happened.

Prerequisites: Event-Driven Backtest Architecture

A backtest that processes a strategy's order-placement logic and the market data update in the wrong sequence will happily let the strategy "see" a price before it was actually available, or fill an order against a quote that had already moved. The bug is invisible in the output — the backtest just runs and produces a number — which is exactly why it's dangerous. The fix is architectural: give the simulation a single, authoritative clock, and route every event in the backtest through a central event loop that only ever advances that clock forward, one event at a time, in the true order things happened.

How it works: one clock, one queue, no skipping

The event loop maintains a single current simulation time and a queue of pending events — market data ticks, order acknowledgments, fills, timer callbacks — each stamped with the time it occurs. On each iteration, the loop pops the earliest pending event, advances the clock to that event's timestamp, processes it (which may itself generate new events, like a strategy reacting to a price update by placing an order), and repeats. The critical invariant is that the clock only moves forward and every event is processed in true chronological order relative to every other event — a strategy can never see, react to, or fill against something that, in real time, hadn't happened yet.

event queue (by time) t=10.1 quote t=10.3 fill t=10.4 order clock advances toevent's timestamp handler runs,may enqueue new events
The event loop always pops the earliest queued event, advances the clock to match it, and dispatches it — new events a handler creates (like an order triggered by a quote) go back into the same time-ordered queue, never processed out of turn.

Worked example: why naive loops break causality

A naive backtest loops over daily bars and, within each day, first checks "did my order fill" using that day's high and low, then computes a new signal from that day's close and places tomorrow's order. This looks fine until a strategy also uses intraday quotes for stop-losses: if the stop-loss check is coded to run after the day's signal update instead of before it in wall-clock order, a stop that should have triggered mid-day at a worse price gets evaluated against the end-of-day price instead, silently improving the backtest's exit price. An event-loop design forces the ordering question to be answered explicitly, with an actual timestamp per event, rather than left to whatever order the code happens to iterate in.

What this means in practice

Any backtest complex enough to have more than one kind of event — market data, order state changes, timers, corporate actions — benefits from an explicit clock and event loop rather than nested loops with implicit ordering assumptions. It's also what makes latency modeling possible at all: you can't meaningfully simulate "this order arrives 3ms after the quote that triggered it" without a clock granular enough, and a queue disciplined enough, to represent that gap.

The simulation clock and event loop give a backtest a single, consistent notion of "now," processing every event — market data, orders, fills — in true chronological order. Without this discipline, a backtest can silently let a strategy react to information before it was actually available, inflating results without any visible error.

Related concepts

Practice in interviews

Further reading

  • Chan, Algorithmic Trading: Winning Strategies and Their Rationale, ch. 2
ShareTwitterLinkedIn