来自 MQL5 向导的预制专家交易系统运作于 MetaTrader 4 平台中·综合运用
「把历史订单伪装成成交记录的取值逻辑」
在 MT5 回测或历史分析里,直接调 HistoryDealGet 系列函数拿不到早期订单的明细,常见做法是写一层兼容封装,把 OrderSelect 的旧字段映射到成交属性上。下面这段就是典型实现:用 ticket 的最高位(0x8000000000000000)标记是否为平仓单,再按属性枚举分流取值。 时间字段里,开仓单取 OrderOpenTime,平仓单乘 1000 取 OrderCloseTime,毫秒级精度靠 *1000 补齐;类型字段通过 OrderType2DealType 转换,平仓时还套了 REVERSE 反推。Magic 号直接读 OrderMagicNumber,不受进出场标记影响。 浮点类属性更需注意进出场分工:DEAL_VOLUME 永远取 OrderLots;DEAL_PRICE 开仓读 OrderOpenPrice、平仓读 OrderClosePrice;佣金只在开仓记 OrderCommission, swap 和 profit 只在平仓单回写 OrderSwap / OrderProfit,否则填 0。 字符串类只有品种和注释,分别映射 OrderSymbol 与 OrderComment。整套逻辑跑在 HistoryDealGetLong/Double/String 三个重载里,未匹配的属性统一 return false。开 MT5 把这段塞进 ea 的回测调试块,能直接验证老账户历史是否被正确识别。
case DEAL_TIME_MSC: long_var = (exit ? OrderCloseTime() : OrderOpenTime()) * class="num">1000; class="kw">return true; case DEAL_TYPE: long_var = OrderType2DealType(exit ? REVERSE(OrderType()) : OrderType()); class="kw">return true; case DEAL_ENTRY: long_var = exit ? DEAL_ENTRY_OUT : DEAL_ENTRY_IN; class="kw">return true; case DEAL_MAGIC: long_var = OrderMagicNumber(); class="kw">return true; } } class="kw">return false; } class="type">bool HistoryDealGetDouble(class="type">class="kw">ulong ticket_number, ENUM_DEAL_PROPERTY_DOUBLE property_id, class="type">class="kw">double &double_var) { class="type">bool exit = ((ticket_number & 0x8000000000000000) != class="num">0); ticket_number &= ~0x8000000000000000; class="kw">switch(property_id) { case DEAL_VOLUME: double_var = OrderLots(); class="kw">return true; case DEAL_PRICE: double_var = exit ? OrderClosePrice() : OrderOpenPrice(); class="kw">return true; case DEAL_COMMISSION: double_var = exit? class="num">0 : OrderCommission(); class="kw">return true; case DEAL_SWAP: double_var = exit ? OrderSwap() : class="num">0; class="kw">return true; case DEAL_PROFIT: double_var = exit ? OrderProfit() : class="num">0; class="kw">return true; } class="kw">return false; } class="type">bool HistoryDealGetString(class="type">class="kw">ulong ticket_number, ENUM_DEAL_PROPERTY_STRING property_id, class="type">class="kw">string &string_var) { class="kw">switch(property_id) { case DEAL_SYMBOL: string_var = OrderSymbol(); class="kw">return true; case DEAL_COMMENT: string_var = OrderComment(); class="kw">return true; } class="kw">return false; }
把 MT5 向导 EA 搬进 MT4 实测
把适配标准库的交易类按子目录层级丢进 MetaTrader 4 的 Include 目录,再把 MT5Bridge.mqh 拷到 Include/Trade,就能直接在 MT4 里编译运行 MT5 向导产出的 EA。 拿 Experts/Advisors 里的 ExpertMACD.mq5 做样本,复制到 MQL4/Experts 改名 ExpertMACD.mq4 后编译,结果是零错误零警告。但编译干净不代表逻辑无坑,只是说明接口层接上了。 在 MT4 测试器用默认设置跑一遍,日志里没有订单处理报错;EURUSD M15 上 EA 正常开平仓,止损止盈位也按预期挂着。 和 MT5 策略测试器的结果摆一起看,曲线形态接近、交易笔数大体一致,差异主要来自报价精度(MT5 用浮点)和两边测试算法不同。外汇与贵金属品种点差和滑点敏感,跨平台回测只能看概率倾向,不能当实盘保证。 我们顺手测了带尾随停止和可变手数的自定义组合,也能顺利迁到 MT4——只要你向导里用的模块不越出桥接层覆盖的范围。
◍ MT5向导EA迁MT4的落地边界
把 MT5 向导产出的 EA 挪到 MT4 跑,核心思路是尽量复用 MT5 标准库的交易类代码,实现成本确实低。代价是你本机得同时开着两个终端:一个出 EA 源码,另一个负责实跑,这对只想单机轻量部署的人是个摩擦点。 随文给了两个关键文件:一个是标准库补丁包(145.99 KB 的 ZIP),解压进 MT4 的 Include 目录并保持子目录层级,它只补终端没自带的部分,不会覆盖你现有文件;另一个是 MT5Bridge.mqh(32.35 KB),要单独丢进 Include/Trade。这套库提取自 MT5 build 1545,后续版本标准库若变动,你可能得手工把仿真层重新合并一次。 内核层面,MT4 不可能完全仿真 MT5 的交易环境——新终端不少功能是内核级新增的,所以向导生成的 EA 里某些模块有概率在 MT4 下失效。另外这套桥接层是以 beta 状态放出的,隐藏 bug 不能说没有,外汇和贵金属本身高波动高风险,拿去实盘前建议先拉长周期多场景回测。 论坛上有人遇到 MT4 取不到 SYMBOL_FILLING_MODE 的尴尬,作者后来补了一段兼容函数。下面这段代码把 MT5 的填充类型映射成 MT4 的符号填充模式位,直接替换原来的 SymbolInfoInteger 调用即可验证。 别把填充模式映射想简单了 MT4 里 SYMBOL_FILLING_FOK=1、SYMBOL_FILLING_IOC=2,而 ORDER_FILLING_FOK=0、ORDER_FILLING_IOC=1,所以函数在末尾 +1 做偏移。若你写的 EA 报填充相关检查错,先把 m_type_filling 走这套 GetSymbolFilling 再塞进请求结构。
class=class="str">"cmt">//funtion 返回 MT4 的符号填充模式,又称 SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE)。 class=class="str">"cmt">//参数类型 - 订单填写类型 class="type">uint GetSymbolFilling( class="kw">const class="type">class="kw">string Symb, class="kw">const class="type">uint Type = ORDER_FILLING_FOK ) { class="kw">const ENUM_SYMBOL_TRADE_EXECUTION ExeMode = (ENUM_SYMBOL_TRADE_EXECUTION)::SymbolInfoInteger(Symb, SYMBOL_TRADE_EXEMODE); class="kw">const class="type">int FillingMode = (class="type">int)::SymbolInfoInteger(Symb, SYMBOL_FILLING_MODE); class="kw">return ((FillingMode == class="num">0 || (Type >= ORDER_FILLING_RETURN) || ((FillingMode & (Type + class="num">1)) != Type + class="num">1)) ? (((ExeMode == SYMBOL_TRADE_EXECUTION_EXCHANGE) || (ExeMode == SYMBOL_TRADE_EXECUTION_INSTANT)) ? ORDER_FILLING_RETURN : ((FillingMode == SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) : (ENUM_ORDER_TYPE_FILLING)Type) + class="num">1; } class="type">uint filling = GetSymbolFilling(symbol, m_type_filling); class=class="str">"cmt">//而不是 class=class="str">"cmt">//class="type">uint filling = (class="type">uint)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
「画得少,看得清」
把写好的 mq5 专家文件塞进 MQL4/Experts 之前,记得把扩展名手动改成 mq4——MetaEditor 虽能同时编译两套语言,但 MQL5 内建类型会和 MT5Bridge.mqh 里的模拟类型打架,编译必然失败,这也不是设计目标。 顺带提一句前文几组实测:趋势与平盘的时长占比常被说成 30% 对 70%,跑完样本验证下来,这个比例在多数品种上只是近似而非铁律;十款趋势策略横向比下来,优缺点都挂在过滤器和持仓周期上。 少堆控件、少写冗余逻辑,桥接文件改名即可跑通,界面回卷算法之类能省就省。盘面信息密度降下来,该看的信号反而更清楚。