← All posts
NexAlgo7 min read

Kill switches and circuit breakers

Automation removes hesitation from trading — including the useful kind. The failure mode of a bot is not one bad trade; it is twelve bad trades in ninety seconds while you are asleep. Here is the machinery that exists to interrupt that, and what it cannot do.

A discretionary trader having a catastrophic day usually stops. Something in them notices, somewhere around the fourth loss, that today is not the day. It is an unreliable safety mechanism and it is better than nothing.

A bot has no such mechanism. It will take the fifth trade with exactly the conviction it took the first, and the twentieth after that, because the rule that fired is still firing. Whatever stops it has to be built, and it has to run whether or not anyone is watching.

Two independent checks, on purpose

NexAlgo enforces a daily loss limit in two places that do not depend on each other. That is not redundancy for its own sake — each covers a case the other structurally cannot.

1. At the moment a signal arrives

Before any order is placed, the execution path checks the bot's state and the day's realised P&L:

apps/nexalgo/src/execute.ts
if (bot.kill_switch)
  return record(bot, signal, "skipped",
    { reason: "kill switch engaged" });

if (bot.max_daily_loss !== null) {
  const dayPnl = await botDayPnl(bot);
  if (dayPnl <= -Number(bot.max_daily_loss)) {
    await db.query(`update bots set kill_switch = true ...`);
    return record(bot, signal, "skipped", { reason: ... });
  }
}

Two things worth noticing. First, a breach does not merely skip this signal — it engages the kill switch, so every subsequent signal is refused too. A gate that only blocked the current order would let the next one through thirty seconds later, which is not a limit, it is a speed bump.

Second, the skip is recorded. A refused signal is written to the log with its reason, so a bot that goes quiet is distinguishable from a bot that is being stopped. Silent protection is indistinguishable from a broken integration at three in the morning.

2. Every sixty seconds, regardless

The signal-time check has a hole in it: it only runs when a signal arrives. A bot holding a losing position and receiving no new signals could sail past its daily limit untouched, because nothing triggers the check.

So a second mechanism sweeps independently:

apps/nexalgo/src/guardian.ts
/**
 * Risk guardian — the always-on loop. Every 60s it sweeps active
 * bots with a max_daily_loss rule and engages the kill switch the
 * moment the day's realized P&L breaches the limit, without waiting
 * for the next signal to arrive.
 */

Every sixty seconds it queries every active bot that has a limit and no kill switch already set, computes the day's realised P&L, and trips the switch on any that have breached. It does not wait to be asked.

Tripping loudly

When the guardian engages a kill switch it raises a critical alert carrying the bot name, the day's P&L and the limit that was breached. A sweep that fails — a database error, an exchange timeout — also alerts, at the same severity.

That second case matters more than it looks. A risk system that dies quietly is worse than no risk system, because you are now trading with protection you believe exists. The failure of the guard has to be as loud as the event it guards against.

Testnet is the default, and defaults are decisions

Bots run against exchange testnets unless explicitly configured otherwise, and the check is written so that only an explicit opt-out reaches live markets. A missing field, a malformed request, a half-finished configuration — all of these resolve to testnet.

This is a small thing that reflects a general principle: the dangerous path should require someone to deliberately choose it, and every ambiguous case should fall to the safe side. At the time of writing, no NexAlgo bot trades real money at all.

What none of this saves you from

This is the part usually left out, so plainly:

  • The sweep runs every sixty seconds. In a fast market a position can move well past your limit inside one interval. The kill switch is not a stop loss and does not act at a price.
  • It measures realised P&L. An open position sitting at a catastrophic unrealised loss has not breached a realised-loss limit.
  • It depends on our service running. If the engine is down, nothing sweeps. Keep direct access to your exchange account so you can always close a position yourself, without us.
  • It depends on the limit you set being sensible. A daily loss limit larger than you can absorb is a limit that will be honoured exactly as configured.
  • It cannot help against the exchange. Outages, rejections, liquidations and funding are between you and them.

Guardrails are seatbelts, not a reason to drive faster. They convert some catastrophic outcomes into merely bad ones, which is worth having, and they do not make automated trading safe. The risk disclosure sets out the rest.

Why build it before launch

None of this is a feature anyone asks for in a demo. It is invisible when it works, and the only evidence it exists is a bot that stopped when it should have.

It is built first because the alternative is building it after the first person needs it — and by then the reason you know it was needed is that somebody lost money. That is a bad way to discover a requirement, and an unforgivable way to discover it with someone else's account.

Nexlot is in construction and testing — no public sign-ups, nothing for sale, and every bot on testnet. Join the waitlist to hear when that changes.