MQL5 交易工具包(第 4 部分):开发历史管理 EX5 库(基础篇)
📘

MQL5 交易工具包(第 4 部分):开发历史管理 EX5 库(基础篇)

第 1/3 篇

把历史订单塞进一个可复用 EX5 库

在 MT5 里做策略复盘或风控模块时,反复调 HistorySelect 拉历史订单是高频动作。把这套逻辑封成一个 EX5 库,比每次在 EA 里重写一遍更省事,也方便多项目复用。 库的核心入口就是 HistorySelect,指定起止时间后,用 HistoryDealsTotal 和 HistoryOrdersTotal 分别遍历成交与挂单。注意 MT5 的历史是分成交(deal)和订单(order)两套对象的,混用会拿错字段。 外汇与贵金属杠杆高、滑点跳空频繁,历史数据里可能出现异常成交记录,回测和实盘拉出来的笔数不一定一致,验证库之前先在小周期手动核对几十条。

◍ 为什么交易历史库值得单独造轮子

前面两篇已经把 PositionsManager.ex5 和 PendingOrdersManager.ex5 两个运行时库跑通了,一个管持仓、一个管挂单,还配了带 GUI 的演示样例。这一篇要补的短板是历史侧:把已平仓订单、成交流水和持仓历史捞出来再做加工。 具体落点是再写一个 EX5 库,专门负责检索和处理已完成订单、deal 和 position 的历史记录,并外接分析模块按灵活条件出交易报告——用来评估某个 EA、某套系统或单个品种的真实表现。 很多刚碰 MQL5 的人卡就卡在 HistorySelect / HistoryDealsTotal 那套时序与类型区分上,尤其分不清 order、deal、position 在历史表里谁对应谁。这个库就是给这类开发者避坑用的,也适合任何不想每次都手搓历史查询逻辑的程序员直接复用。 开头先掰几个新手高频误解点,这些点不理顺,后面写回测统计和报表都会埋雷。

「订单落地前:市价单与挂单的两条路径」

MQL5 里的交易事务,起点永远是订单执行。订单只分两类:直接吃当前价的市价单,以及等价格触发的挂单。 直接市价单用卖价或买价实时成交,执行后立刻变成未平仓头寸,系统给一个唯一单号和 POSITION_ID,靠这个 ID 才能在整个生命周期里稳定跟住头寸状态。 挂单有 BUY STOP、BUY LIMIT、SELL STOP、SELL LIMIT、BUY STOP LIMIT、SELL STOP LIMIT 六种。触发价没到之前它一直处于无效挂起态;价格碰线后转成市价单执行,同样拿到单号和 POSITION_ID。 开 MT5 用 OrdersTotal() 和 PositionsTotal() 分别数一下挂单与持仓,能直观看到挂单未触发时只进前者、触发后从前者消失并出现在后者。外汇与贵金属杠杆高,市价滑点和挂单跳空都可能让实际成交偏离触发价,需以账户真实回报为准评估。

头寸从开仓到平仓的状态流转

一个头寸在 MT5 里不是静态对象。部分平仓会在交易历史留下一笔退出交易;若做反手,原仓位被平掉同样记为退出交易;直到整仓被手动、止损、止盈或强平事件关闭,最终退出交易落库。这三步构成头寸生命周期的主干。 MQL5 里一切始于发往交易服务器的订单:挂单请求、市价买卖、部分关闭都先登记为订单。订单成交后才转入历史库成为交易(deal)。借助 ORDER_* 与 DEAL_* 属性,你能把每笔 deal 回溯到原始 order 并关联回 position,形成一条可审计的轨迹。 活跃未平头寸和挂单在工具箱「交易」标签页;已平头寸、订单、成交进「历史」标签页。标准历史函数对新手容易混淆 position 与 deal——position 是当前持续敞口,deal 是已完结的平仓记录。理清这点,后续写策略回测或绩效统计才不会把二者混算。 这种面包屑式跟踪让起源、修改时间、最终结果全透明。基于它你能写算法筛出某策略的改进点,比如统计反转单占比或强平触发频次,再回头调参数。

◍ 建好 HistoryManager 库文件的落地步骤

在 MetaEditor 里点「新建」走 MQL 向导,选「库」源文件类型,命名 HistoryManager.mq5。把它存进 Libraries\Toolkit 目录,和已有的 Positions Manager、Pending Orders Manager 的 EX5 放一起,后期组件多了不会乱找。 文件建出来后,先把属性指令下方那行 “My function” 注释删掉。版权和链接属性你随便改,但 #property library 这行必须原样保留,否则 MT5 不认它是库。 下面这段是初始文件骨架,注意 version 默认标了 1.00,后续迭代自己改号: 代码里被注释掉的 MyCalculator 只是向导给的示例,实际不会编进库,留着也不影响编译。真要验证,建完直接 F7 编译,左侧导航里出现 HistoryManager 且无报错就行。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 HistoryManager.mq5 |
class=class="str">"cmt">//|                                        Copyright class="num">2024, Wanateki Solutions Ltd. |
class=class="str">"cmt">//|                                                      https://www.wanateki.com |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property library
class="macro">#class="kw">property copyright "Copyright class="num">2024, Wanateki Solutions Ltd."
class="macro">#class="kw">property link      "https:class=class="str">"cmt">//www.wanateki.com"
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| My function                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// class="type">int MyCalculator(class="type">int value,class="type">int value2) class="kw">export
class=class="str">"cmt">//  {
class=class="str">"cmt">//   class="kw">return(value+value2);
class=class="str">"cmt">//  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「库顶层的常量、结构与全局数组怎么摆」

写 HistoryManager.mq5 这种历史管理库,第一件事不是在函数里绕逻辑,而是把三类东西钉在全局作用域:预处理器常量、数据结构、动态结构数组。全局声明的直接好处是库内任何模块随调随取,不用层层传参,也避免重复查询 MT5 历史接口带来的开销。 预处理器常量用来给历史数据分类。这里拆成五类并配了整数标识:订单历史 1001、成交历史 1002、头寸历史 1003、挂单历史 1004、全部历史 1005。主获取函数拿到标识后只拉对应子集,时间和算力都省了。 数据结构负责落地存储。DealData 装成交属性,从 ticket、magic 到 commission、swap、profit 共 17 个字段;OrderData 装订单属性,含三种时间字段(timeSetup/timeDone/expirationTime)和订单状态、填充类型等。下面代码是这两段结构加常量的原文,可直接粘进文件 #property 指令下方。 全局动态数组用前面 struct 做元素类型,作为库托管核心数据的唯一容器。查询时往里写,分析时从里读,整个库的状态中心就这一处。外汇与贵金属历史回放涉及高杠杆风险,回测结论仅代表概率倾向,实盘前请在 MT5 策略测试器跑通再上。

MQL5 / C++
class="macro">#define GET_ORDERS_HISTORY_DATA class="num">1001
class="macro">#define GET_DEALS_HISTORY_DATA class="num">1002
class="macro">#define GET_POSITIONS_HISTORY_DATA class="num">1003
class="macro">#define GET_PENDING_ORDERS_HISTORY_DATA class="num">1004
class="macro">#define GET_ALL_HISTORY_DATA class="num">1005
class=class="str">"cmt">//- Data structure to store deal properties
class="kw">struct DealData
  {
   class="type">ulong                ticket;
   class="type">ulong                magic;
   ENUM_DEAL_ENTRY      entry;
   ENUM_DEAL_TYPE       type;
   ENUM_DEAL_REASON     reason;
   class="type">ulong                positionId;
   class="type">ulong                order;
   class="type">class="kw">string               symbol;
   class="type">class="kw">string               comment;
   class="type">class="kw">double               volume;
   class="type">class="kw">double               price;
   class="type">class="kw">datetime             time;
   class="type">class="kw">double               tpPrice;
   class="type">class="kw">double               slPrice;
   class="type">class="kw">double               commission;
   class="type">class="kw">double               swap;
   class="type">class="kw">double               profit;
   };
class=class="str">"cmt">//- Data structure to store order properties
class="kw">struct OrderData
  {
   class="type">class="kw">datetime                     timeSetup;
   class="type">class="kw">datetime                     timeDone;
   class="type">class="kw">datetime                     expirationTime;
   class="type">ulong                        ticket;
   class="type">ulong                        magic;
   ENUM_ORDER_REASON            reason;
   ENUM_ORDER_TYPE              type;
   ENUM_ORDER_TYPE_FILLING      typeFilling;
   ENUM_ORDER_STATE             state;
   ENUM_ORDER_TYPE_TIME         typeTime;
   class="type">ulong                        positionId;
   class="type">ulong                        positionById;
   class="type">class="kw">string                       symbol;
   class="type">class="kw">string                       comment;
   class="type">class="kw">double                       volumeInitial;
   class="type">class="kw">double                       priceOpen;
   class="type">class="kw">double                       priceStopLimit;

常见问题

把历史订单的读取、筛选和状态流转封装成一个独立的EX5库,下次新EA直接引用即可,不用重复写底层代码。
建议分开两条路径处理:市价单走即时成交校验,挂单走触发条件校验,再统一进历史管理结构,避免逻辑耦合。
小布可以基于你的交易记录直接生成头寸开平仓状态视图,并提示异常流转,你不必从零造轮子也能看清历史。
常量与结构定义放库顶层头区,全局数组按订单/头寸类型分块声明,初始化函数里统一清零,降低维护成本。
在库内用明确枚举标记每笔头寸状态,每次平仓事件触发时做 ticket 对冲校验,能有效减少状态错配。