Order Book: Matching Engine
HardAI-assisted coding · AI-Assisted Feature Work
Order Book: Matching Engine
You've joined the team that owns the matching engine for a small exchange: the component that accepts buy and sell orders for one instrument, pairs them up and keeps the resting book. Traders have reported two bugs in the matching itself, and the roadmap has three pieces of work behind them. Prices are integer ticks and quantities are integer units.
Codebase
| File | What it does |
|---|---|
exchange/api.py | run(operations) entry point and the Dispatcher that routes each operation |
exchange/engine.py | MatchingEngine: order entry, matching, order registry |
exchange/book.py | OrderBook: the resting orders on each side and their priority |
exchange/ladder.py | Expands a ladder request into individual orders |
exchange/queries.py | Read-only views: order status, depth, best prices |
exchange/validation.py | Field validation for order entry |
exchange/models.py | The Order dataclass and the side / state vocabulary |
exchange/errors.py | ExchangeError and every error code |
exchange/config.py | Constants |
tests/ | Existing tests (some of them currently fail) |
How your code is run
run(operations: list[list]) -> list executes the operations in order against an empty book and returns one result per operation. Every operation is [name, ...args]. Errors are returned as "error: <code>" strings, never raised.
| Operation | Arguments | Returns | Errors (checked in this order) |
|---|---|---|---|
limit | order_id, trader, side, price, qty | {"fills": [[maker_id, price, qty], ...], "rested": n} | invalid order, duplicate order |
ladder | trader, side, prefix, price, step, levels, per_level, qty | {"placed": n, "traded": q} | invalid order, duplicate order |
status | order_id | {"state": ..., "filled": n, "remaining": n} | unknown order |
depth | side, levels | list of [price, total_remaining], best price first | invalid arguments |
best | none | {"bid": price or null, "ask": price or null} | none |
cancel | order_id | quantity that was cancelled | unknown order, order not open (Part 2) |
amend | order_id, new_price, new_qty | same as limit | invalid order, unknown order, order not open (Part 2) |
market | order_id, trader, side, qty | {"fills": [...], "rested": 0, "unfilled": n} | invalid order, duplicate order (Part 3) |
- An unknown operation name returns
"error: unknown operation"; a wrong number of arguments returns"error: invalid arguments".cancel,amendandmarketcurrently answer"error: not supported". sideis"buy"or"sell".order_idandtraderare non-blank, case-sensitive strings;priceandqtyare integers>= 1. Anything else isinvalid order. Order ids are unique across every order ever accepted, including filled and cancelled ones.- Matching. An incoming order trades against the opposite side while it crosses: a buy crosses sells priced at or below its price, a sell crosses buys priced at or above its price. Resting orders are served best price first (lowest ask, highest bid) and, within one price, in the order they arrived (price-time priority). Each trade happens at the resting order's price and is reported as
[maker_id, price, qty], in the order the trades happen. Whatever is left of a limit order is added to the book (restedis that quantity;0if it fully traded). - A resting order that is partly filled keeps its place in line with its smaller remaining quantity.
status.stateis"open"(still has quantity working),"filled"(fully traded) or"cancelled".filledis the total quantity traded;remainingis the quantity still working (0once filled or cancelled).depthlists at mostlevelsprice levels of one side, each with the total remaining quantity of the open orders at that price.sidemust be a side andlevelsan integer>= 1.ladderis a convenience for market makers. It placeslevels * per_levellimit orders, each of sizeqty, exactly as if thelimitoperations were sent one after another in this order: level 0 first, then level 1, and so on, withper_levelorders per level. Orderkof levelihas id"<prefix>-<i>-<k>"and tradertrader. A buy ladder's prices step down fromprice(price - i * step), a sell ladder's step up (price + i * step).tradedis the total quantity its orders traded on arrival.price, step, levels, per_level, qtymust all be integers>= 1,prefixandtradernon-blank, the lowest price must still be>= 1, andlevels * per_levelat most 20,000; otherwiseinvalid order. If any generated id already exists, nothing is placed and the result isduplicate order.
Worked example
[["limit", "s1", "alice", "sell", 101, 5],
["limit", "s2", "bob", "sell", 101, 3],
["limit", "s3", "carol", "sell", 102, 4],
["limit", "b1", "dave", "buy", 102, 6],
["depth", "sell", 5],
["best"],
["status", "s2"]]returns
[{"fills": [], "rested": 5},
{"fills": [], "rested": 3},
{"fills": [], "rested": 4},
{"fills": [["s1", 101, 5], ["s2", 101, 1]], "rested": 0},
[[101, 2], [102, 4]],
{"bid": null, "ask": 101},
{"state": "open", "filled": 1, "remaining": 2}]b1 is willing to pay up to 102 but trades at the resting price, 101. s1 arrived before s2, so it is filled first.
Part 1 — Matching is wrong
Goal: make matching follow the rules above.
Traders have reported:
- "When two orders sit at the same price, the one that arrived last gets filled first."
- "My buy at 102 traded against an ask at 101 and I was charged 102."
Rules
- Within a price level, earlier arrivals are filled first on both sides of the book.
- Every trade happens at the resting order's price, for buys and sells alike.
Part 2 — Cancels and amends
Goal: let traders cancel and change orders that are resting in the book.
Rules
cancel(order_id)removes an open order from the book (which may already be partly filled) and returns the quantity that was still working. Afterwards itsstatusis"cancelled"with itsfilledquantity unchanged andremaining0.depthandbestno longer include it, and the orders behind it move up. Errors:unknown order;order not openfor orders that are already filled or cancelled.amend(order_id, new_price, new_qty)changes an open order.new_qtyis the new *remaining* quantity (what is still working), not the original size; the order'sfilledquantity is kept. Checks are in this order:invalid order(new_priceandnew_qtymust be integers>= 1),unknown order,order not open.- If
new_priceequals the current price andnew_qtyis at most the current remaining quantity, the order keeps its place in line, now withnew_qtyremaining. The result is{"fills": [], "rested": new_qty}. - Any other amend (a different price, or more quantity than is currently remaining) takes the order off the book and re-enters it as a new arrival at the back of the line with the new price and
new_qty. If it now crosses the other side it trades immediately, exactly like a new limit order. The result has the same shape aslimit: the fills and the quantity that rested. - An order can never trade against itself, and amends never change the order's id or trader.
Example
[["limit", "s1", "alice", "sell", 101, 5],
["limit", "s2", "bob", "sell", 101, 3],
["amend", "s1", 101, 2],
["amend", "s1", 101, 4],
["limit", "b1", "carol", "buy", 101, 4],
["cancel", "s1"],
["status", "s1"]]returns
[{"fills": [], "rested": 5},
{"fills": [], "rested": 3},
{"fills": [], "rested": 2},
{"fills": [], "rested": 4},
{"fills": [["s2", 101, 3], ["s1", 101, 1]], "rested": 0},
3,
{"state": "cancelled", "filled": 1, "remaining": 0}](The first amend only shrinks s1, so it stays first in line. The second raises it back to 4, which sends it behind s2.)
Part 3 — Market orders and self-trade prevention
Goal: support orders that take whatever liquidity is available, and stop traders trading with themselves.
Rules
market(order_id, trader, side, qty)trades against the opposite side from the best price outward with no price limit, using the same priority and trade-price rules as limit orders. Whatever cannot be filled is not added to the book: the result is{"fills": [...], "rested": 0, "unfilled": n}withnthe quantity that found no counterparty. Validation and errors are those oflimitwithout the price. The order'sstatusis"filled"if it fully traded and"cancelled"otherwise (itsfilledquantity is what did trade;remainingis0).- Self-trade prevention. When an incoming order (limit, market, ladder order or re-entered amend) reaches a resting order with the same
traderthat it would otherwise trade with, no trade happens. The resting order is cancelled instead (status"cancelled", nothing reported infills) and matching continues with the next resting order. Resting orders of the same trader that the incoming order does not cross are left alone. - Rules 1 and 2 apply on both sides, and ladder orders are subject to them one by one, in order.
- Parts 1 and 2 behaviour is unchanged.
Example
[["limit", "s1", "alice", "sell", 100, 5],
["limit", "s2", "bob", "sell", 100, 5],
["limit", "b1", "alice", "buy", 100, 7],
["status", "s1"],
["market", "m1", "carol", "sell", 10],
["status", "m1"]]returns
[{"fills": [], "rested": 5},
{"fills": [], "rested": 5},
{"fills": [["s2", 100, 5]], "rested": 2},
{"state": "cancelled", "filled": 0, "remaining": 0},
{"fills": [["b1", 100, 2]], "rested": 0, "unfilled": 8},
{"state": "cancelled", "filled": 2, "remaining": 0}](Alice's own sell s1 is cancelled instead of trading with her buy; the buy then trades with Bob and the remaining 2 rests. Carol's market sell finds only those 2 units.)
Part 4 — Order flow at scale (stretch)
Goal: keep every result above identical, but make the engine handle real market-maker traffic.
Production books hold tens of thousands of resting orders, and market makers send ladders with thousands of orders at a time. Hidden tests send ladders of several thousand orders that rest, cross each other and get swept, interleaved with depth, best, cancel and amend operations on a deep book. Each of these tests needs to finish in about a second.
Notes
validation.py,ladder.py,queries.pyandmodels.pyare known-good for Parts 1 to 3; the Part 1 bugs are in the matching and the book.- You may use the AI assistant freely. Later parts must not break earlier ones: hidden tests re-check earlier behaviour.
- Parts 1 to 3 are a strong result; Part 4 is a stretch.
Example 1
- Input:
- [["limit","s1","alice","sell",101,5],["limit","s2","bob","sell",101,3],["limit","s3","carol","sell",102,4],["limit","b1","dave","buy",102,6],["depth","sell",5],["best"],["status","s2"]]
- Output:
- [{"fills":[],"rested":5},{"fills":[],"rested":3},{"fills":[],"rested":4},{"fills":[["s1",101,5],["s2",101,1]],"rested":0},[[101,2],[102,4]],{"bid":null,"ask":101},{"state":"open","filled":1,"remaining":2}]
- Explanation
- The buy is willing to pay 102 but trades at the resting price 101. s1 arrived before s2, so it is filled first.
Example 2
- Input:
- [sell s1 101x5, sell s2 101x3, ["amend","s1",101,2], ["amend","s1",101,4], buy b1 101x4, ["cancel","s1"], ["status","s1"]]
- Output:
- [..., {"fills":[],"rested":2},{"fills":[],"rested":4},{"fills":[["s2",101,3],["s1",101,1]],"rested":0},3,{"state":"cancelled","filled":1,"remaining":0}]
- Explanation
- Shrinking s1 keeps its place; raising it back to 4 sends it behind s2. After trading 1 unit, cancelling returns the 3 units still working.
Example 3
- Input:
- [sell s1 100x5 alice, sell s2 100x5 bob, buy b1 100x7 alice, ["status","s1"], market m1 carol sell 10]
- Output:
- [..., {"fills":[["s2",100,5]],"rested":2},{"state":"cancelled","filled":0,"remaining":0},{"fills":[["b1",100,2]],"rested":0,"unfilled":8}]
- Explanation
- Alice's buy would trade with her own sell s1, so s1 is cancelled instead and matching continues with Bob's order. Carol's market sell finds only the 2 units of b1 that rested.
Guidelines
- Python 3 standard library only
- Prices are integer ticks and quantities are integer units
- Every operation returns exactly one value; errors are returned as "error: <code>" strings, never raised
- Priority is price first, then arrival order; trades happen at the resting order's price
- Part 4 inputs rest and sweep tens of thousands of orders through ladders