Skip to main content

Command Palette

Search for a command to run...

The One Rule Interviewers Expect in a Stock Trading System: Price–Time Priority

Updated
4 min read
The One Rule Interviewers Expect in a Stock Trading System: Price–Time Priority

Price–Time Priority

The One Rule Interviewers Expect in a Stock Trading System: Price–Time Priority

In any stock trading system the matching engine must enforce price–time priority. This is not optional — it’s the market’s fairness contract.

At its core the rule is simple:

  1. Better price wins. For bids, a higher price is better. For asks, a lower price is better.
  2. If prices tie, earlier timestamp wins — first in, first out (FIFO) within the same price level.

Because this is the law of fairness in matching, your design must guarantee both parts: quick access to the best price and strict FIFO ordering for orders at the same price.

Object-Oriented Design implications

An OrderBook cannot be a simple flat list of orders. Instead model two sides — bids and asks — each as a structure that:

  • Keeps the best price immediately accessible (max for bids, min for asks).
  • Preserves FIFO within each price level (queue semantics for orders that share the same price).

A common and effective representation is:

  • price → queue(Order)
  • a sorted index of prices to allow retrieving the best price quickly (e.g., a balanced tree or heap)

This yields efficient operations:

  • Find best price: O(log P) (P = number of distinct price levels) using a tree map or heap.
  • Enqueue/dequeue at a price level: O(1) using a linked queue (deque).

Typical data-structures by language

  • Java: TreeMap> (bids use descendingKeySet; asks use ascendingKeySet).
  • C++: std::map or std::set of price-level objects with std::deque for orders at each level.
  • Python: sortedcontainers.SortedDict or bisect + list of price levels, with collections.deque for per-price queues.

Matching algorithm (high level)

When a new order arrives:

  1. Determine the opposite side (new bid matches existing asks; new ask matches existing bids).
  2. While the order has remaining quantity and there exists at least one compatible price on the opposite side:
    • Pick the best price on the opposite side.
    • Pop from the head of that price's queue (oldest order at that price).
    • Execute a trade for the min of remaining quantities.
    • If the price-level queue becomes empty, remove that price from the sorted index.
  3. If the incoming order still has remaining quantity, insert it at its price level's queue (newest at tail).

This preserves:

  • Price priority: you always match against the best price first.
  • Time priority: within a price you always consume the oldest order first.

Pseudocode

function match(incomingOrder): opposite = (incomingOrder.side == BUY) ? asks : bids while incomingOrder.qty > 0 and opposite.has_compatible_price(incomingOrder.price): bestPrice = opposite.best_price() queue = opposite.get_queue(bestPrice) restingOrder = queue.peek() tradeQty = min(incomingOrder.qty, restingOrder.qty) execute_trade(incomingOrder, restingOrder, tradeQty, tradePrice = restingOrder.price) decrease quantities accordingly if restingOrder.qty == 0: queue.pop() if queue.empty(): opposite.remove_price_level(bestPrice) if incomingOrder.qty > 0: sameSide = (incomingOrder.side == BUY) ? bids : asks sameSide.get_or_create_queue(incomingOrder.price).push(incomingOrder)

Example

  • Existing asks: 100.50 → [Order A at t1], 101.00 → [Order B at t2]
  • Incoming bid at 101.00 (t3): matches against 100.50 first because it's the best ask (lowest price). Within 100.50, Order A (t1) is matched before any later 100.50 orders.

Performance & concurrency notes

  • Keep price-level queues lockable independently to reduce contention (per-price locks or lock-free queues where possible).
  • Use a single matching thread for determinism in high-frequency engines; or partition by symbol to avoid cross-symbol locking.
  • Persist trades and changes durably and in order to meet audit/regulatory needs.

Edge cases and testing

  • Partial fills must leave correct quantities and timestamps.
  • Orders with identical timestamps (rare) should have a deterministic tie-breaker (e.g., internal sequence number assigned on receipt).
  • Verify behavior on order cancellation and replacement (ensure FIFO at price level still holds).
  • Simulate high-throughput scenarios: many orders at same price, bursts, and out-of-order network delivery.

Summary

Price–time priority is the single non-negotiable rule in matching engines: best price first, then earliest order at that price. Implement an OrderBook as price→queue plus a sorted index of price levels to guarantee both fast best-price access and FIFO within a price level. This design is efficient, auditable, and preserves market fairness.

More from this blog

B

bugfree.ai

361 posts

bugfree.ai is an advanced AI-powered platform designed to help software engineers and data scientist to master system design and behavioral and data interviews.