DEV Community

Cover image for Backtest vs live trading: why they must share one code path
Weston Carnes
Weston Carnes

Posted on • Originally published at stellarbytecapital.com

Backtest vs live trading: why they must share one code path

Cross-post. Original: stellarbytecapital.com/blog/backtest-live-trading-same-code-path

Almost every quant has shipped a strategy that looked brilliant in backtest and quietly bled money live. The instinct is to blame the market, or overfitting, or luck. The real culprit is usually more boring and more fixable: your backtest and your live system are running different code.

When the code differs, the backtest is testing something your live trader will never do. The equity curve is fiction — not because the strategy is bad, but because you never actually tested the strategy you deployed.

How the two paths quietly diverge

  • Lookahead bias. The backtest can "see" the full bar because the data is already there. Live, that data doesn't exist yet. Using the close of the current bar to decide a trade at its open inflates results and never happens live.
  • if isBacktest branches. The moment your strategy behaves differently in backtest vs live, you've forked into two strategies. Each branch is under-tested by definition.
  • Different data handling. Backtest reads clean, adjusted history. Live gets a raw, delayed, occasionally-out-of-order feed.
  • Hidden state and time. A strategy that calls time.Now(), reads a file, or hits the network behaves differently depending on when and where it runs.

The fix: strategy isomorphism

The principle is simple to state and strict to enforce: backtest and live must call the exact same strategy implementation — the same function, byte for byte.

Concretely, the strategy is a single pure function — call it Step() — that takes the current market state and returns a decision. It's called identically by two adapters: a backtest adapter that feeds it historical bars one at a time, and a live adapter that feeds it real-time bars one at a time. Neither adapter is allowed to change what Step() does.

If a strategy behaves differently in backtest and live, that difference is a bug in your harness — not a property of the market.

What isomorphism forbids inside the strategy

To make one function safe to run in both worlds, the strategy body must be pure. It is not allowed to:

  • Branch on whether it's a backtest (no if isBacktest, ever).
  • Read the clock (time.Now()) — time is passed in as data.
  • Touch the network, database, or filesystem.
  • Reach beyond the current and past state it was given — no peeking at future bars.

Everything the strategy needs is injected. Everything it produces is a decision, not a side effect. Placing orders, logging, and persistence live in the adapters.

What this buys you

  • What you test is what you ship. A backtested edge survives contact with production, or it never showed up.
  • Lookahead bias becomes structurally impossible — the strategy simply isn't given future data.
  • Optimization is trustworthy. When you tune parameters, you're optimizing the real strategy, not a backtest-only fantasy.

What isomorphism does NOT fix

Sharing one code path removes a class of self-inflicted error. It does not make a backtest realistic. You still have to model fills and slippage, latency, fees and funding, and regime change. Isomorphism is the foundation, not the whole house — but without it, every other realism effort sits on sand.


We're Xingyao Byte — building quant trading systems, secure AI-execution layers, and payment platforms. Remote, async-first → stellarbytecapital.com

Top comments (0)