Query Order and Batch Lifetimes from Event Files
Hudson River TradingSoftware Engineer · Technical ScreenLast updated
Design an event-driven tracker to query lifetimes for individual orders or order batches, correctly handling open orders and aggregate start/end timestamps.
Problem Statement
In trading systems, orders are often submitted individually or grouped into logical execution batches. You are given a stream of chronological order lifecycle events from an event log. Each event records a timestamp, an action ("CREATE" or "CLOSE"), an order_id, and an optional batch_id (present on "CREATE").
Implement the OrderLifetimeTracker class with the following methods:
process_event(timestamp: int, action: str, order_id: str, batch_id: str | None = None) -> None: Ingests an event. Actions are either"CREATE"(which opens the order attimestampand associates it withbatch_idif provided) or"CLOSE"(which marks the order as finished attimestamp).get_lifetime(identifier: str) -> tuple[int, int] | None: Queries the lifetime(start_time, end_time)for a givenidentifier:- If
identifieris anorder_id:start_timeis the timestamp when the order was created.end_timeis the timestamp when the order closed, or0if it is still open.
- If
identifieris abatch_id:start_timeis the earliest creation timestamp among all orders in the batch.end_timeis the latest close timestamp among all orders in the batch. However, if any order in the batch is still open,end_timemust be0.
- If
identifiermatches neither an existing order nor a batch, returnNone.
- If
You may assume all order_id and batch_id values come from disjoint sets (no ID is used as both an order and a batch).
Interface
class OrderLifetimeTracker:
def __init__(self):
pass
def process_event(self, timestamp: int, action: str, order_id: str, batch_id: str | None = None) -> None:
pass
def get_lifetime(self, identifier: str) -> tuple[int, int] | None:
pass
Constraints
- Timestamps are positive integers strictly increasing or non-decreasing:
1 <= timestamp <= 10^9. - Each
order_idis created at most once and closed at most once. - An order is never closed before it is created.
- Total number of events and queries
N <= 200,000. get_lifetimeshould execute inO(1)time.
Example
tracker = OrderLifetimeTracker()
# Order 1 and Order 2 belong to Batch B1
tracker.process_event(100, "CREATE", "O1", "B1")
tracker.process_event(105, "CREATE", "O2", "B1")
# Order 3 is standalone (no batch)
tracker.process_event(110, "CREATE", "O3", None)
# Close O1
tracker.process_event(150, "CLOSE", "O1")
tracker.get_lifetime("O1") # returns (100, 150)
tracker.get_lifetime("O2") # returns (105, 0) -> still open
tracker.get_lifetime("B1") # returns (100, 0) -> O2 is still open, so end is 0
tracker.get_lifetime("O3") # returns (110, 0)
# Close O2
tracker.process_event(180, "CLOSE", "O2")
tracker.get_lifetime("B1") # returns (100, 180) -> all orders closed
tracker.get_lifetime("UNKNOWN") # returns None
Hints
Hint 1
Maintain state for each order (start time, end time, batch reference) and aggregate metadata for each batch (min start time, max end time, count of total orders, count of open orders).
Hint 2
When an order closes, decrement the batch's open order count and update its maximum end timestamp.
Hint 3
Ensure `get_lifetime` can check whether an ID belongs to an order or a batch in O(1) time without re-scanning all orders in the batch.
Follow-up questions
- How would you handle out-of-order event delivery (e.g., CLOSE arrives before CREATE over network partitions)?
- What changes would be required if `batch_id` namespaces and `order_id` namespaces overlapped?
- If memory is constrained and there are millions of batches, how would you design an eviction or compaction strategy for fully closed batches?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.