FastPrepPro-Rata Order Book

Pro-Rata Order Book

IMC Trading logoIMC Trading● HardNEW GRADONSITE INTERVIEW
Learn

Problem statement

Maintain a finite two-sided limit-order book for one asset and execute immediate-or-cancel aggressor requests using pro-rata allocation. Each operation has one of these forms:

  • ["ADD", side, orderId, price, quantity]
  • ["CANCEL", orderId]
  • ["MATCH", side, limitPrice, quantity]

An ADD operation places a resting order at the back of its price level. A CANCEL operation removes the unfilled remainder of the named resting order; an unknown, filled, or already cancelled ID is a no-op.

A MATCH operation is an incoming immediate-or-cancel request. A BUY request visits sell prices from lowest to highest while the price is at most its limit. A SELL request visits buy prices from highest to lowest while the price is at least its limit. Any unmatched aggressor quantity is discarded rather than added to the book.

At one visited price level, let need be the smaller of the remaining aggressor quantity and the total resting quantity at that level. For each resting order with quantity q, first assign floor(need * q / total). Assign any leftover units one at a time in arrival order to orders that still have unfilled quantity. Emit positive fills in arrival order, reduce the resting quantities, and remove fully filled orders. A partially filled resting order keeps its original position.

Return every fill in execution order as "restingOrderId|price|quantity". ADD and CANCEL operations produce no output.

Function

executeProRata(operations: String[][]) → String[]

Examples

Example 1

operations = [["ADD","SELL","s1","100","6"],["ADD","SELL","s2","100","3"],["ADD","SELL","s3","100","1"],["MATCH","BUY","100","5"],["MATCH","BUY","100","3"]]return = ["s1|100|4","s2|100|1","s1|100|2","s2|100|1"]

The first request allocates base quantities [3,1,0] and gives the leftover unit to s1. The remaining quantities are [2,2,1]. The second request allocates base quantities [1,1,0] and again gives the leftover unit to the earliest eligible order, s1.

Example 2

operations = [["ADD","SELL","s1","99","2"],["ADD","SELL","s2","100","4"],["ADD","SELL","s3","100","2"],["MATCH","BUY","100","5"]]return = ["s1|99|2","s2|100|2","s3|100|1"]

The request consumes all two units at the better sell price 99. At price 100, the remaining three units split proportionally as two for s2 and one for s3.

Example 3

operations = [["ADD","BUY","b1","101","5"],["ADD","BUY","b2","101","5"],["ADD","BUY","b3","100","8"],["CANCEL","b1"],["MATCH","SELL","100","6"]]return = ["b2|101|5","b3|100|1"]

Cancellation removes b1. The sell request first fills all five units of b2 at the best buy price, then fills one unit of b3 at the next eligible price.

Constraints

  • 1 <= operations.length <= 50000
  • Every operation has one of the three forms described above.
  • side is BUY or SELL.
  • orderId contains only ASCII letters, digits, hyphens, and underscores, has length from 1 through 40, and does not contain |.
  • Every ADD order ID is unique and is never reused.
  • 1 <= price, quantity <= 10^6, written as base-10 integers.
  • The total resting quantity at one price level never exceeds 10^9.
  • Across all MATCH operations, at most 200000 resting orders are inspected and at most 200000 positive fills are emitted.

More IMC Trading problems

See IMC Trading hiring insights
public String[] executeProRata(String[][] operations) {
    // Write your code here.
}
operations[["ADD","SELL","s1","100","6"],["ADD","SELL","s2","100","3"],["ADD","SELL","s3","100","1"],["MATCH","BUY","100","5"],["MATCH","BUY","100","3"]]
expected["s1|100|4", "s2|100|1", "s1|100|2", "s2|100|1"]
Checking account…