MQL5 交易工具包(第 8 部分):如何在代码库中实现和使用历史管理 EX5 库(基础篇)
📘

MQL5 交易工具包(第 8 部分):如何在代码库中实现和使用历史管理 EX5 库(基础篇)

第 1/3 篇

把历史管理塞进自己的 EX5 库

在 MT5 里做策略开发,反复读取账户历史会拖慢 EA 运行,尤其回测跨年数据时延迟明显。把历史管理逻辑封装成独立 EX5 库,主程序用 #import 调用,能把订单筛选、缓存更新从交易线程里摘出来。 MQL5 代码库(Code Base)里已有现成的 HistoryManager 类实现,编译后生成 EX5,其他 EA 直接引用即可,不用每次重写 OrderSelect 循环。下面这段是库里初始化历史缓存的核心片段,注意它只在新品种 tick 到达时刷新,而不是每笔成交都扫全量。 外汇与贵金属杠杆高,历史数据缺口可能造成回测过拟合,实盘前务必用真实点差重跑。

MQL5 / C++
class="type">int OnInit()
  {
   if(!m_history.Init(Symbol(),PERIOD_M1))
      class="kw">return INIT_FAILED;
   EventSetTimer(class="num">60);
   class="kw">return INIT_SUCCEEDED;
  }

「把历史库接进你的 MT5 项目」

上一代 EX5 历史管理库已经能直接扫描、检索、排序账户里的平仓单、挂单与成交记录。这一节不重复造轮子,只解决最后一环:怎么把这套库干净地塞进你自己的 MQL5 程序里。 库里像 GetLastClosedPositionData()、GetLastFilledPendingOrderData()、GetLastClosedProfitablePositionData() 这类函数,返回的是带明细的结构体,不用你再手写循环翻 HistoryDeals。按品种、幻数、时间段过滤也封装好了,一行调用就能拿到定向数据。 对外汇和贵金属交易者来说,这类账户数据接口的高频调用本身不放大风险,但历史统计结论只能作为概率参考,实盘杠杆品种随时可能反向跳空。 真正省时间的是统一接口:交易、订单、持仓、挂单全走同一套命名,维护脚本时不用在几个不同 API 之间切脑子。模块化设计让你只 include 用到的函数,小脚本和大型 EA 都能直接扩。

◍ 把历史管理库接进 MT5 项目

要在自己的 EA 或脚本里调用 HistoryManager.ex5,先得让 MetaEditor 找得到它。启动 MT5 后按 F4 进 MetaEditor,别在终端里瞎翻文件夹,路径错了编译器直接报 cannot find library。 先在 MQL5 根目录的 Libraries 下建一层 Wanateki\Toolkit\HistoryManager。完整落点是 MQL5\Libraries\Wanateki\Toolkit\HistoryManager,少一层都会在后续 #import 时报路径异常。 把下载到的 HistoryManager.ex5 丢进上面这个文件夹。紧接着在 MetaEditor 用「新建」走 MQL 向导,选 Include(*.mqh),把文件名填成 Include\Wanateki\Toolkit\HistoryManager\HistoryManager.mqh 再点完成——这一步生成的是头文件,不是库本体,但后续代码靠它做声明。 外汇与贵金属品种的历史数据跨周期调用风险偏高,库接好后建议先在策略测试器用 2023 年 XAUUSD 的 1 分钟数据跑通一次导入,确认无报再上实盘模拟。

头文件骨架:数据结构与宏先落地

做历史管理模块时,先把 HistoryManager.mqh 撑成空壳没用,得往里填四套数据结构:DealData、OrderData、PositionData、PendingOrderData。它们全局可见,负责装各类交易历史属性,后面任何源文件 include 这个头就能直接调。 时间类宏是省事的关键。NOW 取当前时间,ONE_DAY 用 TimeCurrent() 减 PeriodSeconds(PERIOD_D1) 算出 24 小时前,ONE_WEEK、ONE_MONTH 同理换周期常量;ONE_YEAR 则拿月周期秒数乘 12 近似。TODAY 到 THIS_YEAR 给的是 12~15 的整型标记,配合后面的筛选函数用。 ALL_SYMBOLS 定义成空串 "",意思是「不限定品种」;ALL_POSITIONS、ALL_ORDERS、ALL_DEALS 分别是 1110、1111、1112,用来在调用时指定拉全量持仓、订单或成交。外汇和贵金属历史回放高风险,全量拉取前先确认 magic 过滤,避免把测试单混进统计。 #import 指向 Wanateki/Toolkit/HistoryManager/HistoryManager.ex5,头里只放声明:Print 系列四位日期参数打印,Get 系列把数据写进引用数组并返回 bool。真正逻辑在 ex5 里,头文件末尾以 #import 收尾即可。 下面这段是头文件基础骨架,结构体和宏留了注释位等你补成员,函数声明已可编译通过。

MQL5 / C++
class="kw">struct DealData
  {
  class=class="str">"cmt">// Add all the DealData members here
  }

class="kw">struct OrderData
  {
  class=class="str">"cmt">// Add all the OrderData members here
  }

class="kw">struct PositionData
  {
  class=class="str">"cmt">// Add all the PositionData members here
  }

class="kw">struct PendingOrderData
  {
  class=class="str">"cmt">// Add all the PendingOrderData members here
  }
class="macro">#define NOW class="type">class="kw">datetime(TimeCurrent())
class="macro">#define ONE_DAY class="type">class="kw">datetime(TimeCurrent() - PeriodSeconds(PERIOD_D1))
class="macro">#define ONE_WEEK class="type">class="kw">datetime(TimeCurrent() - PeriodSeconds(PERIOD_W1))
class="macro">#define ONE_MONTH class="type">class="kw">datetime(TimeCurrent() - PeriodSeconds(PERIOD_MN1))
class="macro">#define ONE_YEAR class="type">class="kw">datetime(TimeCurrent() - (PeriodSeconds(PERIOD_MN1) * class="num">12))
class="macro">#define EPOCH class="num">0 class=class="str">"cmt">// 1st Jan class="num">1970
class=class="str">"cmt">//--
class="macro">#define TODAY class="num">12
class="macro">#define THIS_WEEK class="num">13
class="macro">#define THIS_MONTH class="num">14
class="macro">#define THIS_YEAR class="num">15
class="macro">#define ALL_SYMBOLS ""
class="macro">#define ALL_POSITIONS class="num">1110
class="macro">#define ALL_ORDERS class="num">1111
class="macro">#define ALL_DEALS class="num">1112
class="macro">#class="kw">import "Wanateki/Toolkit/HistoryManager/HistoryManager.ex5"
class="macro">#class="kw">import "Wanateki/Toolkit/HistoryManager/HistoryManager.ex5"
class=class="str">"cmt">//--
class="type">void PrintDealsHistory(class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime);
class="type">void PrintOrdersHistory(class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime);
class="type">void PrintPositionsHistory(class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime);
class="type">void PrintPendingOrdersHistory(class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime);
class=class="str">"cmt">//--
class="type">bool GetDealsData(DealData &dealsData[], class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime, class="type">class="kw">string symbol, class="type">ulong magic);
class="type">bool GetOrdersData(OrderData &ordersData[], class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime, class="type">class="kw">string symbol, class="type">ulong magic);
class="type">bool GetPositionsData(PositionData &positionsData[], class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime, class="type">class="kw">string symbol, class="type">ulong magic);
class="type">bool GetPendingOrdersData(PendingOrderData &pendingOrdersData[], class="type">class="kw">datetime fromDateTime, class="type">class="kw">datetime toDateTime, class="type">class="kw">string symbol, class="type">ulong magic);
class=class="str">"cmt">//--
class=class="str">"cmt">// Add all the other function declarations here...
class="macro">#class="kw">import
class="kw">struct DealData
  {
   class="type">ulong               ticket;
   class="type">ulong               magic;

「成交、挂单与持仓三类结构体字段拆解」

在 MT5 的历史与实时交易对象抓取里,用结构体把 Deal、Order、Position 的原始字段一次性打包,比反复调用 HistoryDealGetDouble 更省事。下面这三组定义直接对应交易池里的三类核心实体,复制进 EA 的 include 头文件就能用。 DealData 记录已成交交易的快照:entry 区分开仓或平仓方向,type 标记市价或止损单触发,positionId 把每笔成交绑回对应持仓。profit、commission、swap 三个 double 字段让你在回测里能直接算净盈亏,不用事后查账户历史。 OrderData 覆盖挂单生命周期:timeSetup 是下单时间,timeDone 是成交或撤单时间,expirationTime 管 GTC 或当日失效;typeFilling 与 state 组合能筛出 PARTIAL_FILLED 这类半成交状态,对贵金属跳空行情下的流动性判断有用。 PositionData 站在持仓视角:initiatedByPendingOrder 这个 bool 直接告诉你仓位来自限价单还是市价单,openingDealTicket 与 closingDealTicket 把开平两端成交票号都留痕。外汇与贵金属杠杆高,用这些字段做持仓归因时,仍须注意滑点和停牌带来的数据断层风险。

MQL5 / C++
class="kw">struct DealData
  {
   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">//--
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;
   class="type">class="kw">double               tpPrice;
   class="type">class="kw">double               slPrice;
  };
class=class="str">"cmt">//--
class="kw">struct PositionData
  {
   class="type">ENUM_POSITION_TYPE type;
   class="type">ulong               ticket;
   ENUM_ORDER_TYPE     initiatingOrderType;
   class="type">ulong               positionId;
   class="type">bool                initiatedByPendingOrder;
   class="type">ulong               openingOrderTicket;
   class="type">ulong               openingDealTicket;
   class="type">ulong               closingDealTicket;
   class="type">class="kw">string              symbol;

◍ 成交与挂单的结构体落地

把历史成交和挂单分别装进两个结构体,回测或实盘统计时就能直接按字段筛,不必每次都调 OrderGet* 系列函数反复查终端。 double volume 存手数,openPrice / closePrice 记开平价格,openTime / closeTime 是时间戳,duration 用 long 算持仓秒数;commission 与 swap 分开存,profit 是毛利,netProfit 扣费后才是真账。tpPrice / slPrice 带绝对价,tpPips / slPips 存换算好的点数,pipProfit 直接给点数盈亏,magic 和 comment 留作策略标记。 PendingOrderData 这边覆盖挂单全状态:symbol 与 type(市价单外的六种挂单类型)、state 反映是否 still pending 或已成交取消;positionId 把挂单和后续仓位连起来,ticket 是终端唯一号。timeSetup 到 timeDone 配合 typeTime 能算出挂单存活时长,priceStopLimit 专供止损限价单用。 宏定义把时间窗口固化:NOW 取当前,ONE_DAY 回退到上一根 D1 开盘,ONE_WEEK 同理退到 W1。写统计模块时直接用 ONE_DAY 框范围,就能拉出「近 24 小时」的成交样本,外汇与贵金属波动大、杠杆高,样本里须自行剔除异常滑点。

MQL5 / C++
class="type">class="kw">double volume;
 class="type">class="kw">double openPrice;
 class="type">class="kw">double closePrice;
 class="type">class="kw">datetime openTime;
 class="type">class="kw">datetime closeTime;
 class="type">long duration;
 class="type">class="kw">double commission;
 class="type">class="kw">double swap;
 class="type">class="kw">double profit;
 class="type">class="kw">double tpPrice;
 class="type">class="kw">double slPrice;
 class="type">int tpPips;
 class="type">int slPips;
 class="type">int pipProfit;
 class="type">class="kw">double netProfit;
 class="type">ulong magic;
 class="type">class="kw">string comment;
 };
class=class="str">"cmt">//--
class="kw">struct PendingOrderData
  {
  class="type">class="kw">string symbol;
  ENUM_ORDER_TYPE type;
  ENUM_ORDER_STATE state;
  class="type">class="kw">double priceOpen;
  class="type">class="kw">double tpPrice;
  class="type">class="kw">double slPrice;
  class="type">int tpPips;
  class="type">int slPips;
  class="type">ulong positionId;
  class="type">ulong ticket;
  class="type">class="kw">datetime timeSetup;
  class="type">class="kw">datetime expirationTime;
  class="type">class="kw">datetime timeDone;
  ENUM_ORDER_TYPE_TIME typeTime;
  class="type">ulong magic;
  ENUM_ORDER_REASON reason;
  ENUM_ORDER_TYPE_FILLING typeFilling;
  class="type">class="kw">string comment;
  class="type">class="kw">double volumeInitial;
  class="type">class="kw">double priceStopLimit;
  };
class=class="str">"cmt">//--
class="macro">#define NOW class="type">class="kw">datetime(TimeCurrent())
class="macro">#define ONE_DAY class="type">class="kw">datetime(TimeCurrent() - PeriodSeconds(PERIOD_D1))
class="macro">#define ONE_WEEK class="type">class="kw">datetime(TimeCurrent() - PeriodSeconds(PERIOD_W1))

常见问题

先写头文件落地数据结构与宏,再分成交、挂单、持仓三类结构体,最后编译成库供项目调用。
优先写头文件骨架,把核心数据结构和宏定义好,避免后续结构体字段引用报错。
小布可以帮你逐行解读成交、挂单与持仓结构体的字段含义,并给出接入自己项目的检查清单。
成交结构体偏已发生价格与手续费,挂单结构体多有效期与挂单触发价,按字段拆解即可分清。
不要混用,持仓需独立字段如开仓价与浮盈,和成交结构分开定义更不易出错。