Appearance

Personalize your experience

Mode
Accent Color
Layout
Direction
All articles

Risk Controls for an Automated Book

Five layers that have to exist before a strategy touches real money, and why the kill switch belongs outside the strategy that it kills.

QuaTick Risk Desk9 min read1,811 words
PostLinkedIn
On this page

A manual trader has an implicit risk system: they get uncomfortable and stop. An automated one has no such reflex, and will cheerfully execute the four hundredth iteration of a losing rule at 14:58 on a day when the assumption underneath it stopped holding at 09:30. Every control that a discretionary trader supplies with judgement has to be written down.

Layer 1 — Position limits

Layered model

Five independent layers of defence

Controls overlap deliberately: each catches a different failure and remains useful when another component is wrong.

Pre-trade position limits prevent oversized exposure. Mark-to-market loss limits flatten a losing book. Order-rate limits contain runaway loops. An external kill switch removes order access and closes positions without strategy cooperation. Reconciliation compares system, fills and broker truth and stops trading on any mismatch.
The layers should not share one failure domain. In particular, the kill switch and broker reconciliation must survive a strategy process crash.

The maximum quantity the system may hold, per instrument and in aggregate, expressed in lots and checked before every order. This is the least interesting control and it prevents the most spectacular failures, because the classic runaway bug is not a bad signal — it is a loop that fires the same order repeatedly.

limits.pypython
class PositionLimitBreach(Exception):    pass  class PreTradeGate:    """Sits between strategy intent and the order manager. Every order    passes through here, including the ones the strategy is certain about."""     def __init__(self, per_instrument: dict[str, int], gross_lots: int):        self.per_instrument = per_instrument        self.gross_lots = gross_lots     def check(self, symbol: str, delta_lots: int, book: dict[str, int]) -> None:        projected = book.get(symbol, 0) + delta_lots         # Absolute value, because a runaway short is as dangerous as a        # runaway long and a naive >= check only catches one of them.        cap = self.per_instrument.get(symbol, 0)        if abs(projected) > cap:            raise PositionLimitBreach(f"{symbol}: {projected} lots exceeds cap {cap}")         gross = sum(abs(v) for k, v in book.items() if k != symbol) + abs(projected)        if gross > self.gross_lots:            raise PositionLimitBreach(f"gross {gross} lots exceeds cap {self.gross_lots}")
Pre-trade check. The point is that it lives between the strategy and the broker, not inside the strategy.

Layer 2 — Loss limits

A maximum loss per day, and usually a maximum drawdown from the account’s recent high. The design question is what "breach" triggers, and there are only two honest answers.

Response to a loss-limit breachWhat it actually achieves
Stop opening new positionsNothing, if the open position is what is losing
Flatten the book and stopBounds the day’s loss — the only version that does

Almost every system starts with the first option because it is easier, and almost every system that has had a bad day moves to the second. If the limit does not close positions, it is a preference, not a limit.

Layer 3 — Order rate limits

A cap on orders per second and per minute, enforced by your own code rather than by the broker rejecting you. Two reasons: the broker’s throttle response is a failure you then have to handle, and the SEBI framework makes your peak order rate a regulatory fact about your strategy rather than an implementation detail.

A rate limiter also happens to be the cheapest possible circuit breaker against a logic bug. A loop that would have sent ten thousand orders sends the first fifty and then trips something you can alert on.

  • Cap orders per second and per minute — a burst limit alone lets a slow leak through.
  • Count modifications and cancellations. They are order messages and the exchange counts them.
  • Treat hitting your own limit as an alert, not as normal back-pressure. If it fires in normal operation, either the limit or the strategy is wrong.

Layer 4 — A kill switch that outlives the strategy

System architecture

The kill path must bypass the strategy

A separate supervisor talks directly to the gate and broker, so a deadlocked or runaway strategy cannot block the emergency path.

The strategy sends intents through an order gate to the broker. A separate kill-switch service has its own operator trigger, process, credentials and broker session. On activation it disables the order gate, queries broker open orders and positions, cancels orders, flattens from broker-reported quantities, verifies flat and alerts. It does not wait for the strategy.
Independence means different process, credentials and preferably infrastructure—not a Boolean variable inside the process being killed.

This is the control most often built wrong. A kill switch implemented as a flag the strategy checks each iteration does not help when the strategy is stuck, deadlocked, or in a tight loop — which are exactly the conditions under which you want to kill it.

The kill switch has to be a separate process with its own credentials and its own connection to the broker, capable of cancelling all open orders and flattening all positions without cooperation from the strategy. It should also be able to stop the strategy, but flattening must not depend on that succeeding.

What a usable kill switch does, in order

  1. Revoke the strategy’s ability to place new orders

    Ideally at the gate layer, so it takes effect even if the strategy process is unresponsive. A shared flag in Redis that the order manager reads before every send works; a variable inside the strategy does not.

  2. Cancel every open order

    Query the broker for open orders rather than relying on your own record of them. Your record is what might be wrong.

  3. Flatten every position

    Market orders, from the broker’s reported position, not from your internal book. Confirm each fill.

  4. Verify flat, then alert

    Re-query positions and confirm they are zero. A kill switch that reports success without verifying is worse than none, because it removes the urgency to check manually.

  5. Stay killed until a human re-enables it

    No automatic recovery. Whatever caused the kill has not been diagnosed yet.

Layer 5 — Reconciliation

Once a day, and ideally continuously, compare three things: what your system thinks the position is, what the broker says the position is, and what the sum of your fills implies the position should be. All three must agree.

DivergenceLikely cause
System long, broker flatAn order you believed filled was rejected or cancelled
System flat, broker longA fill arrived that your system did not process — often after a websocket reconnect
Quantities differ by a partial amountPartial fill handling; the classic source of unbalanced multi-leg positions
Fills sum correctly but position differsCorporate action, expiry settlement, or a manual intervention nobody logged
What each kind of divergence usually means

The rule is unconditional: when reconciliation fails, trading stops until it is explained. A system that trades on top of an unexplained position mismatch is compounding an error it does not understand.

Position sizing

Trend chart

Compounding drawdown from repeated 2% losses

A modest risk fraction still produces a material decline across an ordinary double-digit losing streak.

Starting from a capital index of 100 and risking 2 percent of current capital on each loss, capital is 90.4 after five consecutive losses, 81.7 after ten, and 73.9 after fifteen. The account survives, but needs a 35.3 percent gain from 73.9 to return to 100.
Illustrative compounding with no wins, costs or slippage. Position sizing should be tested against the longest plausible streak, not the average trade.

Sizing is not really a separate layer — it is what makes the loss limits reachable rather than theoretical. The relevant question is not "how much can I make on this trade" but "how many consecutive losses does this size survive".

A strategy with a 55% win rate will, over a few thousand trades, produce a losing streak in the double digits. Not as a tail scenario — as an expected occurrence. If the size does not survive fifteen consecutive losses, the strategy will end the account before its edge has a chance to express itself.

≥ 10
Consecutive losses
Expected at a 55% win rate over a few thousand trades
1 lot
Correct starting size
Regardless of account size, for the first month live
0
Acceptable unexplained mismatches
Stop and investigate every time

The order these should be built in

  1. Position limits — before the first live order.
  2. Kill switch, external, tested — before the first live order.
  3. Reconciliation, run daily — from day one.
  4. Loss limits that flatten — before you scale past one lot.
  5. Order rate limits — before any strategy that fires bursts.

The first three are not optional and are not much work. What makes them feel optional is that nothing bad has happened yet, which is also true of every system the day before something bad happens.

Frequently asked questions

Why does a kill switch need to run in a separate process?

Because the conditions that require a kill — a deadlock, a tight loop, an unresponsive process — are the same conditions that prevent the strategy from checking a flag. A kill switch with its own process, credentials and broker connection can cancel orders and flatten positions without any cooperation from the strategy it is stopping.

Should a daily loss limit stop new trades or close open positions?

Close positions. A limit that only blocks new entries does nothing when the open position is the thing losing money, which is the main scenario the limit exists for. It also has to be computed on realised plus unrealised P&L, since a realised-only calculation will not fire while a position is deep underwater and still open.

How do I decide position size for an automated strategy?

By asking how many consecutive losses the size survives, not by expected profit. A strategy with a 55% win rate will produce losing streaks in the double digits over a few thousand trades as a matter of course. If the size cannot absorb roughly fifteen consecutive losses, the account will not last long enough for the edge to express itself.

What should happen when position reconciliation fails?

Trading stops until the divergence is explained. A mismatch between your system's position, the broker's reported position and the sum of your fills means one of them is wrong, and trading on top of an unexplained mismatch compounds an error you do not understand yet. The most common causes are unprocessed fills after a websocket reconnect and mishandled partial fills.

Do I need order rate limits if I am below SEBI's 10 orders per second threshold?

Yes, for two reasons independent of the regulation. Your own limiter is a circuit breaker against logic bugs — a runaway loop sends fifty orders instead of ten thousand. And enforcing the cap yourself is cleaner than handling the broker's throttle rejection as a failure case. Count modifications and cancellations too, since the exchange counts order messages rather than trades.

QuaTick Risk Desk

Risk & compliance

Position sizing, margin mechanics, kill switches and the operational controls that decide whether an automated book survives a bad week.

From Paper Trading to Live: A Go-Live Checklist

A go-live checklist for automated strategies: what paper trading cannot test, the failure modes that only appear with real fills, and how to structure a first month live.

QuaTick Risk Desk9 min read

Margin Mechanics for Indian F&O

How margin is computed on Indian futures and options — SPAN, exposure, upfront collection and peak margin — and how much headroom an automated strategy needs to leave.

QuaTick Risk Desk7 min read

Algo Trading in India: What It Actually Involves

A practical introduction to algorithmic trading in the Indian market: what counts as an algo, the four components of any automated setup, and the failure modes that show up first.

QuaTick Research10 min read