Back to projects

Crypto Exchange Trade Engine

Coindelta

The trading core of a crypto exchange: an in-memory matching engine and market-data stack written in C++. It held the live order book for every market, matched incoming orders against it, and published book changes and trades to clients in real time. The two requirements that shaped every decision were predictable tail latency under load, and a book that could always be rebuilt exactly from its own history.

Language
C++
Matching
Limit order book, price-time priority
Order types
Limit, market, immediate-or-cancel, fill-or-kill
Concurrency
Single-threaded matching behind one sequencer
Durability
Journaled event stream, replayed on start
Market data
WebSocket snapshot, incremental diffs and trade tape, sequence-numbered
Risk
Pre-trade balance and limit checks, price bands, self-trade prevention
The engine's components. Solid lines run on the order path. Dashed lines run off the matching thread.

A deterministic core

The engine is event-sourced. A single sequencer assigns every input its place in one ordered stream and journals it, and the matching engine is a pure function of that stream. The same inputs always produce the same book. That property carried most of the operational weight: production sessions could be replayed in tests to reproduce a bug exactly, and recovery after a restart was replaying the journal to rebuild the in-memory state before accepting new orders.

Order book

Each market has a limit order book with strict price-time priority, and each price level holds a FIFO queue of resting orders. Order types decide what happens to an unfilled remainder: a limit order rests on the book, market and immediate-or-cancel orders cancel it, and fill-or-kill orders are rejected unless they fill completely. Price levels are stored in flat, cache-friendly arrays rather than pointer-linked trees, so matching cost stays flat as the book gets deeper.

Latency

Matching runs on one thread with nothing else on it. The hot path does no heap allocation and takes no locks: orders come from a preallocated pool and are reused, and networking and journaling run on their own threads, exchanging work with the matching thread over bounded queues. Keeping one thread in charge of the book removes contention entirely, and the bounded queues turn overload into back-pressure instead of unbounded latency. The target was tight, predictable tail latency, not a good average.

Market data

Clients subscribe over WebSocket and receive a full snapshot of the book, then incremental diffs and a trade tape. Every message carries a sequence number. A client that sees a gap drops its local book and re-syncs from a fresh snapshot, so a lost message or a reconnect can never leave it trading on a stale book.

Risk and audit

Every order passes pre-trade checks before it reaches the book: available balance and limit validation, a price band that rejects orders too far from the market, and self-trade prevention so an account never matches against its own resting order. Because every event is sequenced and journaled, reconciliation and audit read the same log the engine replays, so there is one source of truth for what happened and in what order.