Quant developer HFT course · Module 9: Trading systems
The order gateway and pre-trade risk checks
The question
Every order must pass risk checks before it leaves. Which checks would you implement, where do they live, and how do you keep them from adding latency?
The outbound path
Between a strategy's decision and the network card, an order passes through:
- Pre-trade risk checks.
- Order state: assign a client id, record the order as pending, update open-order totals.
- Encoding into the exchange's message format.
- Session handling: sequence numbers, throttles, heartbeats, the connection itself.
Incoming acknowledgements, fills and rejects come back through the session, are decoded, update the order state and positions, and are passed to the strategy.
The checks
Firms that trade directly on exchanges are required by regulators in most major markets to apply pre-trade controls before orders reach the market, and exchanges impose their own. The usual set:
- Price collars: reject a price too far from a reference, such as the last trade or the mid, by more than a set number of ticks or a percentage. Catches a misplaced decimal.
- Maximum order size: per order, in quantity and in notional value (price times quantity times contract multiplier).
- Position limits: the position after the order, counting filled quantity and all open orders that could still fill, must stay within limits per instrument and in aggregate.
- Credit and exposure limits across the account.
- Order rate limits: orders per second per instrument and per session, both the firm's own and the exchange's (exceeding exchange limits gets messages rejected or the session disconnected).
- Duplicate and runaway detection: the same order repeated many times in a short interval, or a strategy sending orders in a loop.
- Restricted instruments and trading hours.
- Self-trade checks: an order that would trade against the firm's own resting order.
And a kill switch: a way to stop all new orders and cancel all open orders immediately, triggered by an operator, by a monitoring rule (losses beyond a threshold, an unusual fill rate), or by losing contact with the risk system.
Keeping them fast
Pre-trade checks sit on the critical path, so they are built like the rest of the hot path:
- Precomputed limits per instrument in a flat array indexed by the dense instrument id: no lookups by symbol, no locks.
- Integer arithmetic in ticks and lots; notional limits precomputed as quantity limits per price range where possible.
- Positions maintained incrementally on every fill and every order sent or cancelled, so a check reads a number, never sums.
- Branch layout for the common case: the order passes. Failures are rare and handled out of line.
- Rate limits as token buckets or sliding windows of fixed size, updated with a timestamp per order.
- Limits updated by another thread through a seqlock or an atomic swap of an immutable limits table, never by locking the hot path.
Done this way, the full set of checks costs well under a microsecond. Some firms run a second, independent set of checks in hardware on the network card, so a software bug cannot bypass them.
The session
Exchange sessions add their own obligations: login, sequence numbers on every message in both directions, heartbeats when idle, resend requests after gaps, and graceful handling of disconnects. While disconnected, open orders may still trade; the gateway must mark their state unknown and, on reconnect, recover missed messages before trusting its view. Many exchanges offer cancel on disconnect, cancelling a session's orders automatically if the connection drops, which most firms enable.
Check yourself
Question 1 of 3
Which check catches an order priced at 1000.25 instead of 100.025 because of a misplaced decimal?
Collars reject prices far from the current market, which is exactly what a misplaced decimal produces.
Question 2 of 3
When checking a position limit for a new buy order, which quantity should be counted?
Open orders can fill at any moment, so the worst case includes them.
Question 3 of 3
How should risk limits be updated during the day without slowing order checks?
The checking thread reads the current table without blocking; updates appear atomically.
In the interview
Say your own answer out loud first, then open the one we would give.
What pre-trade risk checks would you implement?
Price collars against a reference, maximum order size and notional, position limits counting open orders, credit or exposure limits, order rate limits including the exchange's, duplicate and runaway detection, restricted instruments and trading hours, and self-trade prevention. Plus a kill switch that blocks new orders and cancels everything.
How do you make risk checks cheap?
Keep limits and positions in flat arrays indexed by dense instrument id, maintained incrementally on every order and fill; use integer ticks and lots; lay out branches for the passing case; implement rate limits with fixed-size token buckets; and publish limit changes from another thread via atomic swap or seqlock. The checks then cost well under a microsecond.
What happens to orders if the exchange connection drops?
They may still trade. The gateway marks them uncertain and treats them as potentially filled for risk; on reconnect, the session's resend mechanism recovers missed acknowledgements and fills before the state is trusted. Cancel on disconnect, if enabled, removes them at the exchange.