The nine order gates
Every order a deployed strategy tries to place passes through a pipeline of nine validators before it reaches a broker. This page is the complete reference: what each one measures, what unit its limit is in, whether it is on by default, and exactly what happens when it trips.
Read the badge on each gate. Off until you set it means the rule does not run.
How the pipeline evaluates
An order arrives, each applicable validator runs in sequence, and the first violation stops the order. The order is rejected with the violating rule's name, its reason code, the current value and the limit.
Two properties of the pipeline are worth understanding before the gates themselves:
Order shape is checked before metrics are loaded. max_order_value and per_trade_risk
need nothing but the order itself. The gates that need position counts, today's P&L or capital
figures declare that requirement, and those reads happen only if an order survives the cheap
checks. A malformed order never costs a database query.
Entries are gated; exits are not. Every gate that caps exposure or velocity applies to entries only. A strategy at its position cap must still be able to close a position. This is the single most important asymmetry in the system. And its consequence is that a tripped loss cap leaves your open position running to its own stop.
Gate 1: Maximum order value
max_order_valueOff until you set itAn absolute ceiling on how large any one order can be. It is a pure order-shape check: it needs no market state, no position history and no capital read, which is why it runs first.
When it trips: the order is rejected with MAX_ORDER_VALUE_EXCEEDED. The strategy keeps
running; the next signal that produces a smaller order is placed normally.
Set it when you want a hard cap on single-order size regardless of what the sizing logic computes, the protection against a sizing bug, a bad price tick, or a strategy that scaled up more than you intended.
Gate 2: Per-trade risk
per_trade_riskOff until you set itThe gate that makes "never risk more than 1% on a trade" a real constraint rather than an intention. It computes the actual money at risk between entry and stop and compares it to a budget derived from your allocation.
It only applies to an entry that carries a usable stop price. A market entry with no stop has no measurable risk distance, so the gate cannot evaluate and skips rather than guessing. That is a real limitation worth knowing: a strategy with no stop-loss is not protected by this gate.
When it trips: the order is rejected with PER_TRADE_RISK_EXCEEDED.
Also skips when the strategy's allocated capital is unknown. It cannot resolve a percent into an amount, and it will not invent one.
Gate 3: Maximum open positions
max_open_positionsOff until you set itCaps how many positions the strategy can hold at once. Working orders count, an entry sitting unfilled on the book occupies a slot, so the cap cannot be exceeded by racing two entries.
When it trips: the entry is rejected with MAX_OPEN_POSITIONS_REACHED. Exits are always
permitted, so the strategy can close a position to free a slot and continue.
Historically this control was called max_trades in the strategy configuration, and that key
is still read for compatibility.
Gate 4: Maximum trades per day
max_trades_per_dayOff until you set itA velocity brake. Counts entries, not round trips, and resets at UTC midnight. Not at the local market open. For an Indian equity strategy this means the counter boundary falls at 05:30 IST, inside the overnight gap rather than at the session open.
When it trips: the entry is rejected with MAX_TRADES_PER_DAY_REACHED until the next UTC
day.
Set it when you want a bound on how much a strategy can churn, the protection against a signal that starts firing every candle in a choppy market.
Gate 5: Maximum consecutive losses
max_consecutive_lossesOff until you set itHalts new entries after N losing trades in a row. The streak is derived from the realised sell-P&L timeline and resets on the next winning round-trip. You do not have to clear it manually.
When it trips: the entry is rejected with MAX_CONSECUTIVE_LOSSES_REACHED.
How trading resumes: this gate works with a cooldown. Rather than blocking until a win
that cannot happen while entries are blocked, the halt is time-boxed: after
cooldown_bars_after_loss bars at the strategy's timeframe have elapsed since the losing
trade, the violation is suppressed and entries resume. The default is 3 bars when you have
not set a value. See Alerts & breaches.
Set it when you want the strategy to stand down in conditions it is clearly misreading, rather than paying for the same mistake ten times.
Gate 6: Daily loss limit
daily_loss_limitOff until you set itThe classic circuit breaker: stop trading once today has cost more than a set fraction of the strategy's capital.
Realised only. An open position that is deeply underwater does not count here. This gate reads closed-trade P&L for the day. If you want unrealised loss to halt entries, that is gate 7.
When it trips: the entry is rejected with DAILY_LOSS_LIMIT_EXCEEDED. New entries are
blocked; the open position is left to its own stop and target. Blocking exits would trap
capital in a losing position, which is the opposite of what this gate exists to do.
Skips when allocated capital is unknown, because a percent cannot be resolved into an amount.
Gate 7: Mark-to-market loss
mark_to_market_lossOff until you set itThe unrealised-loss brake. Halts new entries once realised plus unrealised loss is down by a set amount.
Stated as an amount, not a percent, deliberately. A percent silently moves the breaker when your capital changes; an amount is the number you actually meant. It mirrors the backtest control of the same name, so a strategy behaves the same researched and deployed.
The mark it uses is the incoming entry's own price, the entry is for the same instrument the strategy already holds, so its limit or trigger price is the current market for the position being valued. A market order with no price hint carries no usable mark, and the gate then skips rather than guessing a valuation.
Two known narrowings versus the backtest control, stated rather than hidden:
- "Realised" here is today's realised P&L. The backtest measures from the run's starting balance. Closing that gap needs a lifetime-realised metric the live path does not carry yet.
- Open exposure is derived from the buy-lot FIFO ledger, so a short-side position contributes quantity but not a mirrored unrealised sign.
When it trips: the entry is rejected with MARK_TO_MARKET_LOSS_EXCEEDED. As with the daily
cap, the open position is left to its own exits.
Gate 8: Maximum capital allocation
max_capital_allocationAlways enforcedThe ceiling that makes a capital allocation mean something. Rejects an opening order that would push total deployed capital past the limit.
This one is not optional in practice. A configured value caps it explicitly; absent one, the strategy's allocated capital is still the effective ceiling, enforced through gate 9. A strategy cannot trade capital it was not given, and cannot reach into another strategy's allocation.
When it trips: the order is rejected with MAX_CAPITAL_ALLOCATION_EXCEEDED.
Gate 9: Available capital
available_capitalAlways enforcedThe last gate, and the one that cannot be turned off. An opening order the strategy cannot fund from its free capital is rejected.
This mirrors the ledger-backed capital check used everywhere else in the platform, so the same rule governs whether an order can be funded regardless of which path asks.
When it trips: the order is rejected with INSUFFICIENT_AVAILABLE_CAPITAL, reporting what
was needed and what was available.
With gate 8, this pair is why capital isolation holds. Together they are the guarantee that a strategy stays inside its allocation, the one risk property on this platform that does not depend on your configuration.
Three controls alongside the pipeline
Cooldown after a losing streak
Not a validator but a modifier on gate 5. Converts "blocked until a win" into "blocked for N
bars", so a consecutive-loss halt is self-clearing. Configured as
gates.cooldown_bars_after_loss; defaults to 3 bars at the strategy's timeframe. Cooldown
state is computed on every evaluation, not persisted. There is no stuck pause to clear.
Trading window
A time-of-day range, interpreted in IST, outside which the strategy does not enter. It
comes from the strategy's risk_and_execution.trading_window and carries no day-of-week or
holiday information, the source string does not encode any, so market calendars are handled
separately. See Trading hours & holidays.
A window that wraps past midnight is handled correctly as spanning two days, though every real window seen in practice is same-day.
Margin validation
For margined instruments, a margin validator checks the order against available margin before
placement and rejects with INSUFFICIENT_MARGIN. See
Margin, leverage & lot size.
Reason codes
Every rejection carries a stable code. These are what you will see in the risk panel, the strategy timeline, and Telegram notifications if you have them enabled.
| Code | Gate |
|---|---|
MAX_ORDER_VALUE_EXCEEDED | 1 |
PER_TRADE_RISK_EXCEEDED | 2 |
MAX_OPEN_POSITIONS_REACHED | 3 |
MAX_TRADES_PER_DAY_REACHED | 4 |
MAX_CONSECUTIVE_LOSSES_REACHED | 5 |
DAILY_LOSS_LIMIT_EXCEEDED | 6 |
MARK_TO_MARKET_LOSS_EXCEEDED | 7 |
MAX_CAPITAL_ALLOCATION_EXCEEDED | 8 |
INSUFFICIENT_AVAILABLE_CAPITAL | 9 |
INSUFFICIENT_BROKER_BALANCE | Broker-side funding |
INSUFFICIENT_MARGIN | Margin validator |
DUPLICATE_ORDER / DUPLICATE_STRATEGY_EXECUTION | Idempotency, not a risk limit |
Next
- Which limits are on by default, the on/off table and a starting configuration
- Alerts & breaches, what you see and how trading resumes
- Execution flow, where the gates sit in the order path