开发回放系统 市场模拟(第 26 部分):智能交易系统项目 C_Terminal 类(基础篇)
把终端塞进回放系统的 C_Terminal 类
在 MT5 回放系统的第 26 部分里,核心是把真实终端的能力抽象成一个 C_Terminal 类,让历史行情回放时能像实盘一样被 EA 调用。这个类不负责画 K 线,只管把“当前上下文”喂给交易逻辑:品种、时间帧、tick 队列、账户状态都在它内部维护。 从工程角度,C_Terminal 必须和 MQL5 的 CTerminal 标准类解耦,否则回放时一调 TerminalInfoInteger 就会穿透到真实账户。原文给出的做法是自己存一份镜像状态,EA 只认回放实例,实盘实例在测试器里被完全屏蔽。 一个可验证的落点:原文发布于 2024-05-13,在 mql5 社区该帖获 677 浏览、1 条评论,说明这类底层封装需求虽小众但有人盯。你在 MT5 里新建 EA,include 自己写的 C_Terminal 头文件,把 OnTick 里的 TerminalInfo* 调用全部改为实例方法,就能跑通离线回放而不碰真账户。外汇与贵金属品种点差跳变频繁,回放若未镜像 spread 字段,结果会偏乐观,属高风险验证项。
「为什么要在回放系统里塞一个通用智能系统」
上一阶段我们已经搭好了基础用途的回放/模拟环境,但那只能看过去走势,没法像真盘一样做针对性研究。现在要做的,是把一套能深度研究的智能系统接进去,而且它得通吃股票和外汇,还得跟现有回放框架兼容。 这事看起来工程量吓人,其实大部分底层流程在早前的自动化 EA 文章里已经铺好了——从完全自动化交易系统的搭建到通用模块拆分都有现成路径。真正的硬骨头只有一个:在本地模拟 MetaTrader 5 与交易服务器的连接,把开放市场的撮合和回报做得足够逼真。 很多人一看到‘模拟服务器连接’就觉得不可能,但编程本身就是把‘以为很难’变成‘跑通为止’。先开 MT5 把之前的回放工程加载起来,确认基础模拟能跑,再想怎么往里挂智能逻辑,比干想难度有用得多。外汇和贵金属这类品种波动受杠杆和流动性影响极大,模拟再真也替代不了实盘风险,验证时务必用小资金或纯 demo。
◍ EA 资产适配与数值硬约束
做 EA 之前先想清楚一件事:你打算让它跑在什么品种上。不同资产的最小价格变动单位差得很远——有的 0.01,有的 0.5,还有的直接 5 起步。外汇里这种差异尤其刺眼,随便填个手数或挂单价,交易服务器根本不认。 很多程序员干脆只为一个品种写 EA,不是偷懒,是服务器规则逼的。回放和模拟系统也一样,不能让 EA 发出带随机数值的订单,否则练了半天和实盘对不上,等于白练。 所以 EA 从设计起就要按「直接连交易服务器」的标准来:订单参数走标准化,尽量贴实盘真实环境。外汇和贵金属杠杆高、跳空频繁,这种数值错配在极端行情下可能直接废单或错单,风险不小。 键盘鼠标虽是 MT5 图表交互的基础,但本篇先不展开;核心是——别指望一套数值逻辑通吃所有品种,开 MT5 测之前先核对合约规格里的最小变动价位。
用 C_Terminal 类把期货合约名算准
做 MT5 自动化,项目再小也建议从类结构起步。把终端相关逻辑收进 C_Terminal.mqh,文件与类同名,后面在 EA 里找起来不费劲。类里全局变量必须丢在 private 段,外部代码一律不准直改,否则封装破掉,出 bug 都难定位。 新建 Macros.mqh 只先放一行:用宏高亮日期。宏会以内联方式插进代码,跑起来比函数调用更快,后面多次重构重复代码时也不容易抄错。把它 include 进 C_Terminal.mqh 时用双引号 "Macros.mqh",编译器从当前头文件目录找;若用 <Macros.mqh> 则去 MQL5/Include 找,目录一变就得重配路径。 类里第一段 private 代码按 B3 规则拼指数期货和美元期货名。TimeCurrent 一行同时初始化 MqlDateTime 结构 mdt1 和宏提取的日期 dt;枚举强制从零开始(WIN=0, IND=1, WDO=2…),末尾必须是 OTHER。要加新合约,比如 OX,就在 OTHER 前插 BGI,否则枚举不按预期工作。 拼名靠 for 循环遍历枚举,用 EnumToString 把值转字符串和资产前三位比,命中就存进 eTS。之后按合约初始化 sz1 字母,未匹配就直接 return——回放模式下的特殊品种常走这条退路。 第二个循环用数学规则生成 WDO 合约名:前三位 WDO + 月份品名 + 两位年份。StringFormat 拼名后,用 SymbolInfoInteger(SYMBOL_EXPIRATION_TIME) 取到期时间,宏比当前日期,是未来日就锁定最近合约。年份从 2000 起,靠两个三元运算符先增月、月归零再增年,直到找到有效合约。 构造函数里关掉图表对象描述、开启对象删除事件;析构函数把时间刻度恢复可见。更稳的做法是用一个全局结构存修改前图表状态,构造时记、析构时还原。类不是代码堆,是带生命周期的对象:实例化跑构造,销毁跑析构。 两个待细讲的函数已露面:一个让类外只读访问私有数据,避免外部乱改;另一个在图表变动时刷新类内数据。下篇再拆,现在你开 MT5 建 C_Terminal.mqh 把枚举和拼名循环抄进去,换你做的品种前三字母试编译就能验证。