Order Book Reconstruction from Tick Data

Order-book reconstruction turns an exchange’s event stream into the state that a trader could have observed at each instant. It is the foundation for queue-aware execution research, short-horizon alpha, and credible transaction-cost simulation. A bar database cannot answer whether a bid was cancelled before a marketable sell arrived. For that, the backtest needs the book.

The relevant raw inputs are typically add, cancel, delete, replace, trade, and trading status messages. Their meaning varies by feed: some publish order IDs and full depth, while others provide only price-level deltas. Start with the feed specification, rather than inferring semantics from sample data. The storage and clock choices described in tick-data architecture determine whether this replay remains reproducible at scale.

Define the state and event contract

At event \(t\), maintain an order map and aggregated bid/ask ladders. An order-ID feed lets the book retain time priority; an aggregated feed does not. This distinction is material for passive fills.

EventOrder-ID book actionLevel book actionInvariant
AddInsert ID, price, sizeIncrease level sizeSize is positive
CancelReduce/remove IDDecrease level sizeNever below zero
ReplaceDelete then addRemove then addPreserve feed ordering
ExecuteReduce resting orderReduce displayed levelMatch known liquidity
Trade printOften informationalOften informationalDo not double count

Do not assume every trade print consumes displayed liquidity. Feeds can report an execution and a separate consolidated trade message; dark or hidden executions may have no displayed predecessor. Treat the exchange sequence number as authoritative, then retain receive time as a separate field for latency research.

Replay in sequence, not timestamp order

Nanosecond timestamps are not an ordering guarantee: multiple messages may share a timestamp, clocks can be corrected, and vendor normalization can reorder packets. Replay by (tradingdate, venue, channel, sequencenumber). Detect gaps before applying later messages; a locally consistent book after a gap is still economically wrong.

from collections import defaultdict

class Book:
    def __init__(self):
        self.orders = {}
        self.levels = {"B": defaultdict(int), "S": defaultdict(int)}

    def add(self, order_id, side, price, qty):
        self.orders[order_id] = [side, price, qty]
        self.levels[side][price] += qty

    def reduce(self, order_id, qty):
        side, price, open_qty = self.orders[order_id]
        removed = min(qty, open_qty)
        self.levels[side][price] -= removed
        if removed == open_qty:
            del self.orders[order_id]
        else:
            self.orders[order_id][2] -= removed

Production code additionally rejects duplicate adds, unknown cancels, crossed books outside special states, and invalid price increments. Log each violation with the raw message; silently clipping negatives creates a cleaner but fictional dataset.

Snapshots, recovery, and corporate actions

Most feeds provide periodic snapshots. Use one to seed a session, replay incrementals after its sequence number, and compare subsequent snapshots against the locally reconstructed state. On a sequence gap, discard the affected channel until a valid snapshot arrives. “Repairing” by assuming the missing messages were harmless biases depth and queue estimates exactly when markets are stressed.

Normalize prices to integer ticks, quantities to exchange lots, and symbols to a point-in-time security master. Split adjustments, symbol changes, halts, and auction states need explicit events. A continuous-book model should not manufacture depth during an opening or closing cross; see auction mechanics.

Validation is an empirical process

Validate at both message and market levels:

TestExpected resultFailure usually means
Sequence continuityNo unexplained gapsPacket loss or bad partition
Best bid < best askExcept locked/cross rulesEvent ordering error
Snapshot reconciliationExact depth matchIncorrect event semantics
Executed sizeDoes not exceed visible queue, where applicableDouble-counted prints
Top-of-book quote countMatches vendor referenceWrong session/status handling

For each instrument-day, store gap counts, reconciliation differences, and the fraction of events rejected. These are data-quality features, not merely pipeline telemetry: abnormal rates cluster around halts, upgrades, and volatile periods.

From book state to research features

Once replay is deterministic, derive microprice, depth-weighted imbalance, cancellation intensity, and order-flow imbalance. Keep feature timestamps causal: a feature at event \(t\) may use state after the event only if the strategy could receive that event before submitting its order. The distinction matters for market microstructure signals and for estimating adverse selection.

Persist compact snapshots only when needed, but retain immutable normalized events. Snapshots accelerate queries; events are the audit trail that lets a researcher repair a bug and reproduce a historical decision.

Key takeaways

  • Reconstruct from documented message semantics and sequence numbers, not timestamps alone.
  • Separate executions from informational trade prints to avoid consuming depth twice.
  • A sequence gap requires snapshot recovery; plausible-looking interpolation is not valid.
  • Validate against exchange snapshots and retain quality metrics per instrument-day.
  • Queue-sensitive research requires order-ID state, causal clocks, and immutable events.
#order book #tick data #market microstructure #replay #Python