Guide

Why your EA works in the backtest but not live

When an EA’s live results drift away from its backtest, the cause is almost always one of nine things. Here is how to find yours by comparing the two trade by trade.

The backtest made money. The live account does not. Before changing a single parameter, find out which of the usual causes you are dealing with, because each one has a different fix and some of them mean the strategy itself is fine.

Start with a fair comparison

Run the Strategy Tester over exactly the period the EA has been live: same broker, same symbol, same inputs, same starting balance, real ticks. Then put the tester’s trade list next to the live trade list. The first trade that differs points to the cause:

  • A trade the tester took and live did not: usually a rejected order, a filter, or a restart.
  • The same trade at a worse price: costs and execution.
  • A trade live took and the tester did not: usually look-ahead in the code, or different data.

The nine usual causes

1. The backtest was modelled optimistically

Open-prices or minute-bar modes, or a fixed spread, make tight stops and scalps look better than they are. Fix: retest on real ticks. If the edge disappears, it was never there. See how to backtest with real tick data.

2. Costs were missing or understated

Spread widening at the rollover and around news, commission, and swaps on held positions. Fix: make sure every cost appears in the backtest. A strategy whose average trade is close to its average cost will not survive live.

3. Execution: slippage, requotes and delay

Market orders fill at the next available price, and a slow connection makes that price worse. Fix: host the EA on a VPS close to the broker’s servers, add a slippage limit, and test with simulated delay.

4. The code looks into the future

Reading the current, unfinished bar as if it were closed, or using an indicator that repaints, produces signals in the backtest that could not have existed live. Fix: make decisions on closed bars, and check every indicator the EA reads for repainting.

5. The settings were fitted to the past

Heavily optimised parameters describe history, not the market. Fix: re-optimise with an out-of-sample period and fewer parameters, and prefer settings whose neighbours also work.

6. Broker differences rejected or resized orders

Minimum stop distances, filling modes, symbol suffixes, lot steps and digits differ between brokers and break order placement silently. Fix: read the Experts and Journal logs, and make the EA log every rejected order with its full context:

OrderLogging.mqh6 lines
1// Log every rejected order with the context you need to diagnose it2if(!trade.Buy(lots, _Symbol, 0.0, sl, tp))3   PrintFormat("BUY failed: retcode=%u (%s) ask=%.5f sl=%.5f tp=%.5f spread=%d lots=%.2f",4               trade.ResultRetcode(), trade.ResultRetcodeDescription(),5               SymbolInfoDouble(_Symbol, SYMBOL_ASK), sl, tp,6               (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD), lots);

7. Server time and sessions shifted

A session filter written for one broker’s server time trades the wrong hours at another, and daylight-saving changes move it twice a year. Fix: define sessions relative to a known timezone, and handle the clock changes.

8. Restarts made the EA forget

After a terminal or VPS restart, an EA that keeps its state only in memory can lose track of open trades, open duplicates, or reset its risk limits. Fix: rebuild state from the terminal’s positions on every start, and save anything the rules depend on.

9. The account is different

Netting instead of hedging, different leverage, or a different account currency changes how positions combine and how lots are sized. Fix: test on the same account type you trade, and make the EA detect the account mode.

When the strategy really is the problem

If the trade lists match and the difference is fully explained by costs, the code is doing its job: the edge is simply smaller than the original backtest suggested. The honest options are to trade less often and target larger moves, to reduce costs, or to stop. That is not a pleasant answer, but it is cheaper than finding out with more live losses.

Checklist

  • Tester run over the live period, on the same broker and account type, with real ticks
  • First differing trade identified
  • Logs checked for rejected orders
  • Costs, delay and swaps included in the test
  • Closed-bar logic and non-repainting indicators confirmed
  • State rebuilt after restarts
Written by

Algorithmic trading developer at Vertex Algorithms. Builds MT4/MT5 Expert Advisors, Python trading bots and Pine Script for traders, prop-firm traders and trading businesses.

Want me to find out why yours differs?

Send the source code, the backtest and a few weeks of live trades. You get the cause in plain English and a fixed quote to fix it.

WhatsApp