In-Memory Order Book with Price-Time Order, Level Aggregation and Order Cancel

Read the full interview experience this question came from →

Quick Overview

Implement an in-memory order book that keeps bids and asks in price-time order, maintains the aggregated quantity at each price level, and cancels a single order by id without disturbing any other order. Tests container choice, per-operation complexity, handle stability and memory allocation in C++.

In-Memory Order Book with Price-Time Order, Level Aggregation and Order Cancel

Company: Citadel Securities

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

Implement an in-memory order book for a single instrument. Orders arrive one at a time, each with a unique order id, a side (bid or ask), a price and a quantity. The book must: - keep the orders at each price in the order in which they arrived; - maintain the aggregated quantity resting at each price level on each side; - cancel any individual order by its id without disturbing any other order, including the arrival order of the orders around it. Treat the interface below as a starting point to confirm with the interviewer: ```cpp enum class Side { Bid, Ask }; class OrderBook { public: bool add(uint64_t order_id, Side side, int64_t price, uint64_t qty); bool cancel(uint64_t order_id); uint64_t level_quantity(Side side, int64_t price) const; // 0 if the level is empty std::vector<std::pair<int64_t, uint64_t>> depth(Side side, std::size_t levels) const; // best price first }; ``` The discussion is in C++ and goes into container choice, how the standard containers are implemented, and where memory is allocated. ### Clarifying Questions - Should an incoming order that crosses the spread (a bid at or above the best ask) trade against resting orders, or does the book only store orders? - Can an order's quantity be changed after it is added, and does a change lose its place in line? - How are prices represented: integer ticks or decimals? - What should `add` do with a duplicate id or a zero quantity, and what should `cancel` do with an unknown id? - How many price levels and resting orders should the book handle, and are there latency targets per operation? ### Part 1 — Add orders and aggregate levels Implement `add`, `level_quantity` and `depth`. Orders at the same price keep their arrival order, each side is ordered from its best price, and aggregated quantities are available without delay. ```hint Two orderings Orders need one ordering across prices and another within a price. Choose a container for each, and decide where the aggregated quantity lives so that a query does not have to walk every order. ``` #### What This Part Should Cover - Containers for price order and arrival order, with the complexity of each operation. - Incremental maintenance of each level's aggregated quantity. - A correct best-first order for bids and for asks. ### Part 2 — Cancel an order Implement `cancel`. It receives only an order id; it must remove that order, update the aggregate, and leave every other order exactly where it was. ```hint Find it without searching A cancel names only the order id. Think about what you would need to record at add time so that a cancel reaches the order directly and removes it without shifting its neighbors. ``` #### What This Part Should Cover - An index from order id to the order, and which handles stay valid as other orders come and go. - Removal without moving or invalidating other orders, and clean-up of levels that become empty. - The cost of cancel compared with the cost of add. ### What a Strong Answer Covers - Per-operation complexity for add, cancel, level queries and depth. - Invariants that hold after every operation: level totals, no empty levels, and a consistent id index. - Where memory is allocated on each operation, and how to reduce allocation and improve cache locality. - Input validation and behavior for duplicate or unknown ids. ### Follow-up Questions - Add matching: how does an aggressive order consume resting liquidity in price-time priority with your structures? - Where does your design allocate memory on each operation, and how would you take allocation off the hot path? - If prices always fall within a narrow band of ticks, what could replace the ordered map, and how would you track the best price? - How would you support reducing an order's quantity without losing its place in line?

Overview: Implement an in-memory order book that keeps bids and asks in price-time order, maintains the aggregated quantity at each price level, and cancels a single order by id without disturbing any other order. Tests container choice, per-operation complexity, handle stability and memory allocation in C++.

Read the full Citadel Securities Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Citadel Securities
Citadel Securities logo
Citadel Securities
Sep 11, 2026
mediumSoftware EngineerOnsiteSoftware Engineering Fundamentals
0
0

Implement an in-memory order book for a single instrument. Orders arrive one at a time, each with a unique order id, a side (bid or ask), a price and a quantity. The book must:

  • keep the orders at each price in the order in which they arrived;
  • maintain the aggregated quantity resting at each price level on each side;
  • cancel any individual order by its id without disturbing any other order, including the arrival order of the orders around it.

Treat the interface below as a starting point to confirm with the interviewer:

enum class Side { Bid, Ask };

class OrderBook {
public:
    bool add(uint64_t order_id, Side side, int64_t price, uint64_t qty);
    bool cancel(uint64_t order_id);
    uint64_t level_quantity(Side side, int64_t price) const;   // 0 if the level is empty
    std::vector<std::pair<int64_t, uint64_t>> depth(Side side, std::size_t levels) const;   // best price first
};

The discussion is in C++ and goes into container choice, how the standard containers are implemented, and where memory is allocated.

Clarifying Questions Guidance

  • Should an incoming order that crosses the spread (a bid at or above the best ask) trade against resting orders, or does the book only store orders?
  • Can an order's quantity be changed after it is added, and does a change lose its place in line?
  • How are prices represented: integer ticks or decimals?
  • What should add do with a duplicate id or a zero quantity, and what should cancel do with an unknown id?
  • How many price levels and resting orders should the book handle, and are there latency targets per operation?

Part 1 — Add orders and aggregate levels

Implement add, level_quantity and depth. Orders at the same price keep their arrival order, each side is ordered from its best price, and aggregated quantities are available without delay.

What This Part Should Cover Guidance

  • Containers for price order and arrival order, with the complexity of each operation.
  • Incremental maintenance of each level's aggregated quantity.
  • A correct best-first order for bids and for asks.

Part 2 — Cancel an order

Implement cancel. It receives only an order id; it must remove that order, update the aggregate, and leave every other order exactly where it was.

What This Part Should Cover Guidance

  • An index from order id to the order, and which handles stay valid as other orders come and go.
  • Removal without moving or invalidating other orders, and clean-up of levels that become empty.
  • The cost of cancel compared with the cost of add.

What a Strong Answer Covers Guidance

  • Per-operation complexity for add, cancel, level queries and depth.
  • Invariants that hold after every operation: level totals, no empty levels, and a consistent id index.
  • Where memory is allocated on each operation, and how to reduce allocation and improve cache locality.
  • Input validation and behavior for duplicate or unknown ids.

Follow-up Questions Guidance

  • Add matching: how does an aggressive order consume resting liquidity in price-time priority with your structures?
  • Where does your design allocate memory on each operation, and how would you take allocation off the hot path?
  • If prices always fall within a narrow band of ticks, what could replace the ordered map, and how would you track the best price?
  • How would you support reducing an order's quantity without losing its place in line?
Loading comments...