The
OrderSend() function is the heartbeat of any MQL4 Expert Advisor, yet most traders only understand its basic signature. Proper slippage control and error handling separate profitable live EAs from backtest-only relics.1. Complete OrderSend Parameter Deconstruction
``
cpp
int OrderSend(
string symbol, // symbol name
int cmd, // OP_BUY, OP_SELL, OP_BUYLIMIT, etc.
double volume, // lot size
double price, // requested price
int slippage, // maximum slippage in points
double stoploss, // stop loss level
double takeprofit, // take profit level
string comment, // order comment
int magic, // EA identifier
datetime expiration, // pending order expiration
color arrow_color // chart arrow color
);
`
The critical insight: slippage works differently for market orders versus pending orders. For OP_BUY and OP_SELL, slippage is the maximum allowable deviation from the requested price. For pending orders, slippage is ignored entirely.
2. Realistic Slippage Modeling For Backtest Accuracy
A common flaw in EA programming is using fixed slippage. Market conditions demand dynamic calculation:
`cpp
int CalculateDynamicSlippage(string symbol) {
double spread = MarketInfo(symbol, MODE_SPREAD);
double atr = iATR(symbol, PERIOD_M5, 14, 1);
double volatilitySlippage = MathCeil(atr / Point() 0.1);
int spreadSlippage = (int)MathCeil(spread 1.5);
return MathMax(3, (int)MathMax(volatilitySlippage, spreadSlippage));
}
`
3. Production-Grade OrderSend Wrapper With Retry Logic
The most common live trading failure is ERR_TRADE_RETRY (error 4) due to price changes between request and execution. This wrapper handles it:
`cpp
int SafeOrderSend(string sym, int cmd, double vol, double price, int slip, double sl, double tp, string cmt, int magic, datetime exp, color col) {
int ticket = -1;
int attempts = 0;
int maxAttempts = 5;
int error = 0;
while(attempts < maxAttempts && ticket < 0) {
ticket = OrderSend(sym, cmd, vol, price, slip, sl, tp, cmt, magic, exp, col);
error = GetLastError();
if(error == ERR_NO_ERROR) break;
// Handle requote and busy errors
if(error == ERR_REQUOTE || error == ERR_TRADE_CONTEXT_BUSY) {
Sleep(50);
attempts++;
continue;
}
// Handle price changed: refresh and retry
if(error == ERR_PRICE_CHANGED) {
RefreshRates();
if(cmd == OP_BUY) price = Ask;
else if(cmd == OP_SELL) price = Bid;
attempts++;
continue;
}
// Unknown errors: log and break
if(error != ERR_NO_ERROR) {
Print("OrderSend failed with error ", error, " after ", attempts, " attempts");
break;
}
attempts++;
}
return ticket;
}
`
4. Error Code Mapping For Diagnostic
Implement this error classification to quickly identify the root cause:
`cpp
string OrderSendErrorDescription(int errorCode) {
switch(errorCode) {
case ERR_TRADE_NOT_ALLOWED: return "Trading disabled";
case ERR_MARKET_CLOSED: return "Market closed";
case ERR_NOT_ENOUGH_MONEY: return "Insufficient margin";
case ERR_PRICE_CHANGED: return "Price changed - need RefreshRates";
case ERR_OFF_QUOTES: return "No quotes - check connection";
case ERR_BROKER_BUSY: return "Broker busy - increase retry delay";
case ERR_REQUOTE: return "Requote - increase slippage";
case ERR_TRADE_CONTEXT_BUSY: return "Context busy - wait or use critical section";
case ERR_NO_ERROR: return "Success";
default: return "Unknown error: " + (string)errorCode;
}
}
`
5. Avoiding The Future Function Trap In OrderSend Backtesting
When backtesting, never use OrderSend() with Volume[0] or Close[0] inside the same tick that generated the signal. This creates look-ahead bias. Always use the previous bar's close:
`cpp
// Dangerous - future function
if(Close[1] > SMA && Close[0] > Close[1])
OrderSend(...); // Uses current incomplete bar
// Correct - no look-ahead
if(Close[1] > SMA && Close[2] > Close[1])
OrderSend(...); // Only confirmed data
`
6. Complete Open Position Example With All Best Practices
`cpp
int OpenBuyPosition(double lot, int magic) {
double ask = Ask;
double spread = MarketInfo(Symbol(), MODE_SPREAD);
int slippage = CalculateDynamicSlippage(Symbol());
double sl = ask - spread 20 Point();
double tp = ask + spread 40 Point();
int ticket = SafeOrderSend(Symbol(), OP_BUY, lot, ask, slippage, sl, tp, "EA_Trade", magic, 0, clrNONE);
if(ticket > 0) {
Print("Position opened: ", ticket, " at ", ask, " slippage: ", slippage);
} else {
Print("Failed to open position. Error: ", OrderSendErrorDescription(GetLastError()));
}
return ticket;
}
``Reference: MQL4 Documentation (docs.mql4.com/trading/OrderSend), Pardo, Robert. "The Evaluation and Optimization of Trading Strategies." Wiley Trading, 2008.