🚀 AI One-Click Generation of mql4 Strategy Code

How to Implement a Robust OrderSend() Retry Loop in Mql4

mql4 | Published on: 2025/12/12
The following content is generated by EasyQuant .

Problem Description

My trade sometimes fails because of a requote. How can I make my robot automatically try to place the trade again a few times?

Solution

To handle requotes effectively in MQL4, you need to wrap your OrderSend function in a loop. This loop must perform three specific actions:

  1. RefreshRates(): Update the local copy of prices (Ask/Bid) to the latest server values.
  2. Update Price: Use the new Ask or Bid price for the order.
  3. Sleep(): Wait a short moment before retrying to avoid spamming the server.

Here is a robust, reusable function OrderSendWithRetry that you can use to replace your standard OrderSend calls.

MQL4 Code Implementation

//+------------------------------------------------------------------+
//|                                                  RetryLogic.mq4  |
//|                        Copyright 2023, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

//--- Input parameters
input int      MagicNumber = 12345;
input double   Lots        = 0.1;
input int      StopLoss    = 50; // In points
input int      TakeProfit  = 100; // In points
input int      MaxRetries  = 5;   // Number of times to retry on requote
input int      RetryDelay  = 3000; // Time to wait between retries in milliseconds

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // --- Example Trading Logic ---
   // This is just a placeholder condition to demonstrate usage.
   // Replace this with your actual strategy logic.
   
   if(OrdersTotal() == 0) 
   {
      // Example: Open a Buy Order
      int ticket = OrderSendWithRetry(Symbol(), OP_BUY, Lots, 3, StopLoss, TakeProfit, "My Buy Order", MagicNumber, 0, clrGreen);
      
      if(ticket > 0)
      {
         Print("Order opened successfully. Ticket: ", ticket);
      }
      else
      {
         Print("Failed to open order after retries.");
      }
   }
}

//+------------------------------------------------------------------+
//| Custom Function: OrderSend with Requote Handling                 |
//+------------------------------------------------------------------+
int OrderSendWithRetry(string symbol, int cmd, double volume, int slippage, int sl_points, int tp_points, string comment, int magic, datetime expiration, color arrow_color)
{
   int ticket = -1;
   double price, sl, tp;
   
   // Loop through the maximum number of allowed retries
   for(int i = 0; i <= MaxRetries; i++)
   {
      // 1. Refresh rates to get the absolute latest Bid/Ask
      if(!RefreshRates())
      {
         Print("RefreshRates failed. Retrying...");
         Sleep(RetryDelay);
         continue;
      }

      // 2. Determine the correct price based on order type
      if(cmd == OP_BUY)
      {
         price = Ask;
      }
      else if(cmd == OP_SELL)
      {
         price = Bid;
      }
      else
      {
         // For pending orders, price is usually passed explicitly. 
         // This example focuses on Market Orders (Instant Execution).
         Print("This function is optimized for Market Orders (OP_BUY/OP_SELL)");
         return(-1);
      }

      // 3. Calculate Stop Loss and Take Profit based on the NEW price
      double point = MarketInfo(symbol, MODE_POINT);
      
      if(sl_points > 0)
      {
         if(cmd == OP_BUY)  sl = NormalizeDouble(price - sl_points * point, Digits);
         else               sl = NormalizeDouble(price + sl_points * point, Digits);
      }
      else sl = 0;

      if(tp_points > 0)
      {
         if(cmd == OP_BUY)  tp = NormalizeDouble(price + tp_points * point, Digits);
         else               tp = NormalizeDouble(price - tp_points * point, Digits);
      }
      else tp = 0;

      // 4. Attempt to send the order
      ticket = OrderSend(symbol, cmd, volume, price, slippage, sl, tp, comment, magic, expiration, arrow_color);

      // 5. Check the result
      if(ticket > 0)
      {
         // Order opened successfully, break the loop
         return(ticket);
      }
      else
      {
         int err = GetLastError();
         
         // Check specifically for Requote (Error 138) or Price Changed (Error 129)
         if(err == ERR_REQUOTE || err == ERR_INVALID_PRICE || err == ERR_OFF_QUOTES)
         {
            Print("Error ", err, ": Requote/Price Changed. Attempt ", i+1, " of ", MaxRetries);
            Sleep(RetryDelay); // Wait before trying again
         }
         else
         {
            // If it's a fatal error (e.g., Not Enough Money, Trade Disabled), stop trying immediately
            Print("Fatal Error ", err, ". Stopping retries.");
            return(-1);
         }
      }
   }

   return(-1); // Failed after max retries
}
//+------------------------------------------------------------------+

Key Components Explained

  1. RefreshRates(): This is the most critical part. If you get a requote, it means the price you sent is old. Calling this function forces the EA to fetch the current market tick data. Without this, you would just keep sending the old, invalid price in every loop iteration.
  2. Dynamic Price Calculation: Inside the loop, we recalculate price = Ask (for Buy) or price = Bid (for Sell) after refreshing rates. We also recalculate SL and TP relative to this new price to ensure valid distances.
  3. Error Filtering: The code checks GetLastError().
    • If the error is 138 (Requote) or 129 (Invalid Price), it waits and retries.
    • If the error is something fatal like 134 (Not Enough Money), it breaks the loop immediately to prevent wasting time or freezing the EA.
  4. Sleep(RetryDelay): This pauses execution (e.g., for 3 seconds) to allow the market volatility to settle slightly before sending the next request.

Q&A

Q: Does this work for Pending Orders (Buy Limit, Sell Stop)?
A: The logic provided above is specifically optimized for Market Orders (Instant Execution). Pending orders usually fail due to invalid stops (too close to price) rather than requotes. Requotes are a phenomenon of entering the market at the current price.

Q: What is the slippage parameter?
A: Slippage is the allowed deviation from the requested price. If you set slippage to 3 points, and the price moves by 2 points during transmission, the server will accept the trade without a requote. Increasing slippage is the first line of defense, while the retry loop is the second.

Q: Why do I need to recalculate Stop Loss and Take Profit inside the loop?
A: If the market price has moved (causing the requote), your original Stop Loss and Take Profit levels might now be too close to the new entry price, causing an ERR_INVALID_STOPS (130) error. Recalculating them ensures they maintain the correct distance from the new entry price.