Online Stock Exchange
A tiny exchange: traders place buy and sell orders on an instrument, and something has to
decide which ones turn into a trade. The class model matters more than the matching
algorithm here - get Order and OrderBook right and the matching logic is a dozen lines.
Requirements
Functional
- A trader places a buy or sell order for a quantity of an instrument, either at a fixed price (limit order) or at whatever the best available price is (market order).
- A buy order matches a sell order when their prices cross; a trade is created for the overlapping quantity.
- Partially filled orders stay on the book for their remaining quantity.
- A trader can cancel an order that hasn't fully filled yet.
Non-functional
- Matching must always prefer the best price first (highest bid, lowest ask), then earliest order at that price - price-time priority, not first-come-first-served across all prices.
- Adding a new order type (say, stop-loss) should extend
Order, not require rewritingOrderBook's matching loop.
Design
OrderBook holds two sides - bids and asks - each kept sorted by price-time priority, and
its match method only ever looks at the best order on each side. Order itself carries
whether it's a limit or market order as data (a price, or none), so the book doesn't need a
different code path per order type.
- 1The trader submits an order for one instrument - the exchange routes by instrument symbol.
- 2The order lands on the correct side (bids or asks) of that instrument’s book, sorted by price-time priority.
- 3The book peeks at the best bid and best ask; if they cross, a fill happens for the overlapping quantity.
- 4A trade is recorded once a match is found; both orders’ remaining quantities are reduced.
- 5A trader can pull an order that hasn’t fully filled; the book removes it from its side.
A Trade is the one output of matching that outlives the match call - it's what both
traders and any downstream settlement system actually care about, independent of how the
book got there.
Class diagram
Code
Design decisions
OrderBookis per-instrument, never a single book for the whole exchange. Orders for AAPL and orders for GOOG never interact, so keeping one book per instrument means matching is naturally scoped and one busy instrument's order volume never slows down matching for a quiet one.- A market order is a limit order with no price, not a separate class. Both are the same object with the same fields; a market order simply matches against the best available price on the other side instead of requiring its own price to cross. Splitting it into its own class would duplicate every field except one.
- Price-time priority is an ordering rule on the book's data structure, not a loop
condition scattered through
match. Bids are kept sorted highest-price-first (ties broken by earlier timestamp), asks lowest-price-first, somatchonly ever has to peek at the head of each side rather than scan for "the best one." - What's missing for a real system: this book is single-threaded and in-memory; a real exchange needs the match step to be atomic under concurrent order submission (a lock per instrument, or a single-writer queue per book), and needs every fill to be durably logged before being acknowledged, since a trade can't be un-happened once reported to a trader.