Stock Exchange System Design Interview Questions & Walkthrough
Approach a stock-exchange system design interview the same way you would approach designing the exchange: scope it before you draw anything, build the smallest correct single-writer matching engine first, and only add sequencing, durability, partitioning and market-data fan-out once you can justify each one with a concrete failure mode or scale number. Interviewers are not grading whether you recall Flow Exchange's architecture from memory — they are grading whether you can reason from invariants (no negative quantity, no duplicate fills, one active leader per book) outward to a design. This page gives you the opening questions, a realistic 40-45 minute walkthrough, model answers at core and staff depth, whiteboard exercises, a strong-vs-weak answer comparison, a 25+ question bank, and a quick-revision summary.
Opening the Interview
"Design a stock exchange" is deliberately underspecified. A broker's trading app, a single matching engine, and a multi-venue consolidated market are three different systems that share a name. The first two minutes should scope which one you're building, not draw boxes.
Treat the opening as a negotiation, not a stall tactic. You are aligning with the interviewer on what "the exchange" means in this room, what order types and matching rules are in scope, and what numbers you're designing for. Every clarifying question you ask should be one whose answer would actually change your architecture — not a generic warm-up ritual.
- Exchange vs broker scope — are you designing order acceptance, sequencing and matching, or a retail app that displays balances and sends orders somewhere else?
- Asset class — equities-style continuous limit order book, or something with different mechanics (options, futures)? This page and Flow Exchange assume a single-asset-class continuous equities book.
- Continuous matching vs auction — is this always-on continuous crossing, or do opening/closing auctions matter for this conversation?
- Matching rule — strict price-time priority (the default here), or does the interviewer want pro-rata or another allocation rule discussed?
- Order types in scope — market, limit, Day, IOC, FOK are the MVP; stop orders, icebergs and auctions are explicitly advanced extensions, not silent defaults.
- Throughput and latency targets — is this a few thousand orders/sec on one hot symbol, or tens of thousands exchange-wide? These are illustrative assumptions you'll state, not memorized benchmarks.
- Durability targets — what's the acceptable window between "accepted" and "durably committed," and can the system ever lose a committed execution?
- Single-region vs multi-region — is failover semantics (same-region standby) in scope, or does the interviewer want cross-region/multi-venue discussed as an extension?
“I'll scope this as the exchange itself, not the broker app — order acceptance, a price-time-priority continuous limit order book for a single asset class, with market, limit, IOC and FOK orders. I'll treat stop orders and auctions as extensions I can get to if time allows. I'll assume a single authoritative region with an active-passive standby, and I'll put rough numbers on the table — say, low tens of thousands of commands per second exchange-wide with real skew toward a few hot symbols — purely as design assumptions I can revise if you want a different scale. Does that match what you want to dig into?”
The 40-45 Minute Walkthrough
This is a guide for pacing a conversation, not a rigid script to recite. A good interviewer will branch early into whatever they care about most — follow them there and come back to the rest only if time allows.
| Time | Focus | What to cover |
|---|---|---|
| 0-5 min | Scope and definitions | Exchange vs broker scope, asset class, continuous vs auction, order types in/out of scope, define bid/ask/spread/tick/lot if the interviewer hasn't used them yet. |
| 5-10 min | Requirements, invariants, rough sizing | Functional requirements (new/cancel/replace, price-time priority, execution reports, market data); the handful of invariants you'll defend all interview (no negative remaining quantity, no duplicate fills, FIFO within a price level); a rough illustrative capacity assumption (e.g. tens of thousands of commands/sec with hot-symbol skew) stated explicitly as an assumption. |
| 10-20 min | Simple engine, book, order lifecycle | Start with one gateway, one single-writer matching engine, one in-memory order book — no durability yet. Walk a limit BUY through validate → match → rest/fill using the ACME book from Section 4, then say explicitly where this breaks (a crash loses the whole book). |
| 20-30 min | Ordering, journal, partitioning, market data | Introduce the sequencer (deterministic per-book ordering, not wall-clock race), the durable journal and commit/ack contract, per-symbol partitioning for hot instruments, and the snapshot+delta market-data feed as an async consumer off the matching hot path. |
| 30-40 min | Failover, trade-offs, scale | Leader fencing and epoch-based standby promotion (why active-active on one book is unsafe), what happens when the matching engine crashes, and how a 10x-100x burst on one hot symbol is handled (queueing, admission control, possibly a halt) rather than silently "scaled away." |
| Remaining time | Deeper interviewer follow-ups | Whatever the interviewer digs into — FOK feasibility under concurrency, market-data gap recovery, clock uncertainty, cross-symbol atomicity limits. Treat these as the actual signal of the interview, not an afterthought. |
Resist drawing this full diagram at minute 2. The value of the walkthrough is watching you *arrive* at this shape because of specific problems you identified — the crash-loses-the-book problem motivates the journal, the hot-symbol problem motivates partitioning, the simultaneous-orders problem motivates the sequencer. An interviewer who sees the final diagram at minute 2 can't tell whether you understand it or memorized it.
Core Questions With Model Reasoning
These are the questions almost every stock-exchange system design interview touches, at roughly mid-level depth. Each answer angle below is a reasoning path, not a script to memorize verbatim — the mechanism detail lives in Architecture Deep Dive; link there rather than re-deriving pseudocode live.
How does an order book work?
Define it concretely before anything algorithmic: two sides (bids descending by price, asks ascending by price), each price level holding a FIFO queue of resting orders, with best bid and best ask at the top of each side. Walk the ACME example — asks $101.00×50 (S1), $101.00×30 (S2), $101.50×70 (S3); bids $100.50×40 (B1), $100.00×80 (B2) — and point out the $0.50 spread between best bid and best ask. See Order Book Data Structures for the underlying structures.
- What data structure backs each side? — A sorted structure (balanced tree / ordered map) keyed by price, each node holding a FIFO queue — not a flat list, because you need fast best-price lookup and ordered iteration for sweeps.
- Why descending for bids and ascending for asks? — Because "best" means highest price a buyer will pay and lowest price a seller will accept — the structure should make the best price O(1) or O(log P) to find, not a linear scan.
How do you match buy and sell orders?
An incoming order crosses when its price is marketable against the opposite best price (a buy at or above best ask, a sell at or below best bid); fill the minimum of incoming-remaining and resting-remaining against the head of that price level, repeat while still marketable and remaining quantity exists, then rest any unfilled remainder. Use Test A (BUY 60 @ $101.00 fills 50 vs S1 then 10 vs S2, S2 left with 20) to make it concrete.
- What happens if the incoming order's limit price is better than the resting order's price? — The trade executes at the resting order's price, not the incoming order's limit — the limit is a ceiling (for a buy) or floor (for a sell), never a guaranteed execution price.
- Can a single incoming order generate multiple trades? — Yes — Test B (BUY 100 @ $101.50) generates three trades across S1, S2 and S3, fully filling the incoming order; S3 keeps 50 shares resting at $101.50 afterward.
How do you implement price-time priority?
Price priority first — always match against the best price level before a worse one. Time priority second — within a price level, FIFO by arrival/acceptance sequence, never by wall-clock timestamp. S1 (seq10) fills before S2 (seq11) at the identical $101.00 level purely because it arrived first; nothing about the matching rule looks at clock time.
- What breaks this if you get it wrong? — Using wall-clock arrival instead of sequencer order is the classic bug — under concurrent gateway connections, clocks can disagree or be reordered in transit, so only the sequencer's assigned order is authoritative. See Price-Time Priority Algorithm.
- Is time priority the only allocation rule exchanges use? — No — pro-rata and auction-based allocation are real alternatives used by some venues/instrument types; this design's baseline is strict price-time priority, and that's a stated choice, not a universal law.
How do you handle two simultaneous orders?
There is no true "simultaneous" inside a single book — a deterministic per-instrument sequencer assigns every accepted command a strictly increasing sequence number before the matching engine ever sees it, so two orders that arrive within microseconds of each other on different gateway connections still get one unambiguous processing order.
- What if the two orders target different instruments? — They can be sequenced and matched fully independently — price-time priority and the sequencer guarantee is per-book, not a global total order across every symbol.
- Does network arrival order equal sequencer order? — Not necessarily — retries, reordering and gateway failover mean receipt order and processing order can diverge; the sequencer's assignment is the only one that matters for fairness and replay.
Why not store the live order book in a relational database?
The live book is mutated on every accepted command at the target throughput and needs microsecond-to-low-millisecond read/mutate latency with zero lock contention across unrelated price levels — a relational engine's transaction/locking/page-write overhead sits directly in that hot path. The right answer isn't "never touch a database" — it's an in-memory structure for the operational book plus a durable journal of every command/event, so the book can be rebuilt deterministically and a database can still serve post-trade/reporting reads.
- So is the book durable at all? — Yes — durability comes from the write-ahead journal and periodic snapshots, not from the live in-memory structure itself; see Journal, Commit and Replay.
- Where does a relational database fit in this design at all? — Reporting, reconciliation, compliance queries and post-trade systems — anywhere read latency and transactional query flexibility matter more than hot-path mutation speed.
How do you cancel an order while it is being filled?
The cancel request is itself just another command that goes through the same sequencer as every new order — whichever arrives first in sequence order wins, deterministically. If the matching command is sequenced first, the cancel acknowledges against the order's actual remaining quantity after that fill (possibly "nothing left to cancel"); a cancel can never retroactively undo an already-committed fill.
- What does the client see in each outcome? — Either a cancel-confirmed execution report (if sequenced before any matching fill) or a reject/no-op with the order's true post-fill remaining quantity (if the fill was sequenced first) — never a silent contradiction between the two.
- Walk through Test D specifically. — Test D races cancel(S1) against an incoming matching buy; the outcome is resolved purely by which command the sequencer assigned first — there is no "partial undo" state.
How do you handle partial fills?
Deduct the traded quantity equally from both the incoming and resting order's remaining quantity, emit an execution report reflecting the new remaining quantity to both sides, and leave the resting order's remainder at the head of its price level's FIFO queue (it does not lose its time priority). Test A leaves S2 with 20 shares remaining, still first in line at $101.00 for the next incoming order.
- Does a partially filled order keep its original sequence number? — Yes — the remaining quantity is the same order with the same arrival priority, not a new order; re-queuing it as "new" would wrongly jump it ahead of or behind correctly-arrived competitors.
How do market, limit, IOC and FOK orders differ?
Market orders have no limit price and sweep available depth, accepting an uncertain execution price with no guarantee of a full fill if liquidity runs out. Limit orders define a ceiling/floor price and may rest unfilled. IOC (Immediate-or-Cancel) matches whatever is immediately marketable and cancels the remainder instead of resting it. FOK (Fill-or-Kill) requires full quantity to be immediately fillable or it executes zero — no partial side effects. Tests E and F make this concrete: IOC BUY 100 @ $101.00 fills 80 and cancels 20; FOK of the identical order fills nothing, because only 80 shares were executable within the limit.
- How is FOK's all-or-nothing check actually implemented without partial execution leaking through? — A feasibility pre-check walks the book to confirm the full quantity is executable at the limit before committing any fill, all within the same serialized matching step — see Market Orders, IOC, FOK and Stops.
- Does Day/GTC matter here? — Time-in-force for how long an order rests (Day expires at session end; GTC persists) is a separate axis from IOC/FOK's immediate-execution behavior — don't conflate the two.
How do you ensure a fill isn't applied twice?
Every accepted command gets a unique, durable sequence number from the per-book sequencer, and every emitted trade/execution gets its own unique, durable trade/execution ID generated once at commit time — the journal is the single source of truth for "did this already happen," so replay, retries or duplicate client submissions never re-derive a second effect for the same committed event.
- What about a duplicate client order submission (same client order ID retried)? — Test G — the engine recognizes the duplicate client order ID and returns/derives the original's status rather than creating a second order or a second match; this is deduplication via durable IDs, not a generic "exactly-once network" promise.
How do you persist an in-memory matching engine?
Every accepted command is written to an immutable, durable journal before (or as part of) acknowledging it, in strict sequence order; periodic snapshots of the full book state are taken so recovery doesn't require replaying from sequence zero. The in-memory book itself is never the durability boundary — the journal plus snapshots are.
- What's the precise durability point relative to the client ack? — This needs to be stated explicitly, not assumed — commonly, the command is durably committed to the journal before the acknowledgment is sent, so an ack implies durability; see the staff-level follow-up on this exact contract.
How do you recover after a crash?
Load the most recent verified snapshot (sequence N), then deterministically replay every journaled command from sequence N+1 onward through the same matching logic, reconstructing an identical book and an identical set of trade/execution IDs — because matching is a pure deterministic function of (book state, ordered command stream).
- What about execution reports that were never delivered before the crash? — Outbound reports must themselves be replayable/re-derivable from the durable journal without re-executing the trade — recovering the book and recovering outbound delivery are two distinct problems; see Journal, Commit and Replay.
- Is the exchange trading during this recovery window? — No — that symbol (or the whole engine, depending on partition scope) is unavailable/halted until recovery completes and is verified; this is a deliberate fail-closed choice, not an oversight.
How do you handle a hot stock?
Per-symbol partitioning means a hot instrument's queue depth and latency grow independently of other instruments, but a single hot symbol is still fundamentally bounded by one single-writer engine's throughput — you can't naively parallelize writes to one book without breaking price-time priority. The real answer is bounded queues, admission control/throttling, monitoring p99 latency and queue depth, and, if genuinely overloaded, a controlled halt rather than silently degrading correctness.
- Can you just add more engines for that one symbol? — Not for the same book — multiple writers mutating one book concurrently breaks the single authoritative ordering that price-time priority depends on; see Partitioning and Hot Instruments.
How do you publish market data?
The matching engine emits book deltas and trade prints as it processes each command; a separate market-data publisher turns those into a snapshot + sequenced incremental delta feed, entirely off the synchronous matching critical path. Subscribers that detect a sequence gap request/reload a fresh snapshot rather than guessing at missing updates.
- Why off the critical path? — Market-data fan-out to potentially many subscribers at varying speeds must never block or slow down order acceptance and matching — a slow subscriber is the subscriber's problem, not the exchange's.
- Is market data ever the source of execution truth? — Never — feeds can be delayed or lossy; the journal and the engine's committed state are the only authoritative record of what executed.
What is the role of the broker, exchange and clearinghouse?
The broker is the client-facing entity that onboards members and routes orders; the exchange (what this design is) accepts, sequences, matches and reports executions, and publishes market data; the clearinghouse/CCP and depository/CSD handle post-trade novation, netting and settlement. An execution report from the exchange is not settlement — these are genuinely separate systems with asynchronous handoff between them.
- Why does this distinction matter architecturally? — Collapsing them implies the matching engine's commit must wait on clearing/settlement, which it must not — clearing failures downstream can never silently reverse a validly executed trade.
Why might you halt rather than keep serving?
Because continuing to accept or match orders under an unsafe condition (risk-service outage with stale limits, journal write failures, detected book-state divergence, extreme volatility beyond assumed capacity) risks violating a correctness invariant — and a wrong trade is far more costly and harder to unwind than a temporary, clearly communicated unavailability. Fail-closed-to-halt is a deliberate design stance here, not a reliability shortcoming.
- Isn't 100% uptime the goal? — No — correctness is prioritized over raw availability for this design; a halt that preserves invariants is a success case, and the interview answer should say so explicitly rather than apologize for it.
Advanced Senior/Staff Follow-Ups
These are the questions that separate a candidate who can describe the architecture from one who has actually reasoned about its edges. Expect them in the last 10-15 minutes, or as immediate pushback on a core answer above.
Define the precise acknowledgment/durability contract.
State it as a single sentence an interviewer can't poke a hole in: the client receives an acknowledgment only after the command is durably committed to the journal (and, if group/replicated commit is used, after it satisfies that commit policy) — never before. Vague answers like "we persist it async" invite the obvious follow-up below.
- What exactly is the trade-off of synchronous vs group-commit durability? — Synchronous per-command commit maximizes durability guarantees at a latency cost per command; group commit amortizes that cost across a small batch at the cost of a slightly larger window of at-risk, not-yet-durable commands if the process dies mid-batch.
What happens on a crash after journal commit but before the client response is sent?
The command is durable (it's in the journal and will be replayed/reflected on recovery), but the client never received confirmation — so from the client's point of view the outcome is unknown, not failed. The correct behavior is an idempotent status/retry query keyed on the client order ID, which returns the order's true post-recovery state rather than creating a duplicate.
- Why is this fundamentally different from a lost order? — A lost order (never journaled) is actually absent from the system of record; a lost response (journaled, but ack never arrived) is present and will surface correctly on query — conflating these two leads to wrong retry logic.
How does replay behave across a journal schema evolution?
Journal records need an explicit schema version tag; replay logic must be able to interpret every historical version it might encounter, either by versioned deserializers or by an upgrade/migration pass over old snapshots — silently assuming the current schema when replaying old records is a correctness bug waiting to happen.
- What's a concrete failure mode if this is skipped? — A new required field with an implicit default on old records could cause the replayed book to diverge from the original committed state — exactly the kind of silent divergence Scenario 26 in Failure & Scale Scenarios is about.
Is matching deterministic across different software versions of the engine?
Not for free — replaying an old journal through a newer engine binary is only safe if the matching logic, floating/fixed-point arithmetic, and ordering semantics are provably unchanged for that version range; otherwise replay can produce a different (and wrong) book. This needs explicit versioning and, ideally, replay-equivalence testing across releases, not an assumption.
- How would you test for this in practice? — Golden-replay regression tests: replay a fixed historical journal through each new engine build and diff the resulting book/trade stream against the previously verified output.
How does leader fencing prevent split brain?
Each partition owner holds an epoch/term number that strictly increases on every promotion; a fenced-out former leader's writes are rejected by downstream dependencies (journal, network) once a higher epoch is active, so even a "zombie" old leader that hasn't realized it's been replaced cannot commit conflicting state. See Replication and Leader Fencing.
- What if the network partition means the old leader can still write to its local journal? — The fencing token must be checked by whatever the leader needs quorum/acknowledgment from (e.g. replication acceptors), not merely assumed by the leader itself — self-reported leadership is not a safe fencing mechanism.
How does gateway session failover work without losing sequence continuity?
Gateway sessions carry their own session-level sequence numbers (distinct from the book's matching sequence); on reconnect/failover, the client and new gateway instance resynchronize from the last acknowledged session sequence, replaying or resending anything in the gap — the matching engine's sequence is unaffected because gateway identity is not part of its ordering authority.
- What happens to in-flight commands during the failover window? — They must be tracked durably enough (e.g. already journaled, or held in a bounded retry buffer) that failover doesn't silently drop an accepted-but-unacknowledged command.
Can an order ever be atomic across two symbols (e.g. a cross-instrument hedge)?
Not within this single-writer-per-book design — each book is independently sequenced and matched, so there is no built-in cross-symbol atomicity. A genuine multi-leg atomic requirement would need an explicit higher-level coordination layer (and an honest discussion of what happens if one leg fills and the other can't), not an implicit assumption that two books commit together.
- Is this a limitation worth calling out proactively? — Yes — naming this limitation unprompted is a strong staff-level signal; it shows you understand the boundary of your own design rather than waiting to be caught by it.
How is pre-trade risk state kept consistent across partitions?
Per-member risk limits (position, notional, order-rate) may need to be enforced across multiple instrument partitions a member trades on simultaneously, but each partition's risk check only has local visibility by default. This requires either a shared, low-latency risk-state service with reservation semantics, or accepting slightly relaxed cross-partition enforcement with reconciliation — and you should state explicitly which trade-off you're making and why.
- What's the risk-staleness vs latency trade-off here? — A centralized risk service adds a network hop (and staleness window) to every order's hot path; a fully local-only check is fast but can't see a member's aggregate exposure across symbols in real time.
How do you route orders correctly during an instrument migration between partition owners?
Migration needs a fencing barrier: the old owner stops accepting new commands for that instrument at a known sequence point, drains/journals its final state, and the new owner only begins accepting once routing metadata is updated and propagated — any window where both think they own the instrument (or neither does) is the bug to design out.
- What if a gateway's routing metadata is stale during the migration? — It should reject or redirect rather than guess — forwarding to the old (now fenced) owner should produce a clear rejection the gateway can retry against updated metadata, not a silent misroute.
Is FOK feasibility actually safe to check under concurrent order flow?
Yes, because the feasibility check and the fill both happen inside the same single serialized matching step for that book — there's no window for another command to modify the book between the check and the commit, since the engine processes one command at a time per instrument. The "concurrency" risk would only exist if you mistakenly checked feasibility and executed as two separate steps.
- Does this generalize to a multi-threaded matching engine? — No — this safety argument depends on single-writer-per-book serialization; a shared multithreaded book would need explicit locking or versioning to preserve the same guarantee, at a real complexity and likely latency cost.
How does a market-data subscriber recover from a feed gap?
The subscriber detects a sequence-number gap in the delta stream, discards buffered deltas it can no longer trust, requests or pulls a fresh full snapshot plus its sequence number, and resumes applying deltas from that point forward — it never attempts to interpolate or guess the missing update.
- What if gaps happen very frequently for one subscriber? — That's a subscriber-side or network-path problem to isolate and alert on (e.g. a slow consumer or a lossy link) — it should not be allowed to pressure the publisher into changing its fan-out behavior for all other subscribers.
Walk through the tail-latency budget using queueing-theory intuition.
As utilization of any single-writer stage (notably the matching engine) approaches its capacity, queueing delay grows nonlinearly, not linearly — this is the core reason p99/p99.9 latency can spike dramatically well before average utilization looks alarming. A credible answer names the stages (gateway, risk, sequencer, matching, journal, egress), which are synchronous vs asynchronous, and that headroom (not just raw throughput) is what protects tail latency.
- What's a concrete lever to protect p99.9 specifically? — Bounded queues with admission control/backpressure rather than unbounded buffering — an unbounded queue trades a clear rejection now for an unbounded, worse latency spike later.
What's the actual scaling limit for one hot symbol, and how do you plan for it?
A single hot instrument is capped by one single-writer engine's achievable throughput for that book — you cannot add more writers to the same book without breaking ordering guarantees. Planning for it means knowing that ceiling ahead of time (from load testing, not guessing), monitoring queue depth/p99 as leading indicators, and having an explicit admission-control or halt policy for when the ceiling is approached, rather than discovering the limit during a real volatility event.
- Is vertical scaling (faster hardware/lower-latency code path) the only lever? — It's the main lever for a single book; the complementary lever is reducing skew by not concentrating irrelevant load (e.g. keep non-matching traffic like status queries off the same hot path), not parallelizing the book itself.
How does clock uncertainty interact with fairness guarantees?
Fairness depends entirely on the sequencer's assigned order, which is explicitly not wall-clock based — so clock drift between gateways or members has no effect on matching fairness. Clocks still matter for audit timestamps, latency measurement and compliance records, so synchronized time (e.g. NTP/PTP-class accuracy, stated as an assumption) matters for traceability even though it doesn't matter for correctness of matching order.
- Could you use timestamps as a tiebreaker instead of a sequencer? — No — that's the exact anti-pattern this design avoids, because two independently-stamped timestamps can't be trusted to reflect true causal order across a distributed set of gateway connections.
How do you handle auditability and a trade correction/bust?
Every committed execution has a durable, immutable, uniquely-identified record in the journal — a trade correction is never an in-place edit of that record. It's a new, explicitly-controlled correction/bust event that references the original trade ID, requiring its own authorization and audit trail, so the full history (original plus correction) remains reconstructable.
- Why does this matter for replay determinism? — If a correction silently rewrote history instead of appending a new linked event, replaying the journal from scratch would no longer reproduce the same sequence of events the system actually experienced.
How would you plan capacity for an extreme-volatility day (e.g. a 50-100x burst)?
Start from an explicit illustrative baseline and burst multiplier (stated as assumptions, never as verified benchmarks), identify which stage saturates first under that multiplier (usually the hottest single-symbol matching engine and its ingress queue), and design the response as a combination of pre-provisioned headroom, admission control/throttling, and — if genuinely exceeded — a deterministic halt rather than accepting orders you can no longer process correctly or within acceptable latency.
- Is adding more hardware the whole answer? — Only up to the single-writer-per-book ceiling discussed above — beyond that, the honest answer is policy (halt/throttle), not infinite scale-out of one book.
Whiteboard and Pseudocode Exercises
These are hands-on exercises an interviewer might actually ask you to do at the whiteboard, not just talk about. Practice drawing and narrating each one in under 3-4 minutes.
1. Draw the book before and after Test A and Test B
Start from the canonical ACME book (asks S1 $101.00×50, S2 $101.00×30, S3 $101.50×70; bids B1 $100.50×40, B2 $100.00×80). Draw it, then draw the post-state for Test A (BUY 60 @ $101.00: S1 fully filled, S2 left at 20) and, from a fresh reset, Test B (BUY 100 @ $101.50: S1 and S2 fully filled, S3 left at 50). Narrate which price level is consumed first and why the remaining S2/S3 quantity keeps its original time priority.
2. Write matching pseudocode for an incoming limit order
Describe the shape rather than dumping a full listing: while the incoming order is marketable (crosses the best opposing price) and has remaining quantity, take the head order of the best opposing level, fill min(incoming.remaining, resting.remaining), emit the execution report for both sides, remove the resting order if exhausted (and the level if now empty), then loop; once no longer marketable, rest any remainder on the correct side. The full, exact pseudocode with invariant checks is in Price-Time Priority Algorithm — don't try to reproduce it verbatim here; narrate the shape and the invariants it must preserve (no negative remaining, FIFO within a level).
3. Implement cancel lookup with O(1) access
Maintain a hash map from order ID to a direct reference (pointer/node) into its FIFO queue position, alongside the price-level tree. A cancel request looks up the order ID in the hash map, finds its node, and removes it from the queue in O(1) — without that map, cancellation would require an O(n) scan of a price level or worse. Narrate why cancellation has to be this fast relative to the matching throughput target.
4. Explain a crash-recovery event timeline
Sketch a timeline: engine processes commands up to sequence 1,204, crashes; standby/restarted instance loads the last verified snapshot (say, sequence 1,100); it replays journaled commands 1,101 through 1,204 deterministically; it verifies the resulting book against a checksum or sanity invariant; only then does it resume accepting new commands at 1,205. Call out explicitly that the instrument is unavailable/halted for that symbol during the replay window.
5. Design a snapshot + delta market-data feed
A subscriber first requests a full snapshot (current book state plus its sequence number N), then applies incoming deltas sequenced N+1, N+2, ... as they arrive. If an incoming delta's sequence number isn't exactly one more than the last applied, the subscriber has detected a gap and must re-request a fresh snapshot rather than guess. Narrate the trade-off: snapshots are larger but self-contained; deltas are small but only useful contiguously.
6. Find the bug in this deliberately unsafe dual-leader design
Present (or be given) a design where two matching-engine instances for the same instrument both believe they're the active leader after a network partition, each independently accepting and matching commands from the clients still connected to them. The bug: nothing fences the old leader out, so both can commit conflicting executions against logically the "same" book — a split-brain violation of the single-active-leader invariant. The fix is an epoch/fencing token checked by whatever the leader needs acknowledgment from, not a self-declared leadership flag.
7. Compare single-writer and multithreaded matching
Single-writer-per-book: one thread/process owns and mutates one instrument's book serially — trivially correct ordering, but throughput for that one instrument is capped by one core's processing speed. Shared multithreaded book: multiple threads mutate the same book structure concurrently, requiring fine-grained locking or lock-free structures to stay correct — genuinely harder to reason about and verify, and lock contention can erase the throughput gain it was meant to provide, especially under the kind of all-levels-touched sweep a large incoming order causes. State a recommendation (single-writer per book, scale via per-symbol partitioning instead) and the condition that would make you reconsider (e.g. a provably safe lock-free design with verified correctness under adversarial interleavings).
Strong vs Weak Answers
The difference between a passing and failing answer at this level is rarely raw knowledge — it's whether the reasoning is anchored to explicit requirements and invariants, or floats on assumptions presented as facts.
| Recurring trap | Weak framing | Strong framing |
|---|---|---|
| Scope | Jumps straight into a full architecture diagram without asking what's in scope. | States explicit assumptions out loud first ("I'll assume continuous matching, price-time priority, a single region") and invites correction before building on them. |
| Technology choice | "We'd just use Kafka for the order flow" with no further justification. | Reasons from the correctness requirement first — deterministic per-book ordering — and only then evaluates whether a given messaging technology preserves or threatens that ordering in the matching hot path. |
| Deduplication | Claims a vague "exactly-once over the network" guarantee for the whole system. | Names the actual mechanism — durable command IDs, idempotent handling, unique execution/trade IDs, sequence numbers, per-book ordering — and scopes the guarantee to what's actually true. |
| Execution vs settlement | Treats an execution report as if the trade is now "done" end-to-end. | Explicitly separates exchange-side execution from clearinghouse/CSD-side settlement, and states that downstream clearing failures must not silently reverse a valid execution. |
| Async boundaries | Either makes everything synchronous ("safer") or everything async ("faster") without distinguishing. | Names precisely what must be synchronous (risk check, sequencing, matching commit) versus what can safely be asynchronous (market-data fan-out, post-trade handoff, analytics) and why each falls on its side. |
| Latency claims | States an unverified microsecond number as if it were measured production fact. | Frames latency numbers explicitly as illustrative assumptions, names the real cost of a proposed optimization (e.g. busy-polling burns CPU and power to shave queueing delay), and avoids claiming a specific benchmark without evidence. |
| Scale adaptability | Presents one static architecture as correct at every scale from day one to 100x. | Shows how the design would change under different scale assumptions — e.g. a single engine is fine at modest volume, but hot-symbol partitioning and a sequencer become necessary once skew and concurrency are real — and names the signal that would trigger that evolution. |
Interview Question Bank
At least 25 distinct questions beyond the core set above, grouped by theme. Each has a real answer outline and at least one follow-up — treat these as a second pass once you've internalized Sections 3 and 4, not a restatement of them.
Fundamentals
What's the difference between a maker and a taker?
A maker is the resting order that was already on the book and provides liquidity; a taker is the incoming order that crosses the book and removes (takes) that liquidity. In the ACME example, S1 and S2 are makers; an incoming marketable BUY is the taker.
- Why does this distinction matter beyond terminology? — Some venues use maker/taker fee or rebate structures as policy — mentioned here as an optional extension, not a universal exchange rule.
What's the difference between a tick size and a lot size?
Tick size is the minimum price increment an order can be quoted at (e.g. $0.01 for ACME); lot size is the minimum tradeable quantity unit (e.g. 1 share for ACME). Both are venue-policy parameters, not universal constants — other venues and instruments can use different values.
- What happens if an incoming order violates tick size? — It should be rejected at validation, before sequencing — a price-band/tick violation is a pre-trade check, not something the matching engine should ever need to handle.
Why does a market order never guarantee a price?
A market order sweeps whatever depth is available at whatever prices are resting, in price-time order, until filled or liquidity runs out — if the book is thin, it can execute far from the last traded price, or fill only partially (or not at all) depending on available liquidity and the venue's policy for unfilled market orders.
- How would you protect a client from an extreme fill price? — That's what price collars / protection bands are for — a venue-level control that bounds how far a market order can sweep, treated here as an explicit policy choice, not an implicit guarantee.
What's the functional difference between the exchange and a clearinghouse?
The exchange is responsible for order acceptance, deterministic sequencing, matching and execution reporting — it decides *that* a trade happened. The clearinghouse/CCP is responsible for *what happens after* — becoming the counterparty to both sides (novation), netting obligations, and coordinating settlement with depositories. They are separate systems with an asynchronous handoff, not one synchronous write.
- What would go wrong if you modeled them as one synchronous step? — Matching commit latency would become hostage to clearing-system availability and latency, and a clearing hiccup could incorrectly appear to invalidate an already-valid execution.
Algorithms and Data Structures
What's the time complexity of inserting a new resting order?
Roughly O(log P) to locate or create the correct price level in a balanced-tree/ordered-map structure (P = number of distinct price levels), plus O(1) to append to that level's FIFO queue — the exact complexity depends on which concrete structure you choose, and that choice should be named explicitly rather than assumed.
- What about a market order that sweeps five price levels? — That adds O(number of levels crossed) on top of the lookup cost, since each crossed level may be partially or fully consumed and potentially removed.
Why represent price as integer ticks instead of floating-point dollars?
Floating-point arithmetic introduces representation and rounding error that is unacceptable for money and exact price comparisons — representing price as an integer number of ticks (e.g. $101.00 as 10100 ticks at $0.01 tick size) makes comparisons and arithmetic exact and deterministic, which matters enormously for replay determinism.
- Does this affect how trade value/notional is computed? — Notional (price × quantity) should also be computed and stored using fixed-point/integer arithmetic end-to-end, not converted to floating-point at any intermediate step where precision could be lost.
How would you detect and prevent a crossed book (best bid >= best ask) after processing?
After every command is fully processed, assert the invariant that best bid < best ask (or the book is empty on one/both sides) — if that assertion ever fails outside an explicitly allowed auction/halt state, it indicates a matching-logic bug and should trigger an alert and likely a halt for investigation, not be silently ignored.
- Are there legitimate exceptions? — Yes — auctions and halts can show a transiently crossed or indicative state by design; the invariant applies specifically to continuous trading, and the design should say so explicitly rather than claim a universal rule.
How would you implement an iceberg (hidden-quantity) order's priority?
Only the currently displayed slice is visible in the public book and participates in time priority at that price; when the displayed slice is exhausted, the next slice is released — commonly re-entering the back of the time queue at that price level, which is a real priority cost to the order and should be named as such, not hidden as if it were free.
- Why mention this as an extension rather than baseline? — Iceberg/hidden orders and their exact priority implications are venue-policy-dependent, consistent with this spec's treatment of them as an advanced topic rather than a universal matching rule.
Concurrency and Ordering
Why is the matching engine typically single-writer per instrument rather than multithreaded?
A single-writer event loop per book trivially guarantees the serialized ordering that price-time priority and all correctness invariants depend on; a shared multithreaded book requires locking or lock-free coordination to achieve the same guarantee, adding real complexity and contention risk for a throughput gain that's capped anyway by the single logical book's dependencies.
- Can other parts of the pipeline still be multithreaded? — Yes — networking, risk checks, journaling and market-data fan-out can all be parallelized freely; only the mutation of one instrument's book needs single-writer discipline.
What happens to ordering guarantees if a retried command is processed twice?
The durable command/client-order ID lets the engine recognize the retry and return the original's outcome rather than process it as a second, new command — ordering guarantees aren't threatened because the retry never gets a second sequence number or a second matching effect.
- What if the retry arrives with a different session sequence number? — Session-level sequence numbers (from the gateway/session layer) are distinct from the command's durable client order ID; dedup must key off the durable identity, not the transport-level sequence, which can legitimately differ across retries.
How would ordering be affected by gateway failover mid-session?
Gateway failover can reorder or duplicate delivery at the transport layer, but it cannot change the matching engine's authoritative sequence, because that sequence is assigned by the per-book sequencer after acceptance, not by the gateway or by arrival order.
- So is anything actually at risk during gateway failover? — Yes — in-flight, not-yet-acknowledged commands during the failover window need careful handling so they're neither lost nor duplicated at the session layer, even though the matching-order guarantee itself is unaffected.
Durability and Recovery
What's the difference between RPO and RTO in this design, concretely?
RPO (recovery point objective) is how much committed data you could lose — ideally zero here, since journal commit happens before acknowledgment. RTO (recovery time objective) is how long recovery takes — driven by snapshot interval and the volume of journal to replay since the last snapshot; a longer gap between snapshots means a longer RTO even with zero RPO.
- How would you reduce RTO without touching RPO? — Take snapshots more frequently, so there's less journal to replay after a crash — a pure operational tuning knob, independent of the durability (RPO) guarantee.
What should happen if a snapshot is found to be corrupted during recovery?
The recovery process should detect this via a checksum, fall back to the next-older verified snapshot, and replay a longer span of the journal from there — silently trusting an unverified snapshot risks rebuilding an incorrect book with high confidence.
- What if no earlier snapshot exists? — Fall back to replaying the entire journal from the beginning — slower, but correct; this is exactly why checksums and periodic snapshot verification matter operationally, not just as an afterthought.
How do you reconcile outbound execution reports after a recovery?
Recovery must distinguish between reports that were durably generated but possibly never delivered (replay them from the journal) versus reports that were never generated at all before the crash (generate them now) — reconciliation compares what the journal says was committed against what downstream systems/clients acknowledge having received.
- Could this ever produce a duplicate delivery to the client? — Possibly, at the delivery layer — which is why execution reports should carry a stable, unique execution ID so the client-side system can itself deduplicate on receipt.
Scalability and Latency
Why doesn't adding more matching-engine instances linearly increase throughput for one instrument?
Throughput for a single book is bounded by the single-writer invariant — you scale exchange-wide throughput by adding more independently-owned books (more partitions/instruments), not by adding more writers to the same book, which would violate ordering correctness.
- What does scale out actually look like then? — Horizontal scale across symbols — route each instrument to a stable owning engine, and add engine instances as the number of instruments (not the throughput of one instrument) grows.
What's the latency cost of adding a synchronous pre-trade risk check to every order?
It adds a real, sequential hop to the hot path (gateway → risk → sequencer → matching), so its own latency and failure behavior directly affect every order's total latency budget — which is exactly why the risk engine's own availability, caching strategy and failure-safe policy need to be designed with the same rigor as matching itself.
- Could this check ever safely be deferred/async? — Only for checks where a false negative (a bad order slipping through) is tolerable, which is rarely true for hard limits like max notional — so this needs to be an explicit, justified decision per check, not a blanket default.
How would you benchmark matching-engine latency fairly?
Distinguish network-to-network latency, gateway-to-ack latency, and pure engine processing latency as separate measurements; report percentiles (p50/p99/p99.9), not just an average, under a stated and reproducible load profile — and never present a number as a verified production benchmark unless it actually is one.
- Why specifically avoid averages? — Averages hide the tail-latency spikes that matter most for a system where a handful of slow orders during a burst can represent real financial impact, even if the average looks fine.
Market Data and Protocols
Why use UDP multicast for some market-data distribution instead of always using TCP?
UDP multicast can fan out identical data to many subscribers without per-subscriber connection overhead, which suits low-latency, high-fan-out feeds where occasional loss is acceptable because gap-detection-plus-resync handles it; TCP's reliability and per-connection semantics suit order-entry sessions where every message must arrive and be acknowledged. These are different tools for different jobs, not universal substitutes for one another.
- Does every venue use this exact split? — No — protocol stacks are venue-specific; this is presented as a representative pattern, not a claim that all exchanges use identical protocols.
What's the difference between top-of-book and full-depth (level 2) market data?
Top-of-book is just the best bid and best ask (and often last trade price); full depth/level 2 includes multiple price levels on each side with aggregate (or order-level) quantity — full depth has materially higher bandwidth and fan-out cost, so venues often tier entitlement and latency between the two.
- Why might these have different latency/entitlement rules? — Full-depth data is commercially and competitively more valuable, so direct/low-latency full-depth feeds are often a separate, more tightly entitled product from a slower public top-of-book feed.
How would you handle a trade print that needs to be disseminated out of commit order (e.g. due to fan-out tiering)?
The trade/book-mutation order inside the engine (its true execution order) must remain authoritative and reconstructable from the journal, even if dissemination to different subscriber tiers happens at different speeds — dissemination order across tiers is a distribution-layer concern and must never be confused with or allowed to redefine true execution order.
- What would go wrong if you conflated the two? — A subscriber on a slower tier could believe trades happened in a different order than they actually did, which breaks any downstream logic (e.g. VWAP calculations) that assumes feed order equals true execution order.
Risk, Operations and Security
What's the difference between a price collar and a trading halt?
A price collar rejects or constrains an individual order whose price is implausibly far from a reference price (fat-finger protection) without stopping the market; a trading halt stops all matching for an instrument (or the venue) entirely, typically in response to a broader condition like extreme volatility, a detected integrity issue, or a regulatory/administrative action.
- Which one would catch a single mistyped order for 100x the intended quantity? — A price-collar/fat-finger check on notional or price deviation, not a halt — a halt is a blunt, market-wide instrument, not meant as the first line of defense against one bad order.
How would you detect and contain a message-flood / abusive client?
Per-member/per-session rate limiting and order-to-trade ratio monitoring at the gateway, with a graduated response (throttle, then temporary suspension, then a kill switch for a clearly abusive session) — detection relies on monitoring request rate, reject rate and order-to-trade ratio against the member's baseline, not just a single hard-coded threshold.
- Why not just block the first rate-limit violation permanently? — A graduated response avoids false-positive harm to a legitimate member during a temporary burst, while still containing genuine abuse quickly — proportionality matters operationally.
What should an audit record for a single order capture?
At minimum: the durable sequence number, client and exchange order IDs, all state transitions with timestamps, the member/session identity, and links to any resulting execution/trade IDs — enough that a regulator or internal investigator can reconstruct exactly what happened and in what order, independent of the live book's current state.
- Why can't the live book alone serve as the audit trail? — The live book only reflects current state, not history — a cancelled or fully-filled order may no longer exist in the book at all, so the immutable journal (not the mutable book) is the actual audit source of truth.
Staff-Level Architecture Judgment
If given unlimited budget, would you still choose single-writer-per-book matching?
Yes — this is a correctness-driven choice, not a cost-driven one; unlimited budget lets you buy faster hardware, better networking and more partitions, but it doesn't change the fact that one logical order book needs one authoritative mutator to preserve price-time priority. A strong answer explicitly separates "what more money buys" from "what the correctness invariant requires."
- What would unlimited budget actually change, then? — More redundant standby capacity per partition, more aggressive latency engineering (dedicated networking, CPU affinity), and more partitions/engines to reduce symbol concentration — all scale/latency levers, not a different consistency model.
How would you evolve this design if the venue needed to add a second, geographically distant matching site for the same instruments?
You cannot have two simultaneously active matching authorities for the same book without breaking the single-active-leader invariant — a second site has to be either a fenced, non-active standby for disaster recovery, or the venue has to accept running them as genuinely separate liquidity pools (effectively separate books) with their own independent price-time priority, which is a materially different product decision, not just an infrastructure one.
- Is this the same problem as multi-region cloud deployment generally? — It's a sharper version of it — generic multi-region active-active patterns for stateless services don't transfer here, because the correctness invariant (one authoritative book) is stronger than typical eventual-consistency tolerances.
How would you decide when it's time to split a single exchange-wide sequencer into per-partition sequencers (if starting from a central one)?
Watch for the central sequencer itself becoming a throughput or latency bottleneck across unrelated instruments — once that's measurably true, moving to per-partition sequencing (each instrument/partition gets its own independent sequencer) removes unrelated-instrument coupling, at the cost of losing any cross-instrument total ordering the central sequencer incidentally provided (which this design never promised anyway).
- What's the actual signal that would trigger this change? — Central-sequencer queue depth or latency rising even when no single instrument is particularly hot — that's the tell that the bottleneck is the shared sequencer itself, not any one book.
Quick Revision
A condensed pass for the night before (or the hour before) the interview — the architecture shape, the invariants, the vocabulary, and a 30-second and 2-minute answer you can actually deliver out loud.
- No negative remaining quantity, ever, on any order.
- Total executed quantity never exceeds an order's original quantity.
- Trade quantity is deducted equally and atomically from both the buy and sell side.
- Orders at the same price level execute strictly FIFO by sequencer order, never by wall-clock arrival.
- Bids are sorted descending by price; asks ascending — best price is always at the top of each side.
- Every committed state transition has a durable, replayable position in the journal before it's considered final.
- Replaying a deterministic command stream from a snapshot always yields the identical book and trade IDs.
- Exactly one authorized active leader exists per partition per epoch — never two.
- A duplicate command submission (same client order ID) can never produce a duplicate matching effect.
- A completed cancel can never retroactively undo an already-committed fill.
| Term | Definition |
|---|---|
| Bid | The best price a buyer is currently willing to pay, resting on the buy side of the book. |
| Ask | The best price a seller is currently willing to accept, resting on the sell side of the book. |
| Spread | The gap between best ask and best bid — in the ACME example, $101.00 − $100.50 = $0.50. |
| Tick | The minimum price increment an order can be quoted at (e.g. $0.01 for ACME). |
| Lot | The minimum tradeable quantity unit (e.g. 1 share for ACME). |
| Maker / taker | Maker = the resting order that supplies liquidity; taker = the incoming order that removes it by crossing the book. |
| Price-time priority | Match best price first, then earliest arrival (sequencer order, not wall clock) within that price level. |
| IOC | Immediate-or-Cancel — fill whatever is immediately marketable, cancel any unfilled remainder instead of resting it. |
| FOK | Fill-or-Kill — execute the full requested quantity immediately or execute none of it; no partial fills. |
| Epoch | A strictly increasing term/generation number used to fence out a replaced or failed partition leader and prevent split brain. |
| Trade-off | Choice made here | Alternative |
|---|---|---|
| Matching concurrency | Single writer per book | Shared multithreaded book with locking |
| Order book storage | In-memory structures + durable journal | Database-backed live book |
| Command ordering | Per-partition sequencer | Central global sequencer |
| Replication | Active-passive with epoch fencing | Active-active on the same book |
| Risk validation | Synchronous pre-trade checks | Deferred/async checks |
| Failure scenario | Key response |
|---|---|
| Matching engine process crashes | Load last snapshot, replay journal deterministically, verify, then resume. |
| Cancel races with a fill | Serialize both through the sequencer; outcome reflects whichever was sequenced first, never retroactive. |
| Primary crash and standby takeover | Fence the old leader with a higher epoch before the standby begins accepting commands. |
| Market-data packet loss | Subscriber detects the sequence gap and resyncs from a fresh snapshot, never guesses. |
| Hot-symbol overload / volatility burst | Bounded queues, admission control, and — if truly exceeded — a controlled halt rather than silent degradation. |
The 30-second answer
A stock exchange accepts orders from brokers, deterministically sequences them per instrument, matches compatible buy/sell orders in a single-writer in-memory order book using price-time priority, durably journals every command and execution before acknowledging it, and publishes market data and trade events asynchronously off that critical path. Crash recovery replays the journal from the last snapshot deterministically, and exactly one fenced leader owns each instrument at a time.
The 2-minute answer
I'd scope this as the exchange itself — order acceptance, matching and market data for a single asset class, continuous trading with price-time priority — distinct from a broker's app and from post-trade clearing/settlement. The core is a single-writer matching engine per instrument holding an in-memory order book: bids sorted descending, asks ascending, FIFO within each price level. An incoming order is validated, risk-checked, and assigned a sequence number by a per-instrument sequencer before the engine ever sees it — that sequence, not wall-clock arrival, is what makes two near-simultaneous orders deterministic. The engine matches against the best opposing price, filling at the resting order's price, and rests any unfilled remainder. Every accepted command and emitted execution is written to a durable journal before the client is acknowledged, with periodic snapshots so recovery replays only the tail of the journal, not its entirety. Market data — snapshots plus sequenced deltas — and post-trade handoff to clearing are both asynchronous consumers off that synchronous critical path, so a slow subscriber or a clearing hiccup never blocks or reverses a valid execution. For scale, I'd partition by instrument rather than parallelize one book, since a single book's ordering guarantee requires one writer; a hot symbol is bounded by that one writer's throughput, so the real levers are admission control and, if genuinely exceeded, a deliberate halt. For availability, I'd use active-passive replication with epoch-based leader fencing, so a standby can safely take over after a crash without ever risking two simultaneously active leaders for the same book. The design prioritizes correctness and determinism over raw availability, deliberately failing closed to a halt rather than risking an invariant violation.