如何在莫斯科交易所安全地使用您的 EA 进行交易(基础篇)
📘

如何在莫斯科交易所安全地使用您的 EA 进行交易(基础篇)

第 1/3 篇

◍ 在莫斯科交易所跑 EA 的接入前提

莫斯科交易所(MOEX)通过 MetaTrader 5 网关接入零售经纪商,EA 要在这上面跑,第一道关是交易品种映射。和外汇符号不同,MOEX 的股票、期货代码在 MT5 里常带交易所后缀,比如 SBER 对应 SBER.MOEX,品种名错一个字符,OrderSend 就会直接返回 4753 错误(无效品种)。

  • 年 12 月 2 日平台示例里统计过,初次接入 MOEX 网关的 EA 约有 77 例报单异常,其中多数源于交易时段判断错误——俄交所现货与衍生品收市时间按莫斯科时区走,MT5 本地时间若没用 TimeTradeServer() 校准,容易在休市窗口发单。

外汇与贵金属之外,交易所品种杠杆和保证金算法完全不同,MOEX 期货采用保证金率动态浮动,EA 里写死的 Lot=0.1 可能在波动日触发保证金不足。这类跨市场 EA 回测必须用对应交易所历史数据,用外汇 tick 跑出的曲线不具备参考意义,实盘前建议在模拟网关先验证三笔以上真实品种报单。

「这套内容要拆开的六个技术块」

这一篇不是直接讲策略,而是先把后面要落地的六个硬核模块列清楚,方便你开 MT5 对照着逐节验证。 价格流动的离散性是第一块,会拆到价格缺口、尖刺,以及如何用限价单把最大滑点锁死——手工和 EA 两种路径都给。 第二块是市场流动性分析,重点在入场前和实时两道滑点测算,外加 SpreadRecord 点差指标做过滤器,以及点差暴扩时对手工和自动交易的实际限制。 第三块是安全交易与 EA 测试模式,用休眠模式替掉分时价控制,给一套基于均线和每周期一单的例程,以及用完整柱线测 EA 的合规做法。

先看清技术风险这道坎

外汇与贵金属市场里,每一笔单子都背着实打实的高风险:滑点、流动性真空、价格尖刺,都可能在一两根 K 线内吃掉账户净值。多数亏损不是策略错了,而是交易机制本身在特定行情下失守。 市面上讲资金管理的书和 EA 模板不少,但大多默认‘行情连续、报价正常’这个前提。真遇到流动性枯竭或瞬时尖刺,那些规则往往直接失效。 本文不碰策略开发和仓位配比,只盯一个被忽略的层面——技术风险。重点给 EA 和手动单加上一层防护,避开价格异常要素引发的偶然性崩盘。 底层原理参考了莫斯科交易所衍生品市场的定价机制研究,落地到 MT5 上,就是让你的程序在危险报价出现时知道怎么自保。

◍ 离散报价下的滑点与尖刺陷阱

MT5 里看到的蜡烛看似连续,底层却是离散成交堆出来的。策略测试器开“每笔分时价”模式时,价格被平滑成顺序生成,比如步长 1 从 10 走到 15,中间 11~14 都存在;但真实盘口是跳着走的,一笔单子可能直接跨过好几个价位。 市价单和止损单在这里最危险:设想挂高位买,触发条件写 64 203,实际柱内根本没有这个价,订单只能在下一个存在的 64 220 成交,白白吃了 13 点偏差。外汇、贵金属这类高杠杆品种,这种跳空随时可能把止损打穿,属于典型的高风险场景。 更狠的是价格尖刺。原文举了 RUB/USD 期货的例子:在 64 200 挂高位买、止损 64 100,算好最多亏 100 点;结果一笔远离群体的分时价戳到 64 440 把单子触发,下一笔分时价回 64 100 把止损也打了,不到一秒亏 340 点。尖刺再大一点,账户直接归零,跟资金规模无关。 限价单是唯一切实能管住最大滑点的工具。交易所执行模式下,限价买设 64 220、当前价 64 200,因为当前价更优会立即成交;若中间冒出比 64 220 更差的价,直接忽略不执行。用限价卖/买替掉市价卖/买,把“限价与当前价之差”当成你能容忍的滑点上限就行。 MT5 还有算法单高位限价买(Buy Limit Stop)和低位限价卖(Sell Limit Stop):价格触到停止位后,才在服务器侧挂出限价单。比如多仓 Si-6.15 止损 56 960,限价填 56 955,就是锁死最大 5 点滑点;空仓反之用高位限价买,停止 56 920、限价 56 925。低流动性市场里这比普通止损单靠谱得多。

「一键面板的类骨架怎么搭」

想在 MT5 图表上放一个能点 BUY / SELL 的悬浮面板,第一步是把交互逻辑封进一个类里。下面这段头文件级的声明,就是典型的最小可用骨架:用 CTrade 句柄接管下单,用一串 string 存按钮和编辑框的对象名,用 int 存偏差点数。 私有区里 OnObjClick 和 OnEndEdit 分别接图表点击事件和输入框失焦事件,CalcCurrentPrice 按买卖方向算挂单价;公开区只留构造、析构和 OnChartEvent 总入口。实际跑起来,你只要在 EA 的 OnChartEvent 里转调该类,就能在图上抓到按钮名做市价单。 注意 OP_BUY 定义成 0、OP_SELL 定义成 1,这只是本地宏,别和 MQL5 内建 ENUM_ORDER_TYPE 混用。外汇与贵金属杠杆高,这类面板一键发单若没加滑点保护,极端行情可能以偏离预期不少的点位成交。 别把对象名写死在多处 面板里 m_buy_button_name 写成 "buy_button" 并在构造初始化列表赋值,意味着全图唯一。若在多个实例或指标里复刻同名对象,ChartEvent 会抢事件,点下去可能两边同时触发。调试时开 MT5 的 Objects List 看重名最直观。

MQL5 / C++
class=class="str">"cmt">//|                                                                 Panel.mqh  |
class=class="str">"cmt">//|                     Copyright class="num">2015, Vasiliy Sokolov.              |
class=class="str">"cmt">//|                                       [MQL5官方文档]        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2015, Vasiliy Sokolov."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#include <Trade\Trade.mqh>
class="macro">#define OP_BUY class="num">0
class="macro">#define OP_SELL class="num">1
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CDevPanel
  {
class="kw">private:
   CTrade         Trade;
   class="type">class="kw">string         m_descr_dev;
   class="type">class="kw">string         m_buy_button_name;
   class="type">class="kw">string         m_sell_button_name;
   class="type">class="kw">string         m_deviation_name;
   class="type">class="kw">string         m_volume_name;
   class="type">class="kw">string         m_bg_fon;
   class="type">int            m_deviation;
   class="type">void           OnObjClick(class="type">class="kw">string sparam);
   class="type">void           OnEndEdit(class="type">class="kw">string sparam);
   class="type">class="kw">double         CalcCurrentPrice(class="type">int op_type);
class="kw">public:
                 CDevPanel();
                ~CDevPanel();
   class="type">void           OnChartEvent(const class="type">int id,
                               const class="type">long &lparam,
                               const class="type">class="kw">double &dparam,
                               const class="type">class="kw">string &sparam);
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CDevPanel 类                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CDevPanel::CDevPanel(): m_buy_button_name("buy_button"),

一键面板的对象初始化落点

上面这段构造函数把交易面板的底层 GUI 对象一次性铺好:背景矩形 190×80 像素、买按钮距左 100 像素、卖按钮距左 10 像素,两者 Y 偏移都是 50,肉眼看是左右并排。 偏离值编辑框默认填 3,距左 120、距顶 20,宽 60 且右对齐,说明它压在按钮上方偏右的位置,方便手填滑点容忍度。 外汇和贵金属点差跳动快,这种写死 3 点偏离的面板在伦敦盘开盘瞬间可能拒单,实盘前建议把 m_deviation 改成变量并在 OnChartEvent 里联动刷新。

MQL5 / C++
m_sell_button_name("sell_button"),
 m_deviation_name("deviation"),
 m_volume_name("volume"),
 m_bg_fon("bg_fon"),
 m_descr_dev("descr_dev"),
 m_deviation(class="num">3)
 {
class=class="str">"cmt">//--- 背景
 ObjectCreate(class="num">0,m_bg_fon,OBJ_RECTANGLE_LABEL,class="num">0,class="num">0,class="num">0);
 ObjectSetInteger(class="num">0,m_bg_fon,OBJPROP_YSIZE,class="num">80);
 ObjectSetInteger(class="num">0,m_bg_fon,OBJPROP_XSIZE,class="num">190);
 ObjectSetInteger(class="num">0,m_bg_fon,OBJPROP_BGCOLOR,clrWhiteSmoke);
class=class="str">"cmt">//--- 买入按钮
 ObjectCreate(class="num">0,m_buy_button_name,OBJ_BUTTON,class="num">0,class="num">0,class="num">0);
 ObjectSetInteger(class="num">0,m_buy_button_name,OBJPROP_XDISTANCE,class="num">100);
 ObjectSetInteger(class="num">0,m_buy_button_name,OBJPROP_YDISTANCE,class="num">50);
 ObjectSetInteger(class="num">0,m_buy_button_name,OBJPROP_XSIZE,class="num">80);
 ObjectSetInteger(class="num">0,m_buy_button_name,OBJPROP_BGCOLOR,clrAliceBlue);
 ObjectSetString(class="num">0,m_buy_button_name,OBJPROP_TEXT,"BUY");
class=class="str">"cmt">//--- 卖出按钮
 ObjectCreate(class="num">0,m_sell_button_name,OBJ_BUTTON,class="num">0,class="num">0,class="num">0);
 ObjectSetInteger(class="num">0,m_sell_button_name,OBJPROP_XDISTANCE,class="num">10);
 ObjectSetInteger(class="num">0,m_sell_button_name,OBJPROP_YDISTANCE,class="num">50);
 ObjectSetInteger(class="num">0,m_sell_button_name,OBJPROP_XSIZE,class="num">80);
 ObjectSetInteger(class="num">0,m_sell_button_name,OBJPROP_BGCOLOR,clrPink);
 ObjectSetString(class="num">0,m_sell_button_name,OBJPROP_TEXT,"SELL");
class=class="str">"cmt">//--- 偏离
 ObjectCreate(class="num">0,m_deviation_name,OBJ_EDIT,class="num">0,class="num">0,class="num">0);
 ObjectSetInteger(class="num">0,m_deviation_name,OBJPROP_XDISTANCE,class="num">120);
 ObjectSetInteger(class="num">0,m_deviation_name,OBJPROP_YDISTANCE,class="num">20);
 ObjectSetInteger(class="num">0,m_deviation_name,OBJPROP_XSIZE,class="num">60);
 ObjectSetInteger(class="num">0,m_deviation_name,OBJPROP_BGCOLOR,clrWhite);
 ObjectSetInteger(class="num">0,m_deviation_name,OBJPROP_COLOR,clrBlack);
 ObjectSetInteger(class="num">0,m_deviation_name,OBJPROP_ALIGN,ALIGN_RIGHT);
 ObjectSetString(class="num">0,m_deviation_name,OBJPROP_TEXT,(class="type">class="kw">string)m_deviation);

常见问题

需要先开通支持程序化交易的券商账户,并拿到行情与交易 API 接入权限,否则 EA 无法连接交易所。
该市场报价非连续,价格跳空多,市价单易在尖刺处成交,建议在 EA 中加挂单偏移与最大滑点限制。
可以,小布能读取你的策略参数与成交日志,提示异常滑点和断连风险,并给出面板初始化检查清单。
放在 OnInit 末尾且确认图表环境就绪之后,避免对象重复创建或图表未加载完导致报错。
是交易时段与休市规则差异,错把连续市场逻辑套用过来,会导致挂单失效或异常撤单。