Summary: Advanced technical deep dive into MQL4 OrderSend function. Covers slippage calculation, error handling patterns, pending order management, and performance optimization for production EAs.




The MQL4 OrderSend() function remains the foundational trade execution mechanism for MetaTrader 4 EAs. Unlike MQL5's object-oriented approach, MQL4 requires direct handling of trade context, error codes, and slippage management.

1. OrderSend Complete Parameter Breakdown

``cpp
int OrderSend(
string symbol, // symbol to trade
int cmd, // OP_BUY, OP_SELL, OP_BUYLIMIT, etc.
double volume, // lot size
double price, // requested price
int slippage, // allowable slippage in points
double stoploss, // stop loss price (0 if none)
double takeprofit, // take profit price (0 if none)
string comment, // order comment
int magic, // EA identifier
datetime expiration, // pending order expiration
color arrow_color // chart arrow color
);
`

Returns ticket number on success, -1 on failure with
GetLastError() for diagnosis.

2. Professional Slippage Management

Raw slippage as fixed points fails in volatile markets. Use dynamic calculation:

`cpp
int CalculateDynamicSlippage(string symbol, int requestedPoints) {
double spread = MarketInfo(symbol, MODE_SPREAD);
double atr = iATR(symbol, PERIOD_M5, 14, 1);
double point = Point;
double volatilitySlippage = MathCeil(atr / point * 0.05); // 5% of ATR
int slippage = (int)MathMax(requestedPoints, volatilitySlippage);
slippage = (int)MathMax(slippage, spread); // at least current spread
return MathMin(slippage, 50); // cap at 50 points
}
`

3. Production-Grade Error Handling Pattern

`cpp
int SendOrderWithRetry(string symbol, int cmd, double volume, int maxRetries = 3) {
int ticket = -1;
int attempt = 0;
while(attempt < maxRetries) {
RefreshRates(); // critical: update Ask/Bid before each attempt
double price = (cmd == OP_BUY) ? Ask : Bid;
int slippage = CalculateDynamicSlippage(symbol, 3);
ticket = OrderSend(symbol, cmd, volume, price, slippage, 0, 0, "EA", magic, 0, clrNONE);
int error = GetLastError();
if(ticket > 0) return ticket;
// Handle specific recoverable errors
if(error == 138) { Sleep(2000); continue; } // requote
if(error == 146) { Sleep(1000); continue; } // trade context busy
if(error == 130) break; // invalid stops - don't retry
Sleep(500);
attempt++;
}
return -1;
}
`

4. Pending Order Optimization with Expiration

Avoid orphaned pending orders by always setting expiration:

`cpp
bool PlacePendingOrder(int cmd, double price, double stoploss, double takeprofit, double volume, int hoursValid) {
int expiration = (hoursValid > 0) ? TimeCurrent() + hoursValid * 3600 : 0;
int ticket = OrderSend(Symbol(), cmd, volume, price, 3, stoploss, takeprofit, "Pending", magic, expiration, clrNONE);
if(ticket <= 0) {
int err = GetLastError();
Print("Pending order failed: Error ", err, " - ", ErrorDescription(err));
return false;
}
return true;
}
`

5. Performance Optimization: Batch Order Modification

Modifying orders individually creates excessive server round-trips. Batch similar modifications:

`cpp
void ModifyAllStopLosses(double newSL) {
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magic && OrderSymbol() == Symbol()) {
if(OrderType() == OP_BUY && newSL > OrderStopLoss()) {
if(!OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
Print("Modify failed: ", GetLastError());
Sleep(50); // prevent server rate limit
}
}
}
}
}
`

6. Future Function Warning for OrderSend Backtests

Never use
OrderSend() inside start() without RefreshRates() in backtest. This creates a hidden future function because Ask/Bid values freeze at bar open. Always call RefreshRates()` immediately before sending orders.

Reference: MQL4 Documentation (docs.mql4.com/trading/OrderSend), "Expert Advisor Programming for MetaTrader 4" by Andrew R. Young, 2020.