MT4测试器建模机制全解
搞懂逐笔与多周期建模,避免回测假象
为什么测试器是交易系统开发的核心
在金融市场中交易离不开交易系统。交易者花费大量精力制定开平仓规则,并基于经验选择追踪止损等仓位管理方法,这往往涉及多个学科的知识。创建策略后的第一件事,就是用历史数据测试机械交易系统(MTS)。因此,测试程序成为任何交易程序最重要的组成部分。并非所有支持技术分析的程序都具备测试选项,即便声明支持,也可能存在结构性限制或错误。MT4 在测试器架构设计上预先考虑了未来数据泄漏的问题,从体系结构层面阻止了整类依赖未来信息的策略出现。
两种历史测试建模类型对比
测试程序对策略的回放方式从根本上分为两类。第一类是基于已形成的柱体,预先准备包含价格、指标等所有必要值的数据文件,再将文件送入测试器,测试器只是沿着理论上可分析未来的序列前进。第二类是仅提供已建模的价格变动(tick),每次把价格改变送入测试器入口,就像真实行情推送一样,测试器本质上没有未来。三角形“当前时间”代表测试器所在时间点:第一种方法里,过去与未来都已计算好,测试器顺序读取,若策略能预测未来,结果必须彻底验证;第二种方法里只有过去,每次新 tick 到来才生成“当前”,前面永远是未知的“将来”,无论编写是否出错,测试器都无法预知未来。
- 第一种:预生成完整序列,测试快但有未来函数风险
- 第二种:仅推送 tick,等价实盘,杜绝未来泄漏
- MT4 采用第二种思路,将建模序列存为 fxt 文件
MT4 如何为不同时间范围建模
MT4 测试器不仅建模当前时间范围,还同步建模更高和更低周期。例如在 M15 上测试 EA,可以同时读取 H1 或 M5 的指标值与零柱最高最低价,调用 iHigh(NULL,PERIOD_D1,0) 即可获得当日最高价,与在线交易完全一致。测试器的任务是为所有必要周期正确建模:每次收到新 tick,不仅推进当前零柱,也同步更改所有其他周期的零柱状态,操作完全同步。所有时间范围的同步价格进度就像实盘一样,15 分钟的最低价与更高周期不同,但新 tick 到来时所有周期的买入价相同。
跨品种价格建模的限制
准确测试历史有时需要大量数据及内存和算力。MT4 测试器不允许组合测试多个品种,但允许在测试某品种时读取其他品种价格,此时不执行建模,数据按原样提取。进程初期零柱被简化为 High[0]=Low[0]=Close[0]=Open[0]、Volume[0]=1,只能在柱体开始知道价格,结束前不知。这意味着跨品种只能获得近似的当前柱状态,不能依赖其做精确的跨品种逻辑回测。
指标在测试期间如何计算
十年前受限于硬件,旧式测试只事先计算指标并以不变形式回放,导致零柱上使用的数据等于预测未来。MT4 测试器不接收任何预计算数据,只获取建模价格进度,并根据新 tick 实时计算所有指标,与在线操作方式相同。这是充分优势,但非最优指标算法会大幅拖慢测试速度。EURUSD 自 1999 年分钟历史近三百万柱,每次 tick 建模提供多次价格变动,任何低效都会放大。如今硬件虽强,仍有人误以为 intrabar 建模不正确,实际上 MT4 不仅提供而且要求基于历史的策略测试使用此方法。
//+------------------------------------------------------------------+
//| CheckModelling.mq4 |
//| Copyright © 2007, MetaQuotes Software Corp. |
//| https://www.metaquotes.net/|
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link "https://www.metaquotes.net/"
extern int DayS=21;
extern int MonthS=4;
extern int YearS=2006;
extern int HourS=10;
extern int MinuteS=00;
extern int CounterS=20;
int counter;
int start()
{
if (counter>CounterS) return;
if (Year()<2006) return;
if (Month()<MonthS) return;
if (Day()<DayS) return;
if (Hour()<HourS) return;
if (Minute()<MinuteS) return;
Print("My time frame "," Open=",Open[0]," High=",High[0]," Low=",Low[0],
" Close=",Close[0]," Volume=",Volume[0]," Bid=",Bid);
Print("30 minute frame "," Open=",iOpen(NULL,PERIOD_M30,0)," High=",iHigh(NULL,PERIOD_M30,0),
" Low=",iLow(NULL,PERIOD_M30,0)," Close=",iClose(NULL,PERIOD_M30,0),
" Volume=",iVolume(NULL,PERIOD_M30,0)," Bid=",Bid);
Print("1 hour frame "," Open=",iOpen(NULL,PERIOD_H1,0)," High=",iHigh(NULL,PERIOD_H1,0),
" Low=",iLow(NULL,PERIOD_H1,0)," Close=",iClose(NULL,PERIOD_H1,0),
" Volume=",iVolume(NULL,PERIOD_H1,0)," Bid=",Bid);
Print("4 hour frame "," Open=",iOpen(NULL,PERIOD_H4,0)," High=",iHigh(NULL,PERIOD_H4,0),
" Low=",iLow(NULL,PERIOD_H4,0)," Close=",iClose(NULL,PERIOD_H4,0),
" Volume=",iVolume(NULL,PERIOD_H4,0)," Bid=",Bid);
counter++;
return(0);
}
//+------------------------------------------------------------------+
常见误解与排查方法
MT4 自 2005 年发布后,论坛反复出现因不了解测试器而产生的疑问。其一是 EA 基于指标在错误位置交易:测试后打开图表可见进场点与指标调用,但历史计算的指标值可能与测试时实际值差异巨大,如 ZigZag 断点。不了解机制者以为能预测未来而盈利,实则可视化测试下假象消失。其二是测试指标值与在线不等:多为指标算法错误累积,测试计算与在线一致使错误暴露。其三是导入长历史后更高周期价格错误:因 fxt 与 hst 文件不一致,可能来自副源报价重转;正确做法是使用标准历史中心自 1999 年分钟报价,自动重算并消除时区偏移。其四是导入报价后更高周期指标异常:因其他周期源价不正确则无法算准指标,需确保测试前传输所有必要周期正确数据。
正确测试的数据一致性要求
MT4 测试器基于 hst 文件生成 fxt 建模文件。若 fxt 由其他时区、其他经纪人报价或手动编制,且与现有 hst 不一致,EA 调用 iOpen(NULL,PERIOD_D1,1) 等函数会基于 hst 计算,导致测试器内价格与文件历史截然不同。正确测试要求 fxt 与 hst 数据一致,不要从副源批处理导入,使用标准历史中心分钟报价即可自动重算所有周期并转换时区。另外,只有零柱价格建模后,才基于 hst 计算其他柱的标准和自定义指标,因此其他时间范围的必要价格数据必须正确传输。
总结与最佳实践
MT4 测试器凝结了 2000 年以来多代终端经验,旨在提供准确策略测试环境,省去数月实盘试错。理解其逐笔推送、多周期同步、实时算指标及数据一致性要求,能节约时间并深化对 EA 开发的认识。建议中文交易者放弃旧式“预计算”思维,采用可视化测试核对,依赖标准历史中心,避免手工报价导入,从而让回测结果真正贴近实盘。