The first time a bot of mine ran away, it did not crash. That was the problem. Crashing would have been a mercy. Instead it kept doing exactly what I told it to, which was to keep placing orders based on a price feed that had quietly gone stale. From the bot's point of view everything was fine. It was confident, it was fast, and it was wrong in a tight little loop for about forty minutes before I noticed. Nothing threw an exception. Nothing paged me. The logs looked clean because the code was behaving perfectly. The world was the thing that had changed.
That is the whole reason to think seriously about a kill switch, and it is also why most people build the wrong one. The naive version lives inside the bot: some check that says if drawdown exceeds X, stop trading. That check is fine right up until the bot itself is what is broken, at which point you are asking a program that has lost the plot to notice that it has lost the plot and shut itself down responsibly. Sometimes it does. Often it cannot, because whatever broke also broke the part that was supposed to catch it. A kill switch you can only trust when the bot is healthy is not really a kill switch. It is a comment.
So I think about shutoff in three layers, roughly from most-specific to most-brutal, and the layers matter because each one covers the failure modes the others miss.
Layer one: circuit breakers inside the strategy
These live in the bot process and they are the cheapest to build, so they are worth having even though they are the least trustworthy. The point of a strategy-level breaker is to catch the failures that are still legible to the code. Drawdown is the obvious one. If the strategy is down more than some threshold over a rolling window, it flattens and refuses to re-enter until a human clears it. Pick the window carefully, because a daily breaker and an intraday breaker catch very different things and you probably want both.
The more useful breakers watch behavior rather than PnL. Error rate is the big one. If your order-placement calls start getting rejected, or your fills stop confirming, or the round trip to the exchange starts timing out, that is a signal to stop long before the losses show up. A bot that is spraying orders into a venue that keeps rejecting them is one rate-limit ban away from a bad afternoon. I also like a breaker on order frequency: if the bot tries to place more orders per minute than the strategy could plausibly want, something is looping, and looping is almost never good. Same for a fill that comes back at a price wildly off from where you expected. Treat surprising fills as a fault, not a fun surprise.
The honest caveat is that all of these run inside the process that might be the thing failing. A breaker that depends on a stale price feed to decide whether the price feed is stale will happily conclude that everything is fine. So build these, lean on them for the ordinary cases, and do not trust them with your account.
Layer two: exposure caps enforced from outside
This is the layer people skip, and it is the one that actually saves you. The idea is a second process, separate from the bot, whose only job is to watch the account and enforce hard limits that the bot cannot override or even see. It does not know your strategy. It does not care about your edge. It reads positions and balances straight from the exchange, and if total exposure crosses a line, it starts cancelling and flattening, and it does not ask the bot for permission.
The reason this works is that it fails independently. If the bot's price feed goes stale, this process is on its own feed. If the bot deadlocks, this process is still running. If the bot's logic decides that a five-hundred-percent position is a great idea, this process reads the real position from the venue and disagrees. Because it queries the source of truth directly rather than trusting the bot's internal accounting, it catches the whole category of bugs where the bot thinks it is flat but is actually very much not flat. That specific disagreement, between what the bot believes it holds and what the exchange says it holds, is the single most valuable thing to monitor, and the bot is structurally unable to monitor it about itself.
A few things I have learned to bake into this layer:
- Enforce caps at the exchange account level, ideally with API keys that have their own position or notional limits set at the venue, so the ceiling exists even if your process dies too.
- Have it reconcile the exchange's reported positions against the bot's claimed positions on a short interval, and treat any drift as a fault worth flattening on.
- Make its default action cancel-and-flatten, not just pause. A bot that is paused with open positions in a moving market is still exposed.
- Give it the dumbest possible logic. Every line of cleverness you add is a line that can break the thing whose entire purpose is to work when everything else is broken.
Layer three: the big red button
The last layer is a human pulling the plug, and it needs to be genuinely one action. Not a login, then a dashboard, then a confirm dialog, then a second confirm because the first one timed out. When you need this button you are already stressed, you may be on your phone, and the market is not waiting. So the bar is: one deliberate action flattens everything, everywhere, across every venue and every strategy, and revokes the keys the bots are using so nothing can quietly re-open positions behind you.
The mechanism matters more than the button. The cleanest big red button I have used is not code at all, it is revoking or disabling the API keys at the exchange, because that stops new orders regardless of what any process is doing. Pair that with a flatten command that runs outside every bot, on its own credentials, so it works even if the bots are wedged. And test it. A kill switch you have never fired is a theory. Practice pulling it on a small live account, on a weekend, and time yourself, because the gap between believing you can flatten in ten seconds and actually being able to is usually embarrassing.
A rough sequence to build it in
If I were starting from nothing, I would build it in this order, because the order roughly tracks how often each layer saves you versus how hard it is to get right. Start with the manual flatten script on separate credentials, since it is small and it is the floor under everything else. Then add the external exposure monitor with position reconciliation, because that catches the bug that hurt me most. Then, last, add the in-process breakers for drawdown and error rate, because they are nice but they are also the ones you cannot fully trust.
The mental model that keeps me honest is to assume the bot is compromised and ask what still protects the account. Anything that only works when the bot is healthy is a convenience, not a safeguard. The real safety lives in the parts that keep working after the bot has already gone quietly insane. When we wire up non-custodial execution across venues on Blockcircle, this is the part I care about most, because keeping your keys under your control is exactly what makes the outermost kill switch, revoking access, something you can actually do yourself.
None of this makes a bad strategy good. A kill switch is not edge, it is insurance, and insurance you never file a claim on still feels like money well spent the one time you do. Build the outer layers first, keep them stupid, and fire them at least once before you need them.