The Production Layer Your Backtest Never Tests: OrderSend Retries and Restart-Safe State

The Production Layer Your Backtest Never Tests: OrderSend Retries and Restart-Safe State

26 June 2026, 10:24
Boris Armenteros
0
26

A clean backtest proves your entry and exit rules. It does not prove your EA will survive a live night. Those are two different things, and the gap between them is where most EAs quietly fail at 2am.

The backtester feeds your code a tidy, sequential stream of ticks. Every order fills. The terminal never restarts. The connection never drops. The spread never widens past your stop level. Live trading does all of these, often on the same night, and the code that handles them is exactly the code a backtest can never exercise. That code is the production layer, and it is what separates an EA that compiles from one that holds an account.

Here are three places it breaks, with the fix patterns we apply.

1. OrderSend with no retry

The single most common live failure: an order is rejected for a requote or off-quotes, the EA logs nothing useful, and the position that was supposed to open never does. The strategy is now flat when it thinks it is long.

A naive call sends once and moves on:

// Fragile — one shot, no recovery
int ticket = OrderSend(_Symbol, OP_BUY, lots, Ask, 3, sl, tp);

A production version re-reads the price, respects the broker stop level, and retries on the recoverable errors only:

int SendWithRetry(int type, double lots, double sl, double tp, int maxTries=3)
{
   for(int attempt = 0; attempt < maxTries; attempt++)
   {
      RefreshRates();
      double price = (type == OP_BUY) ? Ask : Bid;
      int ticket = OrderSend(_Symbol, type, lots, price, 5, sl, tp);
      if(ticket >= 0)
         return ticket;

      int err = GetLastError();
      // Retry only on transient, recoverable errors
      if(err == ERR_REQUOTE || err == ERR_PRICE_CHANGED ||
         err == ERR_OFF_QUOTES || err == ERR_TRADE_CONTEXT_BUSY)
      {
         Sleep(200);
         continue;
      }
      printf("%s: non-recoverable error %d, aborting", __FUNCTION__, err);
      break;  // Don't blindly retry a bad request
   }
   return -1;
}

The detail that matters: retry only on transient errors. Looping on a non-recoverable error (invalid stops, not enough money) just hammers the broker with the same bad request.

2. No state across a restart

Terminals restart. The VPS reboots, the platform updates, the user closes it by accident. If your EA holds its position state only in memory, a restart wipes it, and on the next tick the EA either opens a second position on top of the one it forgot, or it manages a trade it no longer recognizes.

The fix is not glamorous: persist the minimum state you need to reconcile, and reconcile on init instead of assuming a clean slate.

int OnInit()
{
   // Don't assume flat — discover reality from the terminal
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS) &&
         OrderSymbol() == _Symbol && OrderMagicNumber() == MagicNumber)
      {
         g_activeTicket = OrderTicket();   // adopt the existing position
         printf("%s: recovered open ticket %d on init", __FUNCTION__, g_activeTicket);
      }
   }
   return INIT_SUCCEEDED;
}

An EA that reconciles against OrdersTotal()  on every OnInit()  cannot double up after a restart, because it never assumes it is flat.

3. Ignoring the broker stop level

Your backtest happily places a stop 2 points from price. A live broker with a 30-point minimum stop level rejects it, and if the EA does not check, the order fails silently. Read MarketInfo(_Symbol, MODE_STOPLEVEL)  and clamp your stop before sending, so what you validate is what the broker will actually accept.

The takeaway

Compilation checks syntax. A backtest checks historical logic. Neither one checks survival. If you only test the part the backtester can reach, you are shipping half an EA and discovering the other half live, with real money. Before you trust an automated version of a working strategy, exercise it against the conditions a backtest never can: rejected orders, restarts mid-trade, and broker constraints your demo account hides.