Ten Layers of Live-Trading Safety: Every Switch Before a Real Order

How many switches stand between a strategy and a real broker order? In Kestrel, the answer is ten, and none of them trusts the others.

Intro

How Far Is a Real Order?

A strategy decides to buy. How many separate things must be true before a real order reaches the broker? In Kestrel, the answer is ten. And the interesting part is that no single layer is trusted to be enough. This is episode twenty-five: Ten Layers of Live-Trading Safety.

  • Strategy to broker
  • Count the switches

Educational Only, Off by Default

Before anything else, a clear statement. This course is educational. Live trading in Kestrel is off by default, and what follows is a description of a design, not a claim that it has been proven in production. Nothing here is financial advice, and no strategy is implied to work.

  • Live is off by default
  • Educational only
  • Not financial advice

Where We Left Off

Last time, in episode twenty-four, we looked at the order service: sessions, shadow books and crash recovery. Today we zoom out and enumerate every switch that stands between an alpha's target position and a real FYERS order. Uncertain outcomes and session details are covered in those earlier episodes, so we will not repeat them.

  • Ep 23: uncertain orders
  • Ep 24: sessions

Why Defence in Depth

The Swiss Cheese Model

Picture slices of Swiss cheese stacked in a row. Every slice has holes, because every check can be wrong, misconfigured, or skipped. An accident needs a path through every hole at once. Add independent slices, and the odds that the holes line up shrink dramatically.

  • Each layer has holes
  • Holes must not align

Independent Means Independent

The key word is independent. If two layers read the same setting, one typo defeats both. So Kestrel places layers in different files, different binaries and even different sources of truth: a config file, the process environment, the broker's own answers. One mistake should not open two gates.

  • Different files
  • Different processes
  • Different failure modes

The Ten Gates at a Glance

Here is the whole map. One: config validation. Two: the engine refusing real venues in backtest. Three: live enabled in orders dot json. Four: the environment variable. Five: the account allow-list. Six: login and profile. Seven: the INTRADAY rule. Eight: reconcile before welcome. Nine: runtime limits. Ten: the boot restore rule.

  • Config, engine, switches
  • Account, login, product
  • Reconcile, runtime, boot

Gates 1 and 2: Config and Engine

Gate 1: Config Validation

Gate one happens before anything starts. If a strategy's mode is live, its orders section must be of type service, with an account. Any other orders path for a live strategy is a ConfigError. The run simply never begins, and the validator, kestrel config, can tell you why without launching anything.

  • mode live needs service
  • Account required
  • Else: ConfigError

The Trader Has No Broker Code

There is a deeper guarantee behind gate one. The trader binary contains no FYERS code at all. Even if a bug skipped validation, the trader has nothing to call. It can only send intents to the order service. A forbidden path is not merely blocked; it does not exist.

  • kestrel_trader: no FYERS code
  • Cannot bypass the service

Gate 2: Backtest Refuses Real Venues

Gate two lives in the engine. A venue can report whether it places real orders, through placesRealOrders. In backtest mode, the engine refuses any venue that answers yes. So even a mistaken wiring cannot make a historical replay talk to a live broker.

  • placesRealOrders()
  • Backtest refuses it

Gates 3 and 4: The Two Switches

Gate 3: live.enabled

Gate three: in orders dot json, live dot enabled must be true. Only then is a FyersRouter even constructed. If the flag is false, there is no router object in memory, no transport, and nothing to misuse. The strongest protection is an object that was never created.

  • orders.json live.enabled
  • Must be true
  • Else no FyersRouter built

Gate 4: The Environment Variable

Gate four is a second, separate switch: the variable FYERS underscore ALLOW underscore LIVE underscore ORDERS must be exactly the string one, in the order service's own process environment. It is read with getenv. A value in a dot env file does not count, because the code never consults the file for this decision.

  • FYERS_ALLOW_LIVE_ORDERS
  • Exactly '1'
  • Service process environment

The Environment Check, Verbatim

Here is the function, and it is tiny. It reads the variable, and returns true only if the pointer is not null and the string equals one. If the variable is missing, that is false. If it is anything else, that is also false. There is no parsing cleverness to get wrong.

  • getenv, compare to "1"
  • null means false

Why Exactly One

Why insist on exactly one? Because loose parsing invites accidents. If true, yes, or two all counted, a stray value pasted from somewhere else might switch on real trading. A strict match turns the switch into a deliberate act. If you meant it, you type the one character the code expects.

  • true: no
  • yes: no
  • 2: no

Why Not a Dotfile

And why the process environment rather than a dotfile? Dotfiles get copied between machines, committed by accident, and edited casually. The process environment belongs to one running process, set by whoever launches it. It makes enabling live a visible act at launch time, not a leftover line in a file.

  • Dotfiles get copied
  • Env is per process
  • Deliberate, visible act

Gates 5 to 7: Account and Product

Gate 5: The Account Allow-List

Gate five concerns who is asking. A live hello must carry the account, and that account must appear in the live dot accounts list. A session cannot invent an account and be trusted. The operator decides, ahead of time, which accounts may ever be used.

  • hello.account
  • Must be in live.accounts

The Refusal Code, Verbatim

This is the start of the refusal function. First it asks whether live is enabled and a router exists. Then whether the environment switch is set. Then it reads the account from the hello, and refuses if it is empty. Notice how every message names the file and path the operator should change.

  • liveRefusal(m)
  • Checks run in order
  • Message names the file

Messages Tell You What to Fix

A refusal returns a pair: a code, and a human message. The code is for machines, and the message is for the person at the keyboard. A refusal that only says no teaches nothing. One that says change orders dot json, live dot enabled, turns a failure into a ten-second fix.

  • Code plus message
  • Names file and path
  • Operator can act

Gate 6: Login and Profile

Gate six asks the broker itself. Today's FYERS login must be valid, and fetching the profile must work. If not, the refusal is login. Then the profile's id must equal the account in the hello. If they differ, the refusal is account. The service checks reality, not just the claim.

  • Today's login must work
  • Profile id equals account
  • Errors: login, account

Gate 7: INTRADAY and Carrying Alphas

Gate seven is about product types. FYERS squares off INTRADAY positions at the close. An alpha that carries positions overnight would have its position silently closed by the broker, leaving Kestrel's books wrong. So INTRADAY with a carrying alpha is refused, with the code config.

  • Broker squares off INTRADAY
  • Carrying alpha refused
  • Refusal: config

Gate 8: Reconcile Before Welcome

The Live Hello Becomes a Ticket

Now the flow around gates six and eight. A live hello becomes a PendingLive. In stage Check, the service asks the router to request a check for the account. In stage Sync, it asks for a sync. The runner is welcomed only after both tickets return. Until then, it waits at the door.

  • PendingLive
  • Stage Check, stage Sync
  • Welcome after both

Asynchronous by Design

All of this is asynchronous. The broker can be slow, and the service loop must never block on it. So the router returns tickets, and the loop keeps serving everyone else: market data, other sessions, status requests. When a ticket completes, the pending hello moves to its next stage.

  • Service loop never blocks
  • Tickets, not waits

Reconcile: Two Ledgers

Reconcile compares two ledgers. On the left, what Kestrel believes each alpha holds. On the right, what the broker reports. Suppose alpha A shows plus fifty on both sides, and alpha B shows plus seventy-five here but only plus twenty-five at the broker. Alpha A matches. Alpha B does not.

  • Our book vs broker book
  • Compare per alpha

Block Only the Affected Alpha

What happens to alpha B? It is blocked, greyed out, and cannot trade. Alpha A is unaffected and continues. The blast radius of a mismatch is exactly the alphas that mismatch, not the whole account, so one confused position does not halt everything that is healthy.

  • Mismatch blocks that alpha
  • Others continue
  • Greyed out

Never Trade to Fix It

Crucially, nothing is ever traded to fix a mismatch. The system could send twenty-five units to make the books agree, but it does not know which side is wrong. Maybe the broker is right. Maybe a manual trade happened. And if broker positions cannot be read at all, every alpha holding a position is blocked.

  • No auto-correction
  • Humans investigate
  • Unreadable: block all holders

Gate 9: Runtime Guards

Gate 9: Risk Limits

Gate nine is the runtime gate, and it runs on every order. Risk limits cover order notional, position notional, gross exposure, daily loss, orders per second, and a price collar around a reference price. Global caps in orders dot json under risk dot caps sit above all per-strategy limits.

  • Order and position notional
  • Gross exposure, daily loss
  • Orders per second, price collar

Local Rejections Before the Wire

Some orders can be rejected locally before bothering the broker. Expired contracts. Index instruments, which are not tradable. IOC orders on MCX. Quantities that are not whole multiples of the lot size. Catching these yourself is faster and cleaner than learning about them from a broker error message.

  • Expired contracts
  • Index instruments
  • IOC on MCX
  • Lot multiples

Kill Switch and Flatten

Gate nine also includes the operator's emergency controls. The kill switch stops new orders. Flatten closes out positions. These are not strategy logic; they are levers for a human who sees something wrong. Last, a place is never re-sent when its outcome is uncertain, and the ledger is written before the post. Episode twenty-three covers why.

  • Kill switch
  • Flatten
  • Operator override

Gate 10: Boot Restore

Gate 10: Restoring at Boot

Gate ten covers restarts. When the service boots, a live session with journal records may be restored, but only if live is enabled and the environment switch is set. Otherwise the service logs an ERROR saying that session's FYERS orders are not supervised. It tells you plainly, instead of pretending all is well.

  • Journal has live records
  • Needs live.enabled AND env switch
  • Else ERROR: NOT supervised

Worked Example

Three Misconfigurations

Let us test the stack with three mistakes. An operator writes a live strategy with a non-service orders path: stopped at gate one by a ConfigError. Another sets everything correctly but leaves live dot enabled false: stopped at gate three, no router. A third forgets the environment variable: stopped at gate four.

  • Stopped at gate 1
  • Stopped at gate 3
  • Stopped at gate 4

Forgot the Variable

Walk through the third case. The operator sets mode live, starts the order service, and the runner sends a live hello. Live is enabled, so the router exists. But liveOrdersAllowed returns false because getenv found nothing. The hello is refused with live_disabled, and the exact message says the variable must be set to one.

  • mode live, env unset
  • hello refused
  • live_disabled

Setting It to true

Now the operator guesses and sets the variable to true. The runner tries again. getenv returns a pointer this time, but the string true is not equal to one, so the function returns false. Still refused, with the same message. Only after setting exactly one does this gate open, and gates five to eight still wait beyond it.

  • FYERS_ALLOW_LIVE_ORDERS=true
  • Still refused
  • Only "1" passes

Pitfalls

Pitfall: One Master Switch

Pitfall one: a single master switch. It looks simple, but a single flag is one typo, one merge, one copied config away from disaster. Independent layers force several unrelated mistakes to coincide. Simplicity for the operator is nice, but safety comes from requiring agreement among things that fail differently.

  • One flag, one typo
  • One accident away
  • Many switches, many sources

Pitfall: Auto-Fixing Mismatches

Pitfall two: auto-fixing. It feels helpful to send a corrective order when books disagree. But if the broker is the side that is right, or someone traded by hand, the fix moves you further from reality, with real money. Block the alpha, tell a human, and let them decide.

  • Fix trade can double the error
  • Unknown which side is wrong

Pitfall: Safety From a Dotfile

Pitfall three: reading safety switches from a dotfile. A dotfile outlives the day it was written. It gets copied to a new machine, synced into a backup, loaded by a different tool. A switch that arms real orders should be set where it is visible: the environment of the process, at launch.

  • Silent leftovers
  • Copied between hosts
  • Use process environment

Paper Can Never Reach FYERS

One more structural guarantee. Paper sessions in the same process use PaperVenue, which has no transport at all. So even when live is enabled and the environment switch is set, a paper session cannot reach FYERS. It has no wire to send on. Again, absence beats a rule.

  • PaperVenue has no transport
  • Even when live is enabled

Recap

Recap: Ten Layers

To recap. Config validation and the engine refusal. Live enabled, so a router exists. The exact-one environment variable. The account allow-list, today's login and profile, and the INTRADAY rule. Reconcile before welcome, without ever trading to fix. Runtime limits and local rejections. And boot restore, which is supervised or loudly flagged.

  • Config, engine, live.enabled, env
  • Account, login, product
  • Reconcile, runtime, boot

Next: A Day in Paper Trading

Next, in the final episode, we run the whole stack through a trading day in paper mode, and collect the lessons learned. Remember: live is off by default, the course is educational, and nothing here is financial advice. Thanks for watching.

  • Ep 26: the whole stack
  • Lessons learned
  • Subscribe