← Back to blog

Bug Patterns · Live Bot Audits

The Risk State That Only Exists in RAM

Two more bugs from live trading bot audits — where the number the bot tracks internally quietly stops matching what's actually protecting you on the exchange.

2026-08-25

A bot's internal variable and the real state of the world are two different things, connected only by whatever code keeps them in sync. Most of the time nobody notices the gap, because most of the time nothing forces the two to diverge. The two bugs below both come from the same root cause: a number that lives only in a running process's memory, with nothing writing it anywhere durable and nothing pushing it back out to the exchange once it changes.

Case 1: the daily loss limit a routine restart wipes out

A live bot I reviewed — real orders hitting a real exchange, not a backtest — ran under a process supervisor configured to restart it automatically on failure, with an explicit crash-loop guard in the unit file. That's a reasonable, even mature, piece of operational hygiene: unattended bots should recover from crashes rather than sit dead until someone notices.

The daily loss limit told a different story. The running total that a loss-limit check compared against its threshold was set once, in the initialization code, and never persisted anywhere — no database row, no file, nothing that survives the process exiting. A separate function existed to recompute the day's real losses from exchange history on a fresh start, correctly handling the exchange's own midnight rollover. It was never called. Every restart — crash, deploy, or the supervisor's own routine recovery — silently reset the day's tracked losses to zero, regardless of how close to the limit the bot actually was five seconds before the restart.

The bot's own logs never show this as a bug, because from the process's point of view nothing went wrong: it started up clean, initialized its state per its own code, and carried on. The gap only exists between what the exchange's account history says happened that day and what the bot currently believes — and nothing in the bot ever compares the two.

Case 2: the stop-loss that only moves in a Python variable

A different bot, trading options on one exchange, ran a trailing stop meant to lock in profit as a position moved favorably. The logic that decided when and how far to trail the stop was correct — it recalculated a new stop level on every price update and stored it on the in-memory trade object.

What it never did was tell the exchange. The execution module that this bot used to open and close positions had no order-amend or order-edit call anywhere in it — only submit-new and cancel. The trailing logic updated trade.stop_loss, which fed the bot's own dashboard and logs, and stopped there. The actual protective order resting on the exchange's books stayed at the price it was first submitted at, for the lifetime of the position. A trader watching the dashboard would see the stop "trailing up" in real time while the order that would actually close the position on adverse movement never moved at all.

This is one failure shape out of several I check for. The full checklist — entry-price path and exit/risk-limit path together — is on one page, or I can walk through it against your bot directly.

Get the safety-net checklist →

Why "it worked in testing" doesn't catch this

Both bugs pass a normal test suite without any trouble, because a unit test constructs its own world and never restarts the process or checks the exchange's own order book — it just calls the function and inspects the in-memory result, which is exactly where each of these bugs' state lives. The daily-loss-limit function computes the right answer given a correct running total; it's simply never fed one after the first restart. The trailing-stop function computes the right new stop price; it just never reaches an API call that would move the real order. Neither bug is a logic error inside the function you'd naturally go read — it's a missing wire between a correct calculation and the one place that calculation needed to land.

What to check in your own bot


Both bots were live, with real capital, when I found these. Neither is exotic — a process supervisor and a trailing stop are both perfectly ordinary things to build. The failure isn't in wanting either feature; it's in the seam between two pieces of code that each work correctly on their own but were never actually connected. I've written about a related shape before — a stop-loss that logs "closed" without telling the exchange — this is the same lesson again: what a bot's internal state says and what's actually true in the market are two separate claims, and only one of them is checkable from the outside.

Contact

Get in touch

Not investment advice. Honest Backtest provides technical code review and backtest recalculation only — an honest read on what the code and the numbers actually show, not a recommendation to trade.
Photography via Unsplash.