PracHub
QuestionsPremiumLearningGuidesCheatsheetNEWCoaches

Quick Overview

This question evaluates understanding of in-memory data structures and API design for maintaining ordered collections with FIFO semantics, correctness of state management, and algorithmic complexity considerations. It is commonly asked in Coding & Algorithms interviews to assess practical implementation skills under constraints (e.g.

  • easy
  • Citadel
  • Coding & Algorithms
  • Software Engineer

Implement an in-memory order book API

Company: Citadel

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: easy

Interview Round: Technical Screen

## Problem Implement an **in-memory limit order book** for a single trading symbol. You **do not** need to implement order matching/execution—only store and maintain orders and provide book queries. The order book has two sides: - **BUY (bid)**: higher price has higher priority. - **SELL (ask)**: lower price has higher priority. Within the same price level, orders follow **FIFO** (earlier timestamp first). ### Order model Each order has: - `order_id` (unique string or integer) - `side` ∈ `{BUY, SELL}` - `price` (positive integer) - `qty` (positive integer) - `ts` (monotonic integer timestamp assigned at insertion; used for FIFO) ### Required API Design and implement a class (or module) with the following operations: 1. `add(order_id, side, price, qty)` - Inserts a new order. - Assume `order_id` does not already exist. 2. `cancel(order_id)` - Removes an existing order by id. - If the order does not exist, do nothing. 3. `modify(order_id, new_qty)` - Updates the quantity of an existing order. - If `new_qty == 0`, treat it as `cancel(order_id)`. - If the order does not exist, do nothing. 4. `best_bid()` and `best_ask()` - Return the current best price and total quantity at that price level, e.g. `(price, total_qty)`. - If the side is empty, return `None`. 5. `top_of_book()` - Return both best bid and best ask (or `None` for a missing side). ### Constraints / expectations - Up to **2×10^5 operations**. - Target time complexity around **O(log M)** per operation, where `M` is the number of distinct price levels on a side. - Memory should be **O(N)** in the number of live orders. ### Notes - No matching means adding a BUY order does not trade against existing SELL orders; both sides simply coexist. - You may assume single-threaded execution unless you explicitly design for concurrency.

Quick Answer: This question evaluates understanding of in-memory data structures and API design for maintaining ordered collections with FIFO semantics, correctness of state management, and algorithmic complexity considerations. It is commonly asked in Coding & Algorithms interviews to assess practical implementation skills under constraints (e.g.

Implement an in-memory limit order book for a single symbol. There is no order matching or execution; you only need to store live orders, update them, and answer top-of-book queries. The book has two sides: - BUY: higher price has higher priority - SELL: lower price has higher priority If multiple orders exist at the same price, their quantities are aggregated for query results. FIFO within a price level is determined by insertion timestamp, but since there is no matching in this problem, you only need to preserve each order's timestamp internally. For this coding task, process a sequence of operations and return the results of all query operations in order. Supported operations: - ("add", order_id, side, price, qty) - ("cancel", order_id) - ("modify", order_id, new_qty) - ("best_bid",) - ("best_ask",) - ("top_of_book",) Rules: 1. add inserts a new order. order_id is guaranteed to be new. 2. cancel removes the order if it exists; otherwise do nothing. 3. modify updates the quantity if the order exists. If new_qty == 0, treat it as cancel(order_id). If the order does not exist, do nothing. 4. best_bid and best_ask return (price, total_qty) for the current best level on that side, or None if that side is empty. 5. top_of_book returns (best_bid_result, best_ask_result), where either side may be None.

Constraints

  • Up to 2 * 10^5 operations
  • price and qty are positive integers whenever provided in an add operation
  • new_qty is a non-negative integer in modify
  • order_id is unique for each add operation
  • Target complexity is around O(log M) amortized per add/query, where M is the number of distinct price levels on one side
  • Memory usage should be O(N), where N is the number of live orders

Examples

Input: ([('best_bid',), ('add', 1, 'BUY', 101, 7), ('add', 2, 'SELL', 105, 7), ('best_bid',), ('best_ask',), ('top_of_book',)],)

Expected Output: [None, (101, 7), (105, 7), ((101, 7), (105, 7))]

Explanation: Initially there are no BUY orders, so best_bid is None. After adding one BUY and one SELL order, the best bid is (101, 7), the best ask is (105, 7), and top_of_book returns both.

Input: ([('add', 1, 'BUY', 100, 5), ('add', 2, 'BUY', 99, 8), ('modify', 1, 2), ('best_bid',), ('cancel', 1), ('best_bid',), ('best_ask',)],)

Expected Output: [(100, 2), (99, 8), None]

Explanation: Order 1 is reduced from 5 to 2, so the best bid stays at price 100 with quantity 2. After canceling order 1, the next best BUY level is 99 with quantity 8. There are no SELL orders.

Input: ([],)

Expected Output: []

Explanation: No operations means no query results.

Input: ([('add', 1, 'BUY', 100, 3), ('add', 2, 'BUY', 100, 4), ('add', 3, 'BUY', 99, 10), ('best_bid',), ('add', 4, 'SELL', 105, 2), ('add', 5, 'SELL', 105, 3), ('best_ask',), ('modify', 1, 1), ('modify', 4, 0), ('top_of_book',)],)

Expected Output: [(100, 7), (105, 5), ((100, 5), (105, 3))]

Explanation: BUY orders at price 100 aggregate to 7. SELL orders at 105 aggregate to 5. After modifying order 1 from 3 to 1 and canceling order 4 via modify(..., 0), the top of book becomes BUY (100, 5) and SELL (105, 3).

Input: ([('cancel', 99), ('modify', 42, 5), ('add', 1, 'SELL', 101, 2), ('add', 2, 'SELL', 100, 1), ('add', 3, 'SELL', 100, 4), ('best_ask',), ('cancel', 2), ('best_ask',), ('cancel', 3), ('best_ask',), ('modify', 1, 0), ('best_ask',)],)

Expected Output: [(100, 5), (100, 4), (101, 2), None]

Explanation: Missing cancel/modify calls do nothing. SELL price 100 starts with total 5, then drops to 4 after canceling one order, then disappears entirely after canceling the remaining order at 100, revealing 101 as the next best ask. Finally, modifying the last order to quantity 0 removes it.

Hints

  1. Use a hash map from order_id to the order's current side, price, quantity, and insertion timestamp so cancel and modify are fast.
  2. Track total quantity at each price level, and use a min-heap for asks plus a max-heap for bids (store negative prices). Lazy deletion is useful when a price level becomes empty.
Last updated: Apr 28, 2026

Loading coding console...

PracHub

Master your tech interviews with 7,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Implement a single-producer multi-consumer ring buffer - Citadel (medium)
  • Sort a Nearly Sorted Array - Citadel (hard)
  • Compute BBO and NBBO from order data - Citadel (medium)
  • Design dynamic weighted random sampling with updates - Citadel (medium)
  • Compute maximum later-earlier difference - Citadel (medium)