MetaTrader 5 中进行测试的原理(基础篇)
📘

MetaTrader 5 中进行测试的原理(基础篇)

第 1/3 篇

MT5回测引擎到底在跑什么

MT5 的策略测试器不是简单把历史K线重放一遍。它按 tick 级别重建市场:先读历史报价,再结合点差、库存费、成交模式(即时或交易所)去模拟每一笔下单与平仓。2013 年官方文档给出的测试器基础架构,至今仍是多数 EA 回测可信度的底层约束。 一个常被忽略的事实是:默认「每笔 tick」模式下,测试器会用相邻分钟棒内的插值生成 tick,并非真实成交 tick。这意味着剥头皮类策略在回测里的滑点表现,可能比实盘更乐观。 想验证这点,开 MT5 随便挂一个 EA 跑 2023 年 XAUUSD 的 H1,对比「每笔 tick」和「真实 tick(需下载历史)」两种模式下的净利润曲线,多数账户会看到真实 tick 下回撤更大。外汇与贵金属杠杆高,回测偏差放大后实盘亏损概率显著上升,动手前先认清这点。

「先证两件事,再让 EA 替你盯盘」

让 EA 接管盘面,图的是它不困不慌、24×7 按规则下单,但上线前得先确认两件事:一是 EA 真的只按你定的交易系统规则出手,二是这套规则在历史行情里确实有过正期望。 MetaTrader 5 自带的 Strategy Tester 就是干这个的——把 EA 丢进去跑历史数据,看它是否严守规则、净值曲线是否向上。外汇和贵金属杠杆高、滑点跳空频繁,历史盈利不代表未来,只能当作可证伪的参考。 开 MT5 按 Ctrl+R 调出测试器,选一个现成 EA 跑 2020—2023 年 EURUSD 的 M1 数据,重点看成交笔数和策略日志是否对得上你的规则描述。

◍ MT5回测里三种价格变动生成模式的差异

MT5 策略测试器提供三种价格变动生成模式:Every tick、1 Minute OHLC、Open prices only。Every tick 最细,每个控制点都触发 EA 的 OnTick(),但耗时最长;后两种是它的简化版,速度换精度。 历史报价以封装的分钟柱从服务器下发,每根柱只明确给四个控制点:开、高、低、收。比如 2011.1.10 00:00 那根 EURUSD 柱,开盘价 1.28940、收盘 1.28958、高 1.28958、低 1.28940,但高低压根不知道出现在第几秒。Every tick 会在四价之间插值补出额外变动点,模拟真实 tick 流。

  • Minute OHLC 只认四价,不补中间点。这会带来确定性欺骗——像代码库里的 Grr-al EA,靠「开盘后第一跳低于开盘价就买、下一跳卖」的死逻辑,在 OHLC 模式画出漂亮向上资金曲线;切到 Every tick 一跑,实盘真相暴露:平衡走平但趋势向下。所以粗模好看必须回 Every tick 复核。

Open prices only 更狠:仅在柱开盘价触发一次 OnTick()。EURUSD H1 上 2010.9.1–12.31 测标准版 Moving Average,三种模式资金曲线完全重合,因为它只在柱开做决策、不挂单不拖止损。但该模式禁随机延迟,且不能访问低于测试周期的数据——H1 测就摸不到 M30,M20 测摸不到 M30 但能碰 H1。外汇贵金属波动剧烈,回测模式选错可能把实盘风险完全掩盖。 想自己看差异,可用 WriteTicksFromTester.mq5 把三种模式的 tick 存盘,再用 TicksFromTester.mq5 指标叠图。EA 与指标靠 FILE_COMMON 标志共用沙箱文件,免手动搬文件。

MQL5 / C++
class=class="str">"cmt">//--- 打开文件
  file=FileOpen(filename,FILE_WRITE|FILE_CSV|FILE_COMMON,";");
class=class="str">"cmt">//--- 检查文件句柄
  if(file==INVALID_HANDLE)
    {
      PrintFormat("Error in opening of file %s for writing. Error code=%d",filename,GetLastError());
      class="kw">return;
    }
  else
    {
      PrintFormat("The file will be created in %s folder",TerminalInfoString(TERMINAL_COMMONDATA_PATH));
    }

公共目录里读 CSV 的句柄检查

在 MT5 里用 FileOpen 读共享 CSV,必须带 FILE_COMMON 标志,文件才会从 TerminalInfoString(TERMINAL_COMMONDATA_PATH) 返回的公共数据目录加载,而不是当前品种私有的 files 夹。 下面这段代码演示了只读 + CSV + 公共目录的三标志组合,分隔符用分号。句柄若等于 INVALID_HANDLE,说明文件没找到或权限不足,这时候直接用 GetLastError 把错误码打出来,比瞎猜快得多。 很多人忽略 else 分支里的路径回显,其实它在排查“文件明明在却读不到”时很关键:能确认 EA 实际去哪个公共路径找的文件。外汇与贵金属品种切换时终端公共路径一致,但文件写入端若来自别的程序,权限冲突概率不低,需实测确认。

MQL5 / C++
  class="type">int file=FileOpen(fname,FILE_READ|FILE_CSV|FILE_COMMON,";");
class=class="str">"cmt">//--- 检查文件句柄
  if(file==INVALID_HANDLE)
    {
      PrintFormat("Error in open of file %s for reading. Error code=%d",fname,GetLastError());
      class="kw">return;
    }
  else
    {
      PrintFormat("File will be opened from %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH));
    }

「回测里的点差从哪来」

MT5 策略测试器不对点差做数学建模,而是直接吃历史数据里的点差值。如果某根 K 线的历史点差记录 ≤0,测试代理会回退到当时请求品种的实际点差,这一点在跑老数据时要心里有数。 测试环境里点差永远被当成浮动类型处理,SymbolInfoInteger(symbol, SYMBOL_SPREAD_FLOAT) 固定返回 true,跟你账户里是不是固定点差品种无关,写 EA 时别拿这个返回值去判断真实环境。 历史数据除了 OHLC,还带每次价格跳动量和成交量,底层用 MqlRates 结构存取。下面这段是结构定义,逐行看字段就知道回测能拿到哪些东西: time 是柱形开始时间;open/high/low/close 分别是开高低收;tick_volume 记录每个价格变动的交易量;spread 就是该根 K 线的点差;real_volume 是市场真实成交量(多数外汇品种为 0)。 外汇和贵金属杠杆高、点差跳变频繁,回测若用到的 spread 字段为空或异常,实盘滑点可能比测试残酷得多,上机前先打印几根历史 K 线的 spread 确认数据质量。

MQL5 / C++
class="kw">struct class="type">MqlRates
  {
   class="type">class="kw">datetime time;          class=class="str">"cmt">// 柱形开始时间
   class="type">class="kw">double   open;          class=class="str">"cmt">// 开盘价
   class="type">class="kw">double   high;          class=class="str">"cmt">// 最高价
   class="type">class="kw">double   low;           class=class="str">"cmt">// 最低价
   class="type">class="kw">double   close;         class=class="str">"cmt">// 收盘价
   class="type">long     tick_volume;   class=class="str">"cmt">// 每个价格变动的交易量
   class="type">int      spread;        class=class="str">"cmt">// 点差
   class="type">long     real_volume;   class=class="str">"cmt">// 市场交易量
  };

◍ 回测里的全局变量是另一套

MT5 策略测试器会模拟客户端全局变量,但按 F3 调出的那张全局变量表,跟回测跑出来的不是同一份。 测试代理在独立空间里维护这些全局变量,不会写入你实盘客户端里能看到的当前全局变量区。 所以凡是依赖 GlobalVariableGet / GlobalVariableSet 做跨 EA 通信或状态记忆的逻辑,回测结果和实盘表现可能脱节——验证时得单独在实盘客户端按 F3 核对一遍。

常见问题

回测按历史 tick 和设定的价格变动模式重演市场,撮合你的订单;和实盘差别主要来自点差、滑点及数据质量,先用小周期验证再放大周期。
先确认文件已放到公共目录且文件名大小写一致,再用 FileIsHandle 检查句柄有效性;返回无效就检查打开权限和路径拼接。
小布可以替你跑基础诊断,比如检查csv句柄、点差设置是否合理,并把异常直接标在品种页,你只管看结论。
默认用测试器里设的固定点差;若选实时点差模式则按历史数据走,贵金属和外汇高风险,参数不对结果会偏乐观。
不会,回测全局变量是测试环境独立的一套,和实盘终端的全局变量隔离,不用担心串号或误触发。