Summary: 面向进阶用户的MQL4 OrderSend函数深度教程,涵盖滑点计算公式、返回码分析、自动重试机制以及完整的开仓封装函数,帮助开发者编写生产级EA交易代码,避免常见错误导致的订单失败。




`OrderSend()`函数是任何MQL4交易EA的核心,但大多数实现忽略了正确的错误处理和滑点控制。一个健壮的`OrderSend()`封装是专业EA与业余EA的分水岭。

1. 函数签名与返回值分析

```cpp
int OrderSend(
string symbol, // 交易品种
int cmd, // 操作类型
double volume, // 手数
double price, // 开仓价格
int slippage, // 允许滑点(点数)
double stoploss, // 止损价位
double takeprofit, // 止盈价位
string comment, // 订单注释
int magic, // EA标识码
datetime expiration, // 挂单过期时间
color arrow_color // 图表箭头颜色
);
```

返回值:成功返回订单号(ticket),失败返回-1。调用后必须立即检查`GetLastError()`。

2. 滑点控制公式

最大允许价格偏差 = `Ask ± slippage * Point`。以买单为例:
```cpp
double currentAsk = Ask;
double maxAcceptableAsk = currentAsk + slippage * Point;
if(maxAcceptableAsk >= currentAsk) {
ticket = OrderSend(Symbol(), OP_BUY, lot, currentAsk, slippage, 0, 0, "EA", magic);
}
```

3. 生产级OrderSend封装函数

```cpp
int SendOrderWithRetry(int cmd, double volume, double price, int slippage,
double sl, double tp, int magic, int maxRetries = 3) {
int ticket = -1;
int retryCount = 0;

while(retryCount < maxRetries) {
ticket = OrderSend(Symbol(), cmd, volume, price, slippage, sl, tp,
"SecureEA", magic, 0, clrNONE);
int error = GetLastError();

if(ticket > 0) {
Print("订单发送成功,订单号:", ticket);
return ticket;
}

Print("第", retryCount + 1, "次尝试失败。错误码:", error, " - ", ErrorDescription(error));

// 针对不同错误码采用不同退避策略
switch(error) {
case ERR_SERVER_BUSY: // 134
case ERR_NO_CONNECTION: // 134
case ERR_TRADE_CONTEXT_BUSY: // 146
case ERR_PRICE_CHANGED: // 135
Sleep(1000 * (retryCount + 1)); // 指数退避
RefreshRates(); // 刷新市场数据
break;
case ERR_INVALID_PRICE: // 138
price = (cmd == OP_BUY) ? Ask : Bid; // 重新计算价格
RefreshRates();
break;
default:
Print("致命错误,不再重试。");
return -1;
}
retryCount++;
}
return -1;
}
```

4. 错误码参考表

| 错误码 | 常量名 | 含义 | 恢复操作 |
|--------|--------|------|----------|
| 1 | ERR_NO_RESULT | 无错误 | 继续执行 |
| 2 | ERR_COMMON_ERROR | 通用错误 | 刷新报价 |
| 129 | ERR_INVALID_PRICE | 无效价格 | 重新计算 |
| 130 | ERR_INVALID_STOPS | 无效止损止盈 | 调整SL/TP距离 |
| 131 | ERR_INVALID_TRADE_VOLUME | 无效手数 | 标准化手数 |
| 134 | ERR_NOT_ENOUGH_MONEY | 资金不足 | 减小手数 |

5. 手数标准化函数

```cpp
double NormalizeLot(double requestedLot) {
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double stepLot = MarketInfo(Symbol(), MODE_LOTSTEP);

double normalized = MathFloor(requestedLot / stepLot) * stepLot;
normalized = MathMax(minLot, MathMin(maxLot, normalized));

return NormalizeDouble(normalized, 2);
}
```

6. 完整的开仓模式

```cpp
bool OpenBuyOrder(double lot, int slippagePoints, double slPoints, double tpPoints, int magic) {
RefreshRates();
double price = Ask;
double stoploss = (slPoints > 0) ? price - slPoints * Point : 0;
double takeprofit = (tpPoints > 0) ? price + tpPoints * Point : 0;
double normalizedLot = NormalizeLot(lot);

if(stoploss > 0 && price - stoploss < MarketInfo(Symbol(), MODE_STOPLEVEL) * Point) {
Print("止损距离市场价格过近");
return false;
}

int ticket = OrderSend(Symbol(), OP_BUY, normalizedLot, price, slippagePoints,
stoploss, takeprofit, "EA_Buy", magic, 0, clrGreen);

if(ticket < 0) {
Print("OrderSend失败。错误码:", GetLastError());
return false;
}

Print("买单已开。订单号:", ticket, " 开仓价:", price);
return true;
}
```

参考来源:MQL4官方文档《OrderSend》(docs.mql4.com/trading/OrderSend);Kovalyov, Alexander.《MQL4交易编程》2019年版。