Match Limit Orders with Price-Time Priority

Quick Overview

Implement a limit-order matcher that honors crossing rules, price-time priority, partial fills, and resting-order prices. Produce trades and final books in deterministic order while reasoning about data structures, large quantities, edge cases, and logarithmic processing.

Match Limit Orders with Price-Time Priority

Company: Two Sigma

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

Implement an in-memory limit-order matcher that processes orders in arrival order and returns both the executed trades and the final resting order books. Each input order has this shape: ```text Order { id: string side: "BUY" | "SELL" price: integer quantity: integer } ``` Use these matching rules: 1. A buy order can trade with a sell order when the buy price is greater than or equal to the sell price. 2. The best resting buy has the highest price; the best resting sell has the lowest price. 3. At the same price, the order that arrived first has priority. The position in the input array is its arrival time. 4. Match an incoming order repeatedly against the best eligible resting order until the incoming order is filled or the books no longer cross. 5. A trade executes at the resting order's price. A partially filled resting order keeps its original time priority. An unfilled remainder of the incoming order becomes a resting order with its original arrival time. 6. Order IDs are unique. Return: ```text MatchResult { trades: Trade[] buyBook: RestingOrder[] sellBook: RestingOrder[] } Trade { buyId: string sellId: string price: integer quantity: integer } RestingOrder { id: string price: integer remainingQuantity: integer } ``` Trade records must appear in execution order. Return the buy book in descending price and then arrival order, and the sell book in ascending price and then arrival order. ## Constraints - `1 <= orders.length <= 200,000` - `1 <= price, quantity <= 1,000,000,000` - The sum of quantities can exceed a signed 32-bit integer; use 64-bit arithmetic for quantities and aggregates. - The expected running time is `O(n log n)` and the order books may use `O(n)` space. ## Example ```text orders = [ {id: "B1", side: "BUY", price: 100, quantity: 5}, {id: "S1", side: "SELL", price: 99, quantity: 2}, {id: "S2", side: "SELL", price: 100, quantity: 5} ] ``` The result is: ```text trades = [ {buyId: "B1", sellId: "S1", price: 100, quantity: 2}, {buyId: "B1", sellId: "S2", price: 100, quantity: 3} ] buyBook = [] sellBook = [ {id: "S2", price: 100, remainingQuantity: 2} ] ``` `B1` is already resting when each sell arrives, so both trades use its price. The second trade partially fills `S2`, whose remainder enters the sell book. ## Interview Discussion After implementing the matcher, be prepared to: - Design tests for partial fills, non-crossing books, multiple matches, and price-time priority at equal prices. - Diagnose a mismatch between a computed result and a sample by tracing book state after every arrival. - Explain why order ID or quantity should not replace arrival time as the equal-price tie-breaker. - Compare heaps with balanced trees, linked lists, or per-price FIFO queues, including the operations each design makes cheap or expensive. - Identify what you would improve with more time, including the way trade records and the final output string or object are assembled.

Quick Answer: Implement a limit-order matcher that honors crossing rules, price-time priority, partial fills, and resting-order prices. Produce trades and final books in deterministic order while reasoning about data structures, large quantities, edge cases, and logarithmic processing.

|Home/Coding & Algorithms/Two Sigma
Two Sigma logo
Two Sigma
Jul 31, 2026, 12:00 AM
mediumSoftware EngineerTechnical ScreenCoding & Algorithms
2
0

Implement an in-memory limit-order matcher that processes orders in arrival order and returns both the executed trades and the final resting order books.

Each input order has this shape:

Order {
  id: string
  side: "BUY" | "SELL"
  price: integer
  quantity: integer
}

Use these matching rules:

  1. A buy order can trade with a sell order when the buy price is greater than or equal to the sell price.
  2. The best resting buy has the highest price; the best resting sell has the lowest price.
  3. At the same price, the order that arrived first has priority. The position in the input array is its arrival time.
  4. Match an incoming order repeatedly against the best eligible resting order until the incoming order is filled or the books no longer cross.
  5. A trade executes at the resting order's price. A partially filled resting order keeps its original time priority. An unfilled remainder of the incoming order becomes a resting order with its original arrival time.
  6. Order IDs are unique.

Return:

MatchResult {
  trades: Trade[]
  buyBook: RestingOrder[]
  sellBook: RestingOrder[]
}

Trade {
  buyId: string
  sellId: string
  price: integer
  quantity: integer
}

RestingOrder {
  id: string
  price: integer
  remainingQuantity: integer
}

Trade records must appear in execution order. Return the buy book in descending price and then arrival order, and the sell book in ascending price and then arrival order.

Constraints

  • 1 <= orders.length <= 200,000
  • 1 <= price, quantity <= 1,000,000,000
  • The sum of quantities can exceed a signed 32-bit integer; use 64-bit arithmetic for quantities and aggregates.
  • The expected running time is O(n log n) and the order books may use O(n) space.

Example

orders = [
  {id: "B1", side: "BUY",  price: 100, quantity: 5},
  {id: "S1", side: "SELL", price:  99, quantity: 2},
  {id: "S2", side: "SELL", price: 100, quantity: 5}
]

The result is:

trades = [
  {buyId: "B1", sellId: "S1", price: 100, quantity: 2},
  {buyId: "B1", sellId: "S2", price: 100, quantity: 3}
]
buyBook = []
sellBook = [
  {id: "S2", price: 100, remainingQuantity: 2}
]

B1 is already resting when each sell arrives, so both trades use its price. The second trade partially fills S2, whose remainder enters the sell book.

Interview Discussion

After implementing the matcher, be prepared to:

  • Design tests for partial fills, non-crossing books, multiple matches, and price-time priority at equal prices.
  • Diagnose a mismatch between a computed result and a sample by tracing book state after every arrival.
  • Explain why order ID or quantity should not replace arrival time as the equal-price tie-breaker.
  • Compare heaps with balanced trees, linked lists, or per-price FIFO queues, including the operations each design makes cheap or expensive.
  • Identify what you would improve with more time, including the way trade records and the final output string or object are assembled.

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...