The `OrderSend()` function is the heartbeat of any MQL4 Expert Advisor. Yet many advanced users still misuse its parameters or ignore return value validation, leading to hidden backtest inaccuracies and live trading failures. This guide covers production-level usage, error handling patterns, and slippage control.
1. Function Signature & Each Parameter
```cpp
int OrderSend(
string symbol, // symbol name
int cmd, // trade operation
double volume, // lot size
double price, // order price
int slippage, // allowable 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 // arrow color on chart
);
```
Critical nuance: `slippage` is measured in points, not pips. For a 5-digit broker, 10 points = 1 pip. Use `Point` conversion: `slippage = 10 * Point`.
2. Return Value & Validation Logic
`OrderSend()` returns ticket number on success, -1 on failure. Never assume success. Immediately check ticket and call `GetLastError()`.
Production pattern:
```cpp
int ticket = OrderSend(Symbol(), OP_BUY, lot, Ask, slippage, stopLoss, takeProfit, "EA", magic, 0, clrNONE);
if(ticket < 0) {
int error = GetLastError();
Print("OrderSend failed. Error: ", error, " | ", ErrorDescription(error));
// Optional: retry logic with exponential backoff
return;
}
```
Common error codes: 130 (invalid stops), 148 (too many orders), 146 (trade context busy). For error 146, use `Sleep(50)` and retry up to 5 times.
3. Slippage Mathematical Model
Slippage is not just a safety buffer. It affects fill probability. For market orders, required condition:
`Ask - price <= slippage * Point` for sell orders. For buy orders: `price - Bid <= slippage * Point`.
To compute statistically safe slippage from historical data:
`slippage = max(3, ceil( (avg_slippage + 2 * stddev_slippage) / Point ))`
where `avg_slippage` and `stddev_slippage` derived from tick data.
4. Avoiding Future Function in Backtest Using OrderSend
A common mistake: using `OrderSend()` inside a loop that references `Close[0]` without verifying bar completeness. This creates a look-ahead bias. Correct approach:
```cpp
static datetime lastBarTime = 0;
if(Time[0] != lastBarTime) {
lastBarTime = Time[0];
// Place orders only on new bar to avoid intra-bar future leakage
OrderSend(...);
}
```
This ensures backtest accuracy by aligning with real-time bar closure.
5. Pending Orders & Expiration
MQL4 allows `expiration` parameter for pending orders (OP_BUYLIMIT, OP_SELLLIMIT, OP_BUYSTOP, OP_SELLSTOP). Formula for expiration datetime:
`expiration = TimeCurrent() + expiry_seconds`
Maximum expiration is 1 month from current time. Values beyond that truncate silently.
Example with auto-cancel:
```cpp
datetime expiry = TimeCurrent() + 3600; // 1 hour
int ticket = OrderSend(Symbol(), OP_BUYLIMIT, 0.1, buyLimitPrice, 3, 0, 0, "Limit", magic, expiry, clrBlue);
```
6. OrderSend vs OrderSendAsync (MQL5 mention)
MQL4 does not have asynchronous order sending. Every `OrderSend()` blocks until broker response or timeout. In MQL5, `OrderSendAsync()` exists but with different semantics. When migrating to MQL5, replace `OrderSend()` with `CTrade::PositionOpen()` as covered in cross-platform migration notes.
7. Performance Impact on Optimization
During EA parameter optimization, `OrderSend()` becomes the bottleneck if each call includes heavy computation. Pre-calculate stoploss/takeprofit outside the call:
```cpp
double sl = Ask - stopLossPoints * Point;
double tp = Ask + takeProfitPoints * Point;
int ticket = OrderSend(Symbol(), OP_BUY, lot, Ask, slippage, sl, tp, comment, magic, 0, clrNONE);
```
Reducing operations inside `OrderSend` context improves MT4 optimization speed by up to 30%.
Reference