Matching Engine with Order Cancellation

Reported by candidates from IMC Trading's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.

Get StealthCoderRuns invisibly during the live IMC Trading OA. Under 2s to a working solution.
Founder's read

IMC Trading reportedly asked this one in June 2026, and it looks like a simulation problem until you strip the trading jargon. It's an order book: two priority queues keyed by price, with FIFO queues inside each price level, plus a way to kill orders by ID. If your OA invite is sitting in your inbox, that's the whole shape. The risk isn't the idea, it's the bookkeeping under 100000 operations. If you blank on the structure mid-assessment, StealthCoder runs invisibly as a safety net and gives you the working approach in real time.

The problem

Process a finite sequence of limit-order operations for one asset. Each operation has one of these forms:
["BUY", orderId, price, quantity]
["SELL", orderId, price, quantity]
["CANCEL", orderId]
Process operations in input order. A new buy may trade with resting sells whose prices are at most its limit, while a new sell may trade with resting buys whose prices are at least its limit.
Always match the best available price first: the lowest sell price for a buy, or the highest buy price for a sell. Within one price level, resting orders match in arrival order. Each trade uses the resting order's price and the largest possible quantity between the incoming and resting orders. A partially filled incoming order keeps matching; any remainder rests at the back of its price level.
A CANCEL operation removes the unfilled remainder of the named resting order. Cancelling an unknown, already filled, or already cancelled order has no effect. Order IDs from BUY and SELL operations are globally unique and are never reused.
Return every trade in execution order as "buyOrderId|sellOrderId|price|quantity". Cancellations and submissions that do not trade produce no output.

Function
processOrders(operations: String[][]) → String[]

Examples
Example 1
operations = [["BUY","b1","100","5"],["SELL","s1","90","2"],["CANCEL","b1"],["SELL","s2","95","4"],["BUY","b2","100","4"]]
return = ["b1|s1|100|2","b2|s2|95|4"]
Sell s1 trades two units with resting buy b1. Cancelling b1 removes its remaining three units, so s2 rests until b2 arrives.
Example 2
operations = [["SELL","s1","103","3"],["SELL","s2","103","2"],["BUY","b1","105","4"],["CANCEL","s2"],["BUY","b2","104","2"],["SELL","s3","104","1"]]
return = ["b1|s1|103|3","b1|s2|103|1","b2|s3|104|1"]
At price 103, FIFO priority fills s1 before s2. The cancellation removes the last unit of s2. Later, resting buy b2 trades one unit with s3 at b2's resting price.
Example 3
operations = [["BUY","b1","100","2"],["SELL","s1","100","2"],["CANCEL","b1"],["CANCEL","missing"],["SELL","s2","99","1"],["BUY","b2","99","1"]]
return = ["b1|s1|100|2","b2|s2|99|1"]
Order b1 is already fully filled when it is cancelled, and missing never existed, so both cancellations are no-ops.

Constraints
1 <= operations.length <= 100000
Every operation has one of the three forms described above.
orderId contains only ASCII letters, digits, hyphens, and underscores, has length from 1 through 40, and does not contain |.
Every BUY or SELL order ID is unique and is never reused.
1 <= price, quantity <= 10^9, written as base-10 integers.
The number of generated trades and every remaining quantity fit in memory and signed 64-bit arithmetic.

Reported by candidates. Source: FastPrep

Pattern and pitfall

Reduce it to this: a min-heap of sell prices, a max-heap of buy prices, and a map from price to a deque of resting orders. Incoming buy: while the best ask is at or below your limit and you have quantity left, match against the front of that level's deque. Trade at the resting order's price, for min of both quantities. The trick is cancellation. Don't search deques. Store each order's remaining quantity in a map by ID and mark cancels lazily. When you reach the front of a level, skip orders with zero remaining. Pitfalls: using the incoming price instead of the resting price, forgetting to rest the remainder at the back of its level, and leaving empty price levels in the heap. Pop a price when its deque drains. Also parse the string quantities as 64-bit integers. StealthCoder is your hedge if the lazy deletion detail slips under the clock.

If you see this problem in your OA tomorrow, the play is to recognize the pattern in 30 seconds. StealthCoder buys you that recognition.

If this hits your live OA

You can drill Matching Engine with Order Cancellation cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken.

Get StealthCoder

Related leaked OAs

⏵ The honest play

You've seen the question. Make sure you actually pass IMC Trading's OA.

IMC Trading reuses patterns across OAs. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken. Works on HackerRank, CodeSignal, CoderPad, and Karat.

Matching Engine with Order Cancellation FAQ

How hard is the IMC Trading matching engine problem really?+

Medium on ideas, medium-hard on implementation. There's no clever algorithm, but you juggle two heaps, FIFO deques, and cancellations. Most failures come from small bookkeeping bugs, not from not knowing the approach. Write it in clean pieces so each rule is easy to check.

What's the trick for handling CANCEL efficiently?+

Use lazy deletion. Keep a map from orderId to its remaining quantity and set it to zero on cancel. When matching reaches an order with zero remaining, skip and pop it. This avoids scanning a deque and keeps each operation near logarithmic.

Which price does a trade execute at?+

The resting order's price, always. Example 2 shows it: b2 rests at 104 and trades with incoming s3 at 104. In example 1, incoming s2 at 95 trades with resting b2? No, b2 arrives later and trades at the resting sell's price of 95.

Do I need a heap, or is sorting enough?+

Sorting once won't work because orders arrive and rest dynamically. You need a structure that gives the best price repeatedly as levels appear and vanish. Use heaps of distinct prices, or a sorted map, with a deque per price level.

How do I prepare for this in 48 hours?+

Code the order book from scratch twice and run all three examples by hand. Focus on edge cases: partial fills, cancelling a filled order, unknown IDs, and levels that empty out. Practice writing the output string format exactly: buyId|sellId|price|quantity.

Problem reported by candidates from a real Online Assessment. Sourced from a publicly-available candidate-aggregated repository. Not affiliated with IMC Trading.

OA at IMC Trading?
Invisible during screen share
Get it