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.