What Sits Between a Backtest and a Live Bot

A backtest is a single-pass function over a static file. A live trading system is a long-running process that consumes an unreliable stream, holds state it must be able to recover, and issues instructions to a remote system that may or may not carry them out. Most of the work of going live is building the components that gap between those two descriptions, and almost none of it is strategy code.

Educational material, not trading advice. Algorithmic crypto trading is high-risk and most retail algo traders lose money. Nothing here is a recommendation to run a trading system with real money.

The shape of the system

Six components, and the strategy is the smallest of them.

  1. A market data feed that maintains a current view of prices, handles disconnections, and backfills what it missed.
  2. A feature/state layer that computes whatever the strategy needs from the feed, incrementally, using only completed data.
  3. The strategy, ideally the identical code the backtest ran.
  4. An execution layer that turns target positions into orders, respects venue constraints, and tracks order lifecycle.
  5. A reconciliation layer that periodically compares believed state against the venue’s reported state and stops on disagreement.
  6. Monitoring and control — logs, alerts, health checks, and a kill switch.

If the strategy is more than a small fraction of your code, the operational half is probably missing.

The feed problem

A live feed is not a file, and every difference is a bug waiting to happen. Messages arrive late, arrive twice, arrive out of order, and stop arriving without an error. A socket that is open is not a socket that is delivering data.

The patterns that address this:

  • Treat the current bar as incomplete until its interval has ended and, ideally, until a first message of the next interval confirms it. Acting on a forming bar is the archetypal live-only bug, and it never appears in a backtest.
  • Detect staleness explicitly. Track the time since the last message and treat exceeding a threshold as a fault, not as a quiet period. Silence is indistinguishable from a dead connection unless you measure it.
  • Backfill after every reconnection. The gap between disconnect and reconnect is missing data the strategy’s features need. Fetch the interval from a REST endpoint and merge, deduplicating on timestamp.
  • Make message handling idempotent. Duplicates will arrive. If applying the same message twice changes your state, you have a bug that surfaces under exactly the conditions — high volatility, reconnections — when you can least afford it.

State that survives a restart

The process will die, and what happens next is a design decision you should make deliberately. Market state is usually re-derivable: refetch enough history to warm up the features and continue. Position state is not — the authoritative record lives at the venue, and your belief about it is a cache. That asymmetry is the whole reason reconciliation exists.

Practically: persist orders and intended positions to durable storage at the moment of the decision, not after the response arrives. If the process dies between sending an order and recording it, an unrecorded order exists in the world. Write the intent first, then act, then record the outcome — the ordering a write-ahead log uses, for the same reason.

Reconciliation, and why it’s non-negotiable

Your belief about your position and the venue’s record of it will diverge, and the divergence is dangerous in a specific way: a strategy that thinks it is flat when it is not sizes its next order against the wrong base. Causes include a fill you missed while the socket was down, a partial fill recorded as complete, a manual intervention, and an order accepted after your timeout.

The pattern is a periodic loop that fetches actual positions and open orders, compares them to the internal record, and — this is the important part — halts and alerts on mismatch rather than automatically correcting. Auto-correcting a discrepancy you don’t understand turns a reporting bug into a real trade.

Ordering that tolerates ambiguity

A timeout on an order submission does not tell you whether the order was placed. This is the sharpest difference from a backtest, where an order either exists or does not. The rules that follow:

  • Never blindly resubmit. Query state instead. Retrying a possibly-successful order is how one intended position becomes two.
  • Use client-supplied order IDs where the venue supports them, so “did that go through?” becomes a lookup rather than an inference.
  • Express intent as target position, not as a sequence of trades. A system computing “I want target x, I am at y, trade the difference” is self-healing: a missed order is corrected next cycle. One that emits “buy 1” statelessly compounds every missed or duplicated instruction.
  • Round to venue precision before submitting, reading the rules from market metadata rather than hardcoding — see the exchange abstraction layer, and what it hides.

Limits that exist outside the strategy

The safety layer must not be implemented inside the strategy, because the strategy is the thing that might be wrong. Enforce in the execution layer, independent of any signal:

  • Maximum order size and maximum position per asset, as absolute values, rejecting anything larger regardless of what the strategy asked for.
  • Maximum orders per interval. A logic bug that loops will otherwise generate thousands of orders in seconds. This single limit prevents a large fraction of runaway scenarios.
  • A pre-set drawdown halt, decided while calm and enforced automatically — see position sizing and risk management basics and how maximum drawdown is calculated.
  • A kill switch that flattens or halts on one command, and that you have tested. An untested kill switch is a comforting comment in a config file.
  • Sanity bounds on inputs. A corrupt feed value should not become an order.

Observability

You need to be able to answer “why did it do that?” without a debugger — structured logs of every decision with its inputs, periodic snapshots of positions and equity, alerts on faults rather than on P&L, and a heartbeat so a dead process is noticed by something other than you happening to look.

The one metric worth watching above all others is live-versus-expected divergence: the difference between the fills you got and the fills your model assumed. It corrects your cost assumptions, per modelling transaction costs in a backtest, and a widening divergence warns that conditions or assumptions have changed.

The ordering that works

Backtest, then reconcile against a second independent implementation, then paper trade until the plumbing is proven — see what paper trading catches, and what it misses — then the smallest real size that produces real fills, with every limit above in place and tested.

The summary that matters: the strategy is the part of a live system most likely to be wrong about the market, and the least likely to be the thing that breaks. What breaks is the plumbing, and the plumbing is what a backtest never asked you to write.