跨平台专家交易系统: 订单管理器·综合运用
📘

跨平台专家交易系统: 订单管理器·综合运用

第 3/3 篇

「用订单管理器类搭一个跨平台 EA 原型」

在 MetaEditor 里建好 EA 源文件后,第一件事是把自写的交易库用引号引到同目录,而不是用尖括号去系统路径找。接着至少声明三个全局指针:COrderManager、CSymbolInfo、CSymbolManager,它们管下单、品种属性和符号集合。 OnInit 里必须给这三个指针 new 出来,尤其 CSymbolInfo 实例要绑定具体品种名(比如 EURUSD);OnDeinit 里对应 delete,否则终端内可能漏内存。OnTick 的策略核心是新柱检测——用静态变量存上一根柱数,再用方向变量记上一次持仓方向,首笔为 0。 MQL4 直接读预定义 Bars 变量或调 iBars,MQL5 则统一调 Bars 函数,这部分要分平台写。检测到柱数变化后,先刷 CSymbolInfo 汇率,平掉旧仓,再按反方向开新仓,最后更新柱数静态变量。 为让 MQL4/MQL5 共用同一套逻辑,把上述代码挪进头文件,主源文件(test_ordermanager.mq4 或 .mq5)只留一行引用。回测里 MT4 前 10 笔平仓盈亏累计到 9989.50(起点 10000),而 MT5 净持与对冲模式成交记录一致,区别只在净持用等量反向单抵消、对冲像 MT4 那样显式 close #2。外汇与贵金属杠杆高,这类原型仅验证框架,实盘前需自测滑点与点值。

用 OrderManager 跑一根 K 线一反转的 EA 骨架

这段代码演示了在 MT5 里借助封装好的 OrderManager 基类,用极少量逻辑实现「每来一根新 K 线就平掉旧单、反手开新单」的轮转交易。它依赖 MQLx\Base\OrderManager 下的几个对象:COrderManager 管下单平仓,CSymbolManager 管品种容器,CSymbolInfo 取实时报价。 初始化阶段先 new 出三个管理器,把当前 Symbol 绑进 symbol_info,再塞进 symbol_manager,最后 order_manager.Init 完成装配;若品种名绑定失败直接返回 INIT_FAILED,避免后续空指针。OnDeinit 里依次 delete 三个指针,防止 EA 重载时内存泄漏。 [CODE] #include "MQLx\Base\OrderManager\OrderManagerBase.mqh" COrderManager *order_manager; CSymbolManager *symbol_manager; CSymbolInfo *symbol_info; int OnInit() { //--- order_manager = new COrderManager(); symbol_manager = new CSymbolManager(); symbol_info = new CSymbolInfo(); if (!symbol_info.Name(Symbol())) { Print("symbol not set"); return (INIT_FAILED); } symbol_manager.Add(GetPointer(symbol_info)); order_manager.Init(symbol_manager,NULL); //--- return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { //--- delete symbol_info; delete symbol_manager; delete order_manager; } static int bars = 0; static int direction = 0; int current_bars = 0; #ifdef __MQL5__ current_bars = Bars(NULL,PERIOD_CURRENT); #else current_bars = Bars; #endif if (bars<current_bars) { symbol_info.RefreshRates(); COrder *last = order_manager.LatestOrder(); if (CheckPointer(last) && !last.IsClosed()) order_manager.CloseOrder(last); if (direction<=0) { Print("买入交易入场.."); order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask()); direction = 1; } else { Print("卖出交易入场.."); order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid()); direction = -1; } bars = current_bars; } #include "test_ordermanager.mqh" [/CODE] 逐行看核心循环:用 static bars 记录已处理根数,current_bars 取 Bars(NULL,PERIOD_CURRENT);当 bars<current_bars 说明新柱形成,先 RefreshRates 更新卖买价,调 LatestOrder 拿到上一单,未平仓就 CloseOrder 强平。 direction 变量控制多空翻转:≤0 时市价买 0.10 手并将 direction 置 1,否则市价卖并将 direction 置 -1。日志里那笔 2017.01.02 01:00:00 的 instant sell 0.10 EURUSD at 1.05172 就是该逻辑触发的实盘回测痕迹,成交基于 order #3。 别把这套当策略 它只是验证 OrderManager 接口可用的最小骨架,每根 K 线反手在高杠杆外汇/贵金属品种上可能迅速吃掉保证金,EURUSD 这类流动性品种尚且频繁碰滑点,实盘前务必把 direction 翻转条件和手数管理重写。

MQL5 / C++
class="macro">#include "MQLx\Base\OrderManager\OrderManagerBase.mqh"
COrderManager *order_manager;
CSymbolManager *symbol_manager;
CSymbolInfo *symbol_info;
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   order_manager = new COrderManager();
   symbol_manager = new CSymbolManager();
   symbol_info = new CSymbolInfo();
   if (!symbol_info.Name(Symbol()))
   {
      Print("symbol not set");
      class="kw">return (INIT_FAILED);
   }  
   symbol_manager.Add(GetPointer(symbol_info));  
   order_manager.Init(symbol_manager,NULL);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
class=class="str">"cmt">//---
   class="kw">delete symbol_info;
   class="kw">delete symbol_manager;
   class="kw">delete order_manager;
  }
class="kw">static class="type">int bars = class="num">0;
class="kw">static class="type">int direction = class="num">0;
class="type">int current_bars = class="num">0;
class="macro">#ifdef __MQL5__
   current_bars = Bars(NULL,PERIOD_CURRENT);
class="macro">#else
   current_bars = Bars;
class="macro">#endif
if (bars<current_bars)
   {  
      symbol_info.RefreshRates();
      COrder *last = order_manager.LatestOrder();
      if (CheckPointer(last) && !last.IsClosed())
         order_manager.CloseOrder(last);
      if (direction<=class="num">0)
      {
         Print("买入交易入场..");
         order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
         direction = class="num">1;
      }
      else
      {
         Print("卖出交易入场..");
         order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
         direction = -class="num">1;
      }  
      bars = current_bars;
   }
class="macro">#include "test_ordermanager.mqh"

◍ 从日志读出成交确认的链路

上面这段 MT5 Experts 日志,是 2017.01.02 01:00:00 一笔 EURUSD 卖单 0.10 手在 1.05172 成交时的原始回显。DS 行先报 deal performed,说明订单已落地;PG 行给出 instant sell 与买卖盘报价(1.05172 / 1.05237 / 1.05172),其中中间价是卖一、右边是买一。 MQ 行补了 based on order #3,把 deal #3 和 order #3 绑定,KN 行再次确认 deal performed。四行时间戳集中在 16:20:51.958,只有 DS 早 1.2 秒,说明回写有微小延迟但属同一次撮合。 做策略复盘时,直接 grep deal performed 比看 PG 行更稳,能避开报价闪烁干扰。外汇与贵金属杠杆高,单笔日志确认不代表风险可控,仍需结合仓位与止损。

MQL5 / C++
DS        class="num">0      class="num">16:class="num">19:class="num">15.747    Trade   class="num">2017.01.class="num">02 class="num">01:class="num">00:class="num">00   deal performed [#class="num">3 sell class="num">0.10 EURUSD at class="num">1.05172]
PG        class="num">0      class="num">16:class="num">20:class="num">51.958    Trade   class="num">2017.01.class="num">02 class="num">01:class="num">00:class="num">00   instant sell class="num">0.10 EURUSD at class="num">1.05172 (class="num">1.05172 / class="num">1.05237 / class="num">1.05172)
MQ        class="num">0      class="num">16:class="num">20:class="num">51.958    Trades  class="num">2017.01.class="num">02 class="num">01:class="num">00:class="num">00   deal #class="num">3 sell class="num">0.10 EURUSD at class="num">1.05172 done(based on order #class="num">3)
KN        class="num">0      class="num">16:class="num">20:class="num">51.958    Trade   class="num">2017.01.class="num">02 class="num">01:class="num">00:class="num">00   deal performed [#class="num">3 sell class="num">0.10 EURUSD at class="num">1.05172]

「订单管理器的嵌套容器长什么样」

COrderManager 是这套框架里最重的类之一,它的真实结构不是平铺的订单数组,而是一层套一层的容器。管理器内部持有两个 COrders 实例,分别装当前持仓与历史订单,每个 COrders 再作为 COrder 的容器管理已入场的单子。 单笔 COrder 下面还能挂停止价位,数量可以是零、一个,也可能多个;而每一个停止价位又能绑定自己的尾随方法,同样可无可多。这种多级挂在大多数 EA 里用不上,但处理多重支撑阻力策略时,一条单子同时盯几道止损位就很顺手。 下面的流程图只给外貌思路,具体成员以后单拆。实盘里若你写多层级止损 EA,可先照这个嵌套在 MT5 里建空类编译,验证容器引用不会在订单平仓后悬空。

一点提醒

把交易操作收口到 COrderManager 这一类里,EA 的主逻辑和头文件就能在 MQL4、MQL5 两套环境里共用同一份源码,不用为每个平台重写下单与撤单分支。对于做跨平台策略的人来说,这省掉的是重复维护成本,不是某种收益保证。 附带示例包 ordermanager_sample.zip 体积约 894.99 KB,里面是可直接在 MT5 载入跑通的工程;外汇与贵金属杠杆品种波动剧烈,跨平台兼容不代表风险被抹平,实盘前务必在策略测试器用历史数据验一遍下单路径。 这类封装的价值只在你真正动手改参数、接自己信号时才显出来——下载、编译、断点跟一次 OpenBuy 流程,比读十遍说明更有用。

常见问题

至少实现 OnInit 初始化 OrderManager、OnTick 里调用管理器的刷新与下单接口、OnDeinit 释放容器;缺任一都会在加载时挂掉。
在 OrderManager 下单前用时间轴记录当前 K 线起始时间,若已在该根 K 线内有持仓标记则跳过信号,只保留一次反转。
可以。把 EA 日志贴给小布,它能按成交确认链路标出挂单、改仓、成交各步耗时与异常,省去你逐行翻记录。
常见是品种映射下挂订单向量,再嵌止损止盈子结构;嵌套便于按品种批量操作和避免遍历全表,平铺会拖慢 tick 响应。
盯订单号、成交返回码、成交价与请求价偏差;三者齐了且返回码为成功,才说明链路走完不是假信号。