Articles
Order Book System Design Interview: Price-Time Priority, Cancels, and Replay
Prepare for order book system design interviews with price-time priority, partial fills, cancel races, retry identity, snapshots, and deterministic replay.
A buy order crosses two resting sells. One fills completely; the other remains partially open. A cancellation then races with another buy, and the client retries because its acknowledgement disappeared. Can your design explain every remaining quantity and recover the same result after a restart?
An order book system design interview is a state-machine exercise before it is a latency exercise. Price-time priority needs one authoritative ordering point. Cancels, retries and replay must preserve the decisions made at that point.
Official venue facts below come from Coinbase Exchange documentation checked September 30, 2026. They describe that venue, not every exchange or its private architecture. The command ledger, recovery contract and implementation choices are original exercises and inference. No candidate reports establish interview frequency or a particular employer's assessment format.
What rules must be fixed before drawing the architecture?
Official venue fact: Coinbase documents continuous price-time priority: orders execute according to price and then the time received by its matching engine. It also describes execution at the resting order's price and self-trade prevention. These are named rules to discuss, not proof that all venues use identical policies. Coinbase matching engine
For this original design contract, use one symbol, limit orders, integer price ticks and integer quantity units. A buy crosses sells priced at or below its limit; a sell crosses buys at or above its limit. Better prices precede worse prices. At one price, the lower accepted engine sequence wins.
All counterparties in our fixtures are distinct. Market orders, auctions and implementing self-trade prevention are excluded. Partial fills retain the resting order's priority. Cancel-and-replace is a new order with new priority in this contract; an interviewer can choose different amendment rules, so ask explicitly.
Specify order identifiers, permitted transitions and acknowledgement meanings. “Received by a gateway,” “durably accepted” and “filled” describe different milestones. A client timestamp cannot establish an authoritative race outcome across gateways.
Can you walk through price-time priority without hand-waving?
Here is an original six-command ledger. Prices are ticks; quantities are units. Every command has a unique retry key and enters the same sequence.
| Sequence | Command | Result |
|---|---|---|
| 1 | S1 sells 5 at 100 | S1 rests first at 100 |
| 2 | S2 sells 4 at 100 | S2 rests behind S1 |
| 3 | S3 sells 6 at 101 | S3 rests at the worse ask |
| 4 | B1 buys 7 with limit 101 | S1 fills 5 at 100; S2 fills 2 at 100 |
| 5 | Cancel S2 | Its remaining 2 are removed |
| 6 | B2 buys 3 with limit 101 | S3 fills 3 at 101; S3 retains 3 |
At sequence 4, B1 is willing to pay 101 but receives both fills at the resting price of 100. S2 keeps its original priority while partially open. At sequence 6, no ask remains at 100, so S3 becomes the best eligible resting order.
The final book contains only S3, selling 3 at 101. Total filled quantity is 10: five from S1, two from S2 and three from S3. Two additional units were canceled. These totals reconcile the 15 submitted sell units: 15 = 10 filled + 2 canceled + 3 resting.
Our local Python model executes this ledger. It is a memory-only reasoning check, not a live exchange connection or throughput measurement.
Which data structures serve matching and cancellation?
An original implementation choice is to maintain an ordered price index on each side, a FIFO container within each level, and an order-ID index pointing directly to its current record. Store account ownership, side, price, remaining quantity and priority sequence.
A balanced price index supports price insertion or removal in O(log P), where P is the number of occupied levels. A suitable indexed linked container permits direct cancellation without scanning every earlier order. Removing a level requires updating the price index as well.
Our Python fixture uses price dictionaries and OrderedDict queues. Choosing the best price with min or max scans occupied levels, so that step is O(P). Its direct order lookup and queue removal are expected constant-time operations under ordinary hash-table assumptions. Do not describe this implementation as a balanced-tree benchmark.
Matching an incoming order can touch many resting orders. Report work in terms of levels visited and fills emitted, rather than promising constant-time execution for an arbitrarily large order. Every fill also creates output that must be recorded and delivered.
How do cancels and fills race?
Take the book immediately after sequence 4. S2 has 2 remaining at 100, and S3 has 6 at 101. Now introduce a buy for 2 with limit 100 and a cancel for S2.
In our original race exercise, if cancellation is ordered first, S2 disappears. The buy cannot cross S3 at 101, so it rests as a bid for 2 at 100. If the buy is ordered first, it fills S2's final 2 at 100. The subsequent cancellation reports a terminal or unknown target and removes nothing.
Both are legal outcomes under different authoritative sequences. “The cancel reached another gateway first” does not resolve the race. Show where the sequence is assigned and when the result becomes authoritative.
Authorize cancellation against account ownership before mutating the book. Define whether filled and unknown IDs produce distinct responses and whether that distinction reveals another account's activity. Our fixture rejects a cross-account cancellation of an existing order; it does not implement a complete account, risk or information-disclosure policy.
What does a lost acknowledgement imply for retries?
Original recovery contract: scope a retry key to the account and operation contract, retain its canonical request identity, and persist the result associated with the accepted command. Repeating the same request returns the original result. Reusing its key with a different payload fails.
Retrying B1 after sequence 6 must return its original two fills. It must not buy from S3 again or allocate a new command sequence. Our fixture checks unchanged state, sequence and fill count after that duplicate, then checks that a changed quantity under B1's key is rejected.
An order ID alone is insufficient unless the API explicitly makes it the operation's idempotency identifier. A cancel has its own retry identity. Set retention windows and explain what happens after deduplication history expires; unlimited memory is not a plan.
For downstream fills, stable identities such as (engine epoch, command sequence, fill ordinal) let consumers deduplicate delivery. Retransmitting an event is different from executing the order again. Consumers should commit their deduplication record and corresponding ledger update atomically within their own boundary.
What must a snapshot preserve for deterministic replay?
Our original recovery exercise snapshots after sequence 4 and replays commands 5 and 6. Recovery must reconstruct S2's remaining 2 at 100 ahead of S3's 6 at 101, then reach the same final S3 quantity of 3.
A useful snapshot includes its sequence boundary, active orders, remaining quantities, original FIFO priority, deduplication results and the output bookkeeping needed by the chosen delivery protocol. Record schema and matching-rule versions. Validate checksums and reject a missing or incompatible tail rather than silently continuing.
Our memory fixture serializes its state, restores it and replays the tail. It compares active orders, sequence, cached responses and fill identities with the uninterrupted run. A second test restores a checkpoint halfway through 200 seeded commands and replays the remaining 100.
These checks establish determinism for this model. They do not establish disk persistence, crash consistency, replicated consensus or successful failover. A replay test that checks only the final best bid misses changed FIFO order or duplicate fills.
Where do durability and failover enter the design?
The following is design inference, not Coinbase's published architecture: one leader serializes commands for a book, writes the recovery record under a defined durability policy, applies deterministic transitions, and makes outputs available through a recoverable publication path.
State precisely when an acknowledgement is sent. A promise of durable acceptance requires the supporting log or replicated commit to survive the failures included in that promise. If the leader crashes after commit but before acknowledgement, retry must recover the accepted result. If it crashes before commit, the documented retry behavior must avoid inventing an accepted command.
Fence the former leader during failover. Two active writers assigning overlapping sequences cannot preserve one price-time order merely by merging their logs afterward. Different symbols can be separate serialization domains, but cross-symbol orders or atomic risk limits need additional coordination.
Keep risk checks, settlement and balances explicit in the surrounding system. A book-level fill does not by itself prove that cash or inventory ledgers were updated. Explain the transaction or recovery boundaries connecting those components.
How is public market-data reconstruction different?
Official feed fact: Coinbase's full-channel instructions describe subscribing, buffering messages, obtaining a REST snapshot, discarding queued messages at or before its sequence, and applying the remaining messages. They also warn that receiving an order does not necessarily mean it rests on the book; not every done or change message modifies it. Full-channel reconstruction
This reconstructs a published view, not the venue's private command log. It does not supply your hypothetical engine's retry decisions, risk results or durable acceptance boundary.
Official feed fact: Coinbase documents sequence numbers by product and explains how gaps and out-of-order messages can occur despite a TCP connection. Follow the selected channel's guarantees and rules. Feed sequence overview
As design inference, a consumer detecting an unexplained gap should mark its view stale, buffer or stop dependent decisions according to policy, and recover through the documented snapshot/feed protocol. Reconnecting alone does not prove missing updates were recovered.
Can aggregated levels recover FIFO, and what does backpressure change?
Official API fact: Coinbase distinguishes best-price, aggregated and non-aggregated book levels. In its aggregated response, size is already the total at that price; multiplying it by the order count inflates quantity. Get product book
As an original inference, two orders with quantities 5 and 4 can aggregate to 9 without preserving which arrived first. A price-level total cannot recover their individual cancellation targets or FIFO queue. Choose a representation that answers the intended question.
Official operational fact: Coinbase documents slow-consumer errors and suggests reducing processing backlog or choosing a less verbose feed when appropriate. WebSocket best practices
For your design, measure matching latency and output-consumer lag separately. A fast matcher with an undelivered fill backlog is not a healthy end-to-end path. Bound queues and define whether overloaded admission rejects requests before acceptance, applies backpressure or triggers a controlled degraded mode. Do not silently drop accepted commands to improve a percentile.
What should your interview checks demonstrate?
Our indexed matcher and a separate scan-based reference agree on responses and complete state after 200 seeded commands. The fixture also checks positive remaining quantities, index membership and an uncrossed resting book. Its cases cover same-price FIFO, partial fills, both cancel/fill orderings, retry identity and snapshot replay.
This is useful local evidence, with the execution limits already stated. Production evaluation would additionally need crash injection around commit and output publication, leader fencing, malformed input, integer overflow limits, deduplication expiry and recovery-version compatibility.
Use the following specific follow-ups to practise those boundaries. They are preparation choices, not evidence that an employer asks these exact questions.
| Question | Follow-up to explain |
|---|---|
| Implement Price-Time Order Matching | Derive every fill and remaining quantity |
| Design an in-memory order matching engine | Separate price indexing from FIFO priority |
| Design a low-latency trading system | Define acknowledgement and latency boundaries |
| Design a Pre-Trade Risk Check for an Electronic Order Path | Explain which acceptance decisions require coordination |
| Design a pub-sub replay system | Recover a snapshot and contiguous tail without duplicate effects |
Return to the price-time matching exercise and narrate the six-command ledger aloud. A clear answer identifies the authoritative sequence, reconciles quantities, resolves both race orderings and names exactly what replay restores.
Sources and Further Reading
- Coinbase Exchange matching engine — Named venue's priority, execution-price and self-trade rules.
- Coinbase WebSocket channels — Full-channel snapshot and buffered-message reconstruction.
- Coinbase WebSocket overview — Product sequences, gaps and out-of-order delivery.
- Coinbase get product book API — Aggregated versus non-aggregated response levels.
- Coinbase WebSocket best practices — Slow consumers and feed-processing trade-offs.