Basic Prerequisites: 02 · Architecture (the five components). Basic data structures (heaps).


1. Intuition & Practical Objective

The event loop is the entire engine’s notion of time. It is twenty lines, and every property that makes a backtest trustworthy - causality, determinism, reproducible latency - lives inside them.

This page builds the loop from first principles as a discrete-event simulation, states the scheduling mathematics precisely, proves why the queue must be a heap, and walks the order lifecycle finite-state machine that a backtest has to imitate. The objective is that you can write the loop from memory and justify every ordering decision in it.


2. Mathematical Ground Truth & Derivations

The next-event time advance. A discrete-event simulation does not tick. It maintains a clock and an event set , and repeatedly jumps:

The engine’s clock is always exactly the timestamp of the event being processed - it never takes an intermediate value. That is what “the engine cannot see the future” means operationally: at any moment the clock equals the latest information processed, and nothing else exists.

Event population. For a run of bars with signal transitions, orders, and fills, the loop performs

each costing where is the live queue size. The whole backtest is - for the 1500-bar engine of page 04 that is a few thousand pops, microseconds in C++ and milliseconds in Python.

The scheduling of an order. An order written at simulation time is not processed immediately. The ExecutionHandler computes an activation time

and the order can fill only at the first market event with :

The structural bar is irreducible and it is the most under-appreciated term in backtesting. A signal computed from the completed bar is only known at the end of ; the earliest event that can act on it is . So even a hypothetical zero-latency engine has

Every backtest that trades at the signal bar’s own price has quietly set and is arithmetically impossible.

Adverse price move over the delay. Over a delay of years on a driftless arithmetic walk of volatility , the expected absolute move is

Taking , , (one trading day): . A Monte Carlo with paths gives - a agreement, confirming the formula. This is the magnitude of the price risk your order is exposed to while it waits; the shortfall of the hub lookup is the signed, realised version of it.

Determinism as an equation. The ordering key is a triple. With as a heap on and a strictly increasing counter, the pop sequence is a deterministic function of the input data alone:

Any engine whose ordering depends on dictionary iteration order, thread timing, or floating-point accumulation order violates this and cannot reproduce its own results.


3. Computational Implementation - the loop, its cost, and its ordering

Three experiments in one script: (1) prove the tie-break rule by trace, (2) prove a heap beats a sorted list, (3) measure the latency→shortfall table that the loop produces. Standard library only.

The trace reads exactly as the lattice dictates: at , MARKET0 (priority 0) precedes ORDER (2) precedes FILL (3); then the market events in arrival order. The ratio is wall-clock and swings by – between runs and machines - the durable claim is the vs asymptotics behind it, not the constant.

The shortfall grows with delay, but sub-linearly. The strategy simulation in 04 gives mean realised shortfall rising from bps (delay 1) to bps (delay 100) - a factor over a delay, i.e. far slower than the (10×) that pure diffusion would predict, because the fill rule caps how much delay can hurt. Even that modest rise is an implementation cost a vectorized backtest has no slot to record.


4. Failure Modes & First-Principles Breakdowns

  1. Filling on the generating event. The single most damaging loop bug: letting the OrderEvent for bar produce a FillEvent at bar ‘s price. It sets and is outright look-ahead.
  2. Non-deterministic tie-breaking. Ordering on timestamp alone, or on an object whose comparison is not total, lets ties resolve arbitrarily. The 3-key order is not optional; it is what makes the run reproducible.
  3. Float timestamps as the primary key. Accumulating t += dt in floating point drifts; two events that should coincide differ in the place and order wrongly. Use integer bar/sequence indices for the key and floats only for values.
  4. Processing events emitted during processing out of order. A component that appends a new event must not assume it is handled next - it goes back through the queue, and the clock may not advance if its timestamp is (this is how a signal and its order share in the page-02 trace).
  5. Ignoring the terminal order state. When the data ends with live orders, the loop just stops. Real venues cancel or expire them; a backtest that drops them leaves phantom inventory and mis-states final P&L (see FIX & Exchange Connectivity).

The order lifecycle FSM the loop must implement, in the FIX vocabulary:

Skipping PENDING_NEW → OPEN (the latency gap) or the CANCELLED branch is how a backtest acquires phantom fills.


5. References

  • NautilusTrader Docs
  • Halls-Moore, Michael, Advanced Algorithmic Trading
  • Law, Averill M., Simulation Modeling and Analysis (5th ed.)
  • FIX Protocol - Official Specifications (fixtrading.org)

6. Connected Graph Bridges