交易机器人在市场发布前必须经过的检验(基础篇)
📘

交易机器人在市场发布前必须经过的检验(基础篇)

第 1/3 篇

「机器人上线前的必要检验环节」

在 MT5 里跑一个交易机器人,别急着挂真仓。发布到社区或自用前,至少要过一遍历史回测与策略测试器的多周期验证。 以 2016 年 9 月 6 日某公开示例机器人为例,其帖文在平台获得 9 745 次浏览、277 条讨论,说明未经检验的 EA 一旦流出,用户反馈会直接暴露逻辑漏洞。 外汇与贵金属杠杆高、滑点随机,EA 在历史表现稳定也可能在实盘失效。建议先在策略测试器用 99% 建模质量跑至少 3 个月 tick 数据,再考虑是否上模拟盘。

◍ 上架前先过这几道质量关

EA 或指标里一个不起眼的逻辑错误,就可能在实盘里直接吃掉账户本金。MQL5 市场因此设了强制预检,开发者在发布前必须自查一批硬约束,否则审核会被打回重改。 以下几类是最常见的拦截点:保证金不足仍发单、交易量无效、挂单数量超限、品种手数超限;TP/SL 设在 SYMBOL_TRADE_STOPS_LEVEL 最小止损级以内,或在 SYMBOL_TRADE_FREEZE_LEVEL 冻结区内改单;对无历史报价的品种下单报错;还有数组越界、除零、改价未变、引入 EX4/EX5 与 DLL、用 iCustom() 调自定义指标、传无效参数、访问冲突、CPU 与内存占用异常等运行时问题。 外汇与贵金属杠杆高、滑点突变频繁,这类 bug 在回测里不显形,真上架后极易引发连锁亏损。审核员一旦在检验中抓到任一错误,开发者须全部修完才能过审,没有讨价还价余地。

用策略测试器抓EA逻辑漏洞

MT5 自带的策略测试器不只是跑回测,它把 EA 在测试中所有交易操作消息和算法错误都打到测试器日志(Journal)里。配合右键菜单调出的日志阅读器,能把杂乱输出梳理清楚。 测试完 EA 后打开阅读器并勾选“只显示错误(Error only)”,有 bug 会立刻列出来。第一次没报错不代表干净,换交易品种、周期、输入参数,甚至改初始存款重跑几轮,能挖出绝大多数隐藏问题。按官方经验,这套做法覆盖约 99% 的开发期错误。 真要定位某条错误的原因,切到 MetaEditor 的“在历史数据上调试”。可视化测试模式下,价格、指标和每一刻的变量值同屏可见,不用挂实盘苦等,就能把策略逻辑顺一遍。外汇和贵金属波动剧烈,EA 未充分调试就上实盘属高风险行为。

「下单前先卡死保证金缺口」

很多 EA 在发单前只判断信号,不查账户余额,结果挂单触发时直接报『保证金不足』被券商拒绝,这种疏忽在实盘里会漏掉关键入场。要注意,挂单虽然没成交,但平台通常会预占保证金,所以即便单子还挂着,可用资金也可能已经不够后续建仓。 建议用 1 美元或 1 欧元的极小入金先跑机器人,这样一旦资金逻辑有漏洞,损失可控,也容易暴露保证金计算错误。若检查显示自由保证金 cover 不住这笔单,应该打印错误日志并跳过 OrderSend(),而不是硬发。 下面 MQL5 的 CheckMoneyForTrade() 是标准做法:先取 tick 的 ask/bid 作为建仓价,再用 OrderCalcMargin() 算所需保证金,最后拿 ACCOUNT_MARGIN_FREE 比大小。 [CODE] bool CheckMoneyForTrade(string symb,double lots,ENUM_ORDER_TYPE type) { //--- 取得建仓价格 MqlTick mqltick; SymbolInfoTick(symb,mqltick); double price=mqltick.ask; if(type==ORDER_TYPE_SELL) price=mqltick.bid; //--- 所需以及可用保证金的数值 double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE); //--- 调用检验函数 if(!OrderCalcMargin(type,symb,lots,price,margin)) { //--- 出错了,发送报告并返回 false Print("有错误出现在 ",__FUNCTION__," 编号=" ,GetLastError()); return(false); } //--- 如果资金不够进行操作 if(margin>free_margin) { //--- 报告错误并返回 false Print("资金不足以进行 ",EnumToString(type)," ",lots," ",symb," 错误编号=" ,GetLastError()); return(false); } //--- 检验成功 return(true); } [/CODE] 逐行看:MqlTick 结构接实时报价;卖单用 bid、买单用 ask;OrderCalcMargin 失败就直接返 false 并打错误码;margin>free_margin 这一句才是真正的资金闸门。外汇和贵金属杠杆高,自由保证金随浮亏实时缩水,跑 EA 前务必在 MT5 用策略测试器接这段函数验一遍。

MQL5 / C++
class="type">bool CheckMoneyForTrade(class="type">class="kw">string symb,class="type">class="kw">double lots,ENUM_ORDER_TYPE type)
  {
class=class="str">"cmt">//--- 取得建仓价格
   class="type">MqlTick mqltick;
   SymbolInfoTick(symb,mqltick);
   class="type">class="kw">double price=mqltick.ask;
   if(type==ORDER_TYPE_SELL)
      price=mqltick.bid;
class=class="str">"cmt">//--- 所需以及可用保证金的数值
   class="type">class="kw">double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   class=class="str">"cmt">//--- 调用检验函数
   if(!OrderCalcMargin(type,symb,lots,price,margin))
     {
      class=class="str">"cmt">//--- 出错了,发送报告并返回 class="kw">false
      Print("有错误出现在 ",__FUNCTION__," 编号=" ,GetLastError());
      class="kw">return(class="kw">false);
     }
   class=class="str">"cmt">//--- 如果资金不够进行操作
   if(margin>free_margin)
     {
      class=class="str">"cmt">//--- 报告错误并返回 class="kw">false
      Print("资金不足以进行 ",EnumToString(type)," ",lots," ",symb," 错误编号=" ,GetLastError());
      class="kw">return(class="kw">false);
     }
class=class="str">"cmt">//--- 检验成功
   class="kw">return(true);
  }

◍ 下单前先卡死手数边界

EA 在调用 OrderSend() 之前,必须确认委托手数落在品种规则内,否则订单会直接被服务器拒掉。每个品种的最小交易量、最大交易量、步长都写在合约规格里,MT5 中用 SymbolInfoDouble() 配合 SYMBOL_VOLUME_MIN、SYMBOL_VOLUME_MAX、SYMBOL_VOLUME_STEP 三个枚举值实时读取,而不是写死在代码里。 下面这段检查函数就是干这个的:先读最小量,手数不够就返回 false 并带出描述;再读最大量,超了同样拒掉;最后取步长,用 MathRound(volume/step) 反推整数倍,偏差大于 1e-7 就判定不在网格上。 实盘里黄金 XAUUSD 常见 min=0.01、max=100、step=0.01,但部分平台原油或指数 step 可能是 0.1 甚至 1.0。若 EA 按 0.05 下单而 step=0.1,就会被判定为无效交易量。外汇与贵金属杠杆高,手数错配可能触发意外敞口,务必在发单前拦截。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 检查订单交易量的正确性                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CheckVolumeValue(class="type">class="kw">double volume,class="type">class="kw">string &description)
  {
class=class="str">"cmt">//--- 交易操作允许的最小交易量
   class="type">class="kw">double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("交易量小于允许的最小交易量,SYMBOL_VOLUME_MIN=%.2f",min_volume);
      class="kw">return(class="kw">false);
     }
class=class="str">"cmt">//--- 交易操作允许的最大交易量 
   class="type">class="kw">double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("交易量大于允许的最大交易量,SYMBOL_VOLUME_MAX=%.2f",max_volume);
      class="kw">return(class="kw">false);
     }
class=class="str">"cmt">//--- 取得交易量变化的最小步长
   class="type">class="kw">double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
   class="type">int ratio=(class="type">int)MathRound(volume/volume_step);
   if(MathAbs(ratio*volume_step-volume)>class="num">0.0000001)
     {
      description=StringFormat("交易量不是最小交易步长的整数倍,SYMBOL_VOLUME_STEP=%.2f, 最接近的正确交易量是 %.2f",
                               volume_step,ratio*volume_step);
      class="kw">return(class="kw">false);
     }
   description="正确的交易量数值";
   class="kw">return(true);
  }

挂单数量上限的检查盲区

MT5 账户对同时活动的挂单数常有硬上限,用 ACCOUNT_LIMIT_ORDERS 读取到的就是这个值。下面这段判断逻辑能直接抄进 EA:先取上限赋给 max_allowed_orders,若为 0 表示经纪商不限制,直接放行;否则拿 OrdersTotal() 的当前挂单数去比大小,未超才返回 true。 [CODE] //+------------------------------------------------------------------+

//检查是否还允许设置订单

//+------------------------------------------------------------------+ bool IsNewOrderAllowed() { //--- 取得账户中允许设置的挂单数量 int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS); //--- 如果没有限制,返回 true; 您可以发送一个订单 if(max_allowed_orders==0) return(true); //--- 如果我们达到这一行,说明有限制; 找出已经设置了多少挂单 int orders=OrdersTotal(); //--- 返回比较结果 return(orders<max_allowed_orders); } [/CODE] 逐行看:第 4 行用 AccountInfoInteger 拿账户级订单限制;第 6 行遇 0 即无限制,立刻可发单;第 9 行 OrdersTotal 统计当前挂单;第 11 行用小于号判断余量。 这个函数只防了“总数”这一道关。它没算品种层面的敞口上限与单品种挂单配额,实盘里可能碰到某品种挂不进、但总数还没满的情况。外汇与贵金属杠杆高,这类隐性限制会让你策略在波动期漏单,上 MT5 用账户窗口和品种规格自查一遍更稳。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 检查是否还允许设置订单                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool IsNewOrderAllowed()
  {
class=class="str">"cmt">//--- 取得账户中允许设置的挂单数量
   class="type">int max_allowed_orders=(class="type">int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
class=class="str">"cmt">//--- 如果没有限制,返回 true; 您可以发送一个订单
   if(max_allowed_orders==class="num">0) class="kw">return(true);
class=class="str">"cmt">//--- 如果我们达到这一行,说明有限制; 找出已经设置了多少挂单
   class="type">int orders=OrdersTotal();
class=class="str">"cmt">//--- 返回比较结果
   class="kw">return(orders<max_allowed_orders);
  }

常见问题

至少跑策略测试器抓逻辑漏洞、卡死保证金缺口和手数边界、查挂单数量上限盲区,三关过了再谈上架。
在测试器里用极端点差和最低保证金比例跑压力场景,强制EA在下单前校验账户保证金占用,不够就禁开仓。
小布可替你调用策略测试器做逻辑巡检,并标出保证金缺口与手数越界风险,你只需确认报告。
在代码里写死最大手数常量并和下单函数前做if拦截,测试器用随机行情触发边界值验证不越线。
多数平台有挂单总数上限,EA需内置计数并在接近上限时停止新挂单,测试器里堆满挂单测失效分支。