MQL5 常见运行时异常解析与处理
一、引言
在编写 MQL5 代码时,运行时异常可能导致 EA 逻辑混乱或交易失败。本文聚焦实战中常见的运行时异常,通过案例重现、原因分析及解决方案,帮助开发者提升代码健壮性。
二、常见运行时异常及处理
1. 除数为零异常(Zero Divide)
异常现象
- 错误本质:除法运算中除数为零(如止损价与进场价相等导致价差为零)。
代码重现
cpp
// 错误示例:止损价等于进场价,导致价差为零
double entryPrice = 1.2345;
double stopLossPrice = 1.2345; // 故意设置相等
double risk = 100;
double slDistance = entryPrice - stopLossPrice; // 结果为 0
double lots = risk / slDistance; // 触发零除
解决方案
- 提前判断除数非零:cpp
double slDistance = entryPrice - stopLossPrice; if (slDistance <= 0) { Print("止损价差不能为零或负数"); return; // 终止后续下单逻辑 }
2. 数组下标越界(Array Out of Range)
异常现象
- 错误本质:访问数组时索引超出有效范围(如获取 RSI 数据时索引超过实际获取的 K 线数量)。
代码重现
cpp
ArraySetAsSeries(RsiBuffer,true);
// 错误示例:获取 1 根 K 线数据,却访问索引 1
CopyBuffer(rsiHandle, 0, 0, 1, rsiBuffer); // 获取 1 个数据(索引 0)
Print(rsiBuffer[1]); // 索引 1 越界
3. 无效成交量(Invalid Volume,代码 10014)
异常现象
- 错误本质:下单手数不符合平台规范(如手数精度超出
VolumeStep
限制)。 - 示例:平台最小手数为 0.01,却尝试下 0.012 手。
代码重现
cpp
#include <Trade\trade.mqh>
CTrade trade;
double et = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + 100*_Point;
double sl = et -100 *_Point;
double tp = et +100 *_Point;
double lots = 0.012; // 超出平台允许的手数精度
lots = MathRound(lots/lotStep) * lotStep;
trade.BuyStop(lots, et,_Symbol, sl,tp,ORDER_TIME_GTC);
解决方案
- 根据平台步长规范化手数:
cpp
double lotStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
lots = MathRound(lots/lotStep) * lotStep;
trade.BuyStop(lots, et,_Symbol, sl,tp,ORDER_TIME_GTC);
4. 无效价格(Invalid Price,代码 10015)
异常现象
- 错误本质:下单价格不符合交易逻辑(如买单突破价低于当前价格)。
cpp
double et = SymbolInfoDouble(_Symbol,SYMBOL_ASK) - 100*_Point;
5. 无效止损/止盈(Invalid Stops,代码 10016)
异常现象
- 错误本质:止损/止盈价格方向错误(如买单止损价高于进场价,止盈低于进场价)。
cpp
double et = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + 100*_Point;
double sl = et +100 *_Point;
double tp = et -100 *_Point;
6. 市场关闭(Market Closed,代码 10018)
异常现象
- 错误本质:在非交易时段下单(如凌晨 0 点市场休市)。
解决方案
- 获取交易时段并校验:
cpp
if(!isMarketOpen(_Symbol, TimeCurrent())){
Print("市场休市,无法下单");
return;
}
- 具体实现:
cpp
bool isMarketOpen(string symbol, datetime time)
{
static string lastSymbol = "";
static bool isOpen = false;
static datetime sessionStart = 0;
static datetime sessionEnd = 0;
if (symbol == lastSymbol && sessionEnd > sessionStart)
{
if ((isOpen && time >= sessionStart && time <= sessionEnd) || (!isOpen && time > sessionStart && time < sessionEnd))
return isOpen;
}
lastSymbol = symbol;
MqlDateTime mtime;
TimeToStruct(time, mtime);
datetime seconds = mtime.hour * 3600 + mtime.min * 60 + mtime.sec;
mtime.hour = 0;
mtime.min = 0;
mtime.sec = 0;
datetime dayStart = StructToTime(mtime);
datetime dayEnd = dayStart + 86400;
datetime fromTime;
datetime toTime;
sessionStart = dayStart;
sessionEnd = dayEnd;
for (int session = 0;; session++)
{
if (!SymbolInfoSessionTrade(symbol, (ENUM_DAY_OF_WEEK)mtime.day_of_week, session, fromTime, toTime))
{
sessionEnd = dayEnd;
isOpen = false;
return isOpen;
}
if (seconds < fromTime)
{ // not inside session
sessionEnd = dayStart + fromTime;
isOpen = false;
return isOpen;
}
if (seconds > toTime)
{ // maybe a later session
sessionStart = dayStart + toTime;
continue;
}
// at this point must be inside a session
sessionStart = dayStart + fromTime;
sessionEnd = dayStart + toTime;
isOpen = true;
return isOpen;
}
return false;
}
7. 保证金不足(Not Enough Money)
异常现象
- 错误本质:账户可用保证金不足以开仓(马丁策略容易出现)。
解决方案
- 下单前校验保证金:
cpp
double margin;
double ask =SymbolInfoDouble(_Symbol,SYMBOL_ASK);
bool result = OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,lots,ask,margin);
if(result && margin> AccountInfoDouble(ACCOUNT_MARGIN_FREE)){
Print("开仓保证金不足!");
return;
}
8. 小于平台设置的最小间距
异常现象
- SYMBOL_TRADE_STOPS_LEVEL不为0,需要额外再加上一定的最小间距
cpp
void OnStart()
{
//---
double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double et = ask + 10*_Point;
double sl = et -100 *_Point;
double tp = et +100 *_Point;
double lots = 0.012; // 超出平台允许的手数精度
double lotStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
lots = MathRound(lots/lotStep) * lotStep;
//trade.BuyStop(lots, et,_Symbol, sl,tp,ORDER_TIME_GTC);
int stopLevel = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
int spread = (int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
// 进场价与 ask价格小于 平台允许的最小距离
if(et -ask < (stopLevel+spread) *_Point)
{
Print("买单进场价不符合规范");
return ;
}
// 进场价与止损价之差 小于 平台允许的最小距离
if(sl>0 && et -sl< (stopLevel + spread) * _Point)
{
Print("买单止损不符合规范");
return;
}
// 止盈价与进场价之差 小于平台允许的最小距离
if(tp>0 && tp -et < (stopLevel +spread)*_Point)
{
Print("买单止盈不符合规范");
return;
}
}