Skip to content

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;
     }
  }