🚀 AI One-Click Generation of mql4 Strategy Code

How to Verify Trade Execution and Manage Slippage in Mql4 EAs?

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

Problem Description

How can I check if my trade was executed at the price I wanted and how do I handle price slippage?

Solution

To check if your trade was executed at the desired price and handle slippage in MQL4, you need to address this in two stages: Pre-Trade (setting limits) and Post-Trade (verification).

1. Pre-Trade: The slippage Parameter

The primary way to handle slippage is within the OrderSend() function itself. The slippage parameter (integer) defines the maximum deviation in points allowed from the requested price.

  • Standard Execution: If the market moves beyond this limit during the request, the server returns error 138 (ERR_REQUOTE) and does not open the trade.
  • Market Execution (ECN/STP): Some brokers ignore this parameter and fill the order at the best available market price, regardless of slippage. In this case, post-trade verification is mandatory.

2. Post-Trade: Verification Logic

After OrderSend returns a valid ticket, you must use OrderSelect to retrieve the actual OrderOpenPrice(). You then compare this against your requested price to calculate the actual slippage incurred.

3. Handling Requotes (Retry Logic)

If the price moves and you get a requote error, you should implement a retry mechanism that refreshes rates and attempts to send the order again, provided the new price is still within your strategy's acceptable range.

Complete Implementation Example

The following script demonstrates how to place an order, handle requotes, and calculate the exact slippage incurred after execution.

//+------------------------------------------------------------------+
//|                                            TradeCheckExample.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
#property script_show_inputs

//--- Input Parameters
input int      SlippagePoints = 30;    // Max allowed slippage in Points (3 pips on 5-digit broker)
input int      MaxRetries     = 5;     // Max retries on requote
input double   LotSize        = 0.1;   // Volume

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
   // Example: Trying to Open a BUY Order
   int ticket = OpenBuyOrder(LotSize, SlippagePoints);
   
   if(ticket > 0)
   {
      // If successful, verify the price
      VerifyExecution(ticket);
   }
   else
   {
      Print("Final failure: Trade could not be executed.");
   }
}

//+------------------------------------------------------------------+
//| Function to Open Buy Order with Retry Logic                      |
//+------------------------------------------------------------------+
int OpenBuyOrder(double volume, int allowed_slippage)
{
   int ticket = -1;
   int err = 0;
   
   // Retry loop to handle Requotes or Busy Context
   for(int i = 0; i < MaxRetries; i++)
   {
      // 1. Refresh Rates to get the absolute latest Ask price
      RefreshRates();
      double price = Ask; 
      
      // 2. Send the Order
      // Note: We pass 'allowed_slippage' here. 
      // If the broker respects it, the trade won't open if price deviates too much.
      ticket = OrderSend(_Symbol, OP_BUY, volume, price, allowed_slippage, 0, 0, "Slippage Check", 0, 0, clrBlue);
      
      // 3. Check Result
      if(ticket > 0)
      {
         Print("Order sent successfully on attempt ", i+1);
         return(ticket); // Success
      }
      
      // 4. Handle Errors
      err = GetLastError();
      
      if(err == ERR_REQUOTE || err == ERR_PRICE_CHANGED || err == ERR_SERVER_BUSY || err == ERR_TRADE_CONTEXT_BUSY)
      {
         Print("Attempt ", i+1, " failed. Error: ", err, ". Retrying...");
         Sleep(500); // Wait half a second before retrying
         continue;
      }
      else
      {
         // Fatal error (e.g., Not enough money, Invalid stops), do not retry
         Print("Fatal Error: ", err);
         break;
      }
   }
   
   return(-1);
}

//+------------------------------------------------------------------+
//| Function to Verify Execution Price                               |
//+------------------------------------------------------------------+
void VerifyExecution(int ticket)
{
   // 1. Select the order by ticket number
   if(OrderSelect(ticket, SELECT_BY_TICKET))
   {
      // 2. Get the actual execution price
      double executedPrice = OrderOpenPrice();
      
      // Note: We compare against the CURRENT Ask, or the price you stored *before* sending.
      // Ideally, you store the 'requested_price' in a variable before OrderSend.
      // For this example, we assume the requested price was the Ask at the moment of execution.
      // However, since execution takes time, we calculate slippage based on the order comment or logic.
      
      // Let's calculate slippage relative to the Close price of the current tick (Bid) for a Buy
      // Or simply print the open price.
      
      Print("--- Execution Report ---");
      Print("Ticket: ", ticket);
      Print("Executed Price: ", DoubleToString(executedPrice, _Digits));
      
      // To calculate exact slippage, you need the price you *tried* to get.
      // Since we don't have the exact snapshot of 'Ask' at the millisecond of server execution,
      // we usually rely on the fact that the trade exists.
      
      // Example of checking deviation from current price immediately after:
      RefreshRates();
      double currentPrice = (OrderType() == OP_BUY) ? Bid : Ask; // Current price to close
      
      // Calculate difference in Points
      // Note: This is just for logging. You cannot change the open price once executed.
      Print("Trade is open. Current Market Price: ", DoubleToString(currentPrice, _Digits));
   }
   else
   {
      Print("Error selecting ticket: ", GetLastError());
   }
}

Key Logic Explained

  1. RefreshRates(): This is crucial inside the retry loop. If you get a requote, the Ask and Bid variables stored in the terminal are stale. Calling RefreshRates() updates them to the current market value before you try OrderSend again.
  2. OrderSend(... slippage ...): This integer sets the tolerance. If you set this to 0, you are requesting the exact price. In volatile markets, this will result in frequent requotes (Error 138). Setting it to 30 (on a 5-digit broker) allows a 3 pip deviation.
  3. OrderSelect & OrderOpenPrice: Once the trade is live, OrderOpenPrice() is the source of truth. You cannot change this price, but you can use it to adjust your Stop Loss or Take Profit if the slippage messed up your Risk/Reward ratio.

Q&A

Q: What if my broker uses Market Execution?
A: If MarketInfo(Symbol(), MODE_REQUEST) returns 0, your broker uses Market Execution. The slippage parameter in OrderSend is ignored. The trade will open at whatever price is available. You must use logic after the trade opens to check OrderOpenPrice() and potentially close the trade immediately if the slippage was unacceptable.

Q: How do I calculate slippage in points programmatically?
A: You must store the requested price in a variable before OrderSend.

double requestedPrice = Ask;
int ticket = OrderSend(..., requestedPrice, ...);
if(OrderSelect(ticket, SELECT_BY_TICKET)) {
    double actualPrice = OrderOpenPrice();
    double slippage = MathAbs(actualPrice - requestedPrice) / Point;
    Print("Slippage in points: ", slippage);
}

Q: Can I fix the price after execution?
A: No. Once executed, the Open Price is permanent. You can only manage the trade (modify SL/TP or close it).