测试指标

MetaTrader 5 内置测试程序支持两种 MQL 程序:EA 交易和指标。指标始终在可视化窗口中进行测试。但这仅适用于单独测试某个指标的情况。如果指标是通过 EA 交易以编程方式创建和调用的,则用户可以自行决定是否在无可视化对象的情况下测试该 EA 交易及其指标。我们将在下一章学习如何通过 MQL 代码使用指标的技术。同样的技术也将用于与 EA 交易集成。

同时,指标开发者应注意:在无可视化对象的情况下,测试程序会对 EA 交易调用的指标使用加速计算方法。不会在每个分时报价出现时都计算数据,而是仅当用户在从指标缓冲区请求相关数据时才会触发计算(请参阅 CopyBuffer 函数)。

如果尚未根据当前分时报价计算指标,则在第一次访问其数据时会进行一次计算。如果在同一个分时报价内产生其他请求,则则计算好的数据将以就绪状态返回。如果当前分时报价未读取指标缓冲区,则不会进行计算。按需计算指标可显著提升测试和优化的速度。

如果特定指标需要精确计算且不可跳过任何分时报价,MQL5 可通过指令要求测试程序对每个分时报价重新计算指标:这可以通过以下指令实现:

#property tester_everytick_calculate

该指令中的everytick关键词专门用于控制指标的计算方式,不影响分时报价生成模式。换言之,分时报价指的是由测试程序生成的价格变动(无论是分时报价、OHLC M1 价格还是柱线开盘价),该测试程序的设置始终保持有效。

对于本章所讨论的指标而言,该特性并非关键所在。需要特别说明的是,该特性仅适用于策略测试程序中的操作。在终端中,指标会在每个分时报价到来时触发 OnCalculate事件(如果 OnCalculate 的计算耗时过长,导致在新分时报价到来前未能完成,则可能会跳过部分分时报价)。

在测试程序中,指标将在以下任一条件下按每个分时报价计算:

我们来尝试测试上一节的 IndMultiSymbolMonitor.mq5指标。

选择 EURUSD, H1 图表作为交易品种和时间范围。分时报价生成方法设置为“基于真实分时报价”。

启动测试后,我们应在可视化模式窗口的日志中看到以下条目:

2021.10.20 00:00:00 New bar(s) on: EURUSD USDCHF USDJPY , in-sync:false

2021.10.20 00:00:00 New bar(s) on: AUDUSD , in-sync:false

2021.10.20 00:00:00 New bar(s) on: GBPUSD , in-sync:false

2021.10.20 00:00:02 New bar(s) on: USDCAD , in-sync:false

2021.10.20 00:00:11 New bar(s) on: NZDUSD , in-sync:true

2021.10.20 01:00:04 New bar(s) on: EURUSD GBPUSD USDCHF USDJPY AUDUSD USDCAD NZDUSD , in-sync:true

2021.10.20 02:00:00 New bar(s) on: EURUSD USDJPY NZDUSD , in-sync:false

2021.10.20 02:00:00 New bar(s) on: USDCHF , in-sync:false

2021.10.20 02:00:01 New bar(s) on: AUDUSD , in-sync:false

2021.10.20 02:00:15 New bar(s) on: GBPUSD USDCAD , in-sync:true

2021.10.20 03:00:00 New bar(s) on: EURUSD AUDUSD NZDUSD , in-sync:false

2021.10.20 03:00:00 New bar(s) on: GBPUSD USDJPY USDCAD , in-sync:false

2021.10.20 03:00:12 New bar(s) on: USDCHF , in-sync:true

正如你所看到的,不同的交易品种上会逐渐出现新的柱线。通常,在“in-sync”标志设置为 true之前会发生多个事件。

你也可以对本章的其他指标进行测试。请注意:如果 MQL 程序查询分时报价历史数据,在测试程序中选择“基于真实分时报价”的生成方法。

“按开盘价”测试仅适用于开发时支持此模式的指标和 EA 交易,例如,仅通过 Open价进行计算或从第一根柱线开始分析已完成柱线的程序。

注意!在测试程序中进行指标测试时,OnDeinit事件无效。此外,其他终止操作同样不会执行,例如不会调用全局对象的析构函数。