Problem Description
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
RefreshRates(): This is crucial inside the retry loop. If you get a requote, theAskandBidvariables stored in the terminal are stale. CallingRefreshRates()updates them to the current market value before you tryOrderSendagain.OrderSend(... slippage ...): This integer sets the tolerance. If you set this to0, you are requesting the exact price. In volatile markets, this will result in frequent requotes (Error 138). Setting it to30(on a 5-digit broker) allows a 3 pip deviation.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).