c++-matching-engine

2026 · Solo project · C++17, standard library only

A low-latency price-time-priority limit-order-book matching engine in C++17. It accepts limit, market, cancel and modify orders, matches crossing orders against resting liquidity (best price first, FIFO within a level), prints an execution report per fill, and maintains the top of book in O(1). Where a trading bot consumes an exchange’s book, this is the other side of the wire — it is the exchange’s core: the machine that decides who trades with whom, at what price, in what order.

The engine is fed the way a real venue feeds one: over UDP multicast, with MoldUDP64-style sequenced framing, a gap-detecting feed handler that buffers reordered packets and drops duplicates, and a fragmented snapshot channel it falls back on when messages are genuinely lost.

Correctness rests on a differential test against an independently written std::map reference engine: both are driven with the same 180,000 randomised operations and must produce byte-identical trade streams. That, plus the unit suite and the full packet-loss matrix, is 290 checks, 0 failures, ASan + UBSan-clean, built with nothing but g++ and the standard library.


Learning outcomes

  • Matching-engine internals: price-time priority, partial fills, IOC vs. resting semantics, amend rules that decide when an order keeps or loses its place in the queue — the canonical HFT systems problem.
  • Cache-aware data-structure design: an array-indexed price book with a hierarchical bitset over occupied levels, intrusive FIFO queues, an object pool, and generation-tagged handles that make O(1) cancel ABA-safe.
  • Measure, diagnose, fix, re-measure: every optimisation here was driven by a benchmark that first showed the code losing, and is defended by a number.
  • Testing systems you can’t enumerate: differential/model-based testing, and mutation-testing the test itself to prove it can actually fail.
  • Exchange connectivity as it really works: UDP multicast, sequence-gap detection, reorder tolerance, exactly-once application under duplication, and snapshot recovery — plus the socket tuning (SO_RCVBUF, recvmmsg, busy-polling) that decides whether you see the feed or drop it.

Result worth naming

The first version of the engine lost a head-to-head against a textbook std::map book — 0.74×. The benchmark said why: when the top of book emptied, the code walked the price-level array one tick at a time looking for the next occupied price. Adding a hierarchical bitset turned that lookup into a masked load plus ctz, and flipped the same benchmark to 1.78–1.83× faster, with cancel latency at 10.3 ns p50.