- Генерация тиков в тестере
- Управление ходом времени в тестере: таймер, Sleep, GMT
- Визуализация тестирования: график, объекты, индикаторы
- Мультивалютное тестирование
- Критерии оптимизации
- Получение финансовых показателей теста: TesterStatistics
- Событие OnTester
- Авто-настройка: ParameterGetRange и ParameterSetRange
- Группа OnTester-событий для контроля оптимизации
- Отправка фреймов данных с агентов в терминал
- Получение фреймов данных в терминале
- Директивы препроцессора для тестера
- Управление видимостью индикаторов: TesterHideIndicators
- Эмуляция пополнения депозита и снятия средств
- Принудительная остановка тестирования: TesterStop
- Большой пример эксперта
- Математические вычисления
- Отладка и профилирование
- Ограничения работы функций в тестере
Мультивалютное тестирование
Как известно, тестер MetaTrader 5 позволяет проверять стратегии, торгующие на нескольких инструментах. Чисто технически, с оглядкой на доступные "железные" ресурсы компьютера, можно моделировать одновременную торговлю по всем доступным инструментам.
Тестирование таких стратегий налагает на тестер несколько дополнительных технических требований:
- генерация последовательностей тиков для всех инструментов;
- расчет индикаторов для всех инструментов;
- расчет маржевых требований и эмуляция прочих торговых условий по всем инструментам.
История по используемым инструментам закачивается тестером из терминала автоматически при первом обращении к ней. Если в терминале не окажется требуемой истории, он в свою очередь запросит её с торгового сервера. Поэтому перед началом тестирования мультивалютного эксперта рекомендуется выбрать требуемые инструменты в Обзоре рынка терминала и подкачать данные на нужную глубину.
Агент закачивает недостающую историю с небольшим запасом, чтобы обеспечить необходимые данные для расчета индикаторов или копирования экспертом на момент начала тестирования. Минимальный объем истории, скачиваемой с торгового сервера, зависит от таймфрейма. Так для таймфреймов D1 и меньше он составляет один год. Иными словами, предварительная история закачивается от начала предыдущего года относительно стартовой даты тестера. Это дает как минимум 1 год истории, если тестирование задано с первого января, и как максимум чуть меньше двух лет, если тестирование идет с декабря. Для недельного таймфрейма запрашивается история в 100 баров, то есть примерно два года (в году 52 недели). Для тестирования на месячном таймфрейме агент запросит 100 месяцев (то есть историю примерно за 8 лет: 12 месяцев * 8 лет = 96). В любом случае, на более младших таймфреймах, чем рабочий, будет доступно пропорционально большее количество баров. Если существующих данных не хватает для предопределенной глубины предварительной истории, об этом факте будет запись в журнале тестирования.
Настроить (изменить) данное поведение нельзя. Поэтому, если нужно с самого начала обеспечить заданное количество исторических баров текущего таймфрейма, следует ставить более раннюю стартовую дату теста и затем "дожидаться" в коде эксперта наступления нужной даты активации торговли или достаточного количества баров, а до того пропускать все события "вхолостую".
В тестере также эмулируется свой Обзор рынка, из которого программа может получать информацию по инструментам. По умолчанию в начале тестирования в Обзоре рынка тестера есть только один символ — символ, на котором запущено тестирование. Все дополнительные символы добавляются в Обзор рынка тестера автоматически при обращении к ним через функции API. При первом же обращении к "чужому" символу из MQL-программы агент тестирования произведет синхронизацию по этому символу с терминалом.
Обращение к данным чужого символа происходят в следующих случаях:
- использование технических индикаторов, iCustom, IndicatorCreate на паре символ/таймфрейм
- запрос к Обзору рынка по чужому символу:
- SeriesInfoInteger
- Bars
- SymbolSelect
- SymbolIsSynchronized
- SymbolInfoDouble
- SymbolInfoInteger
- SymbolInfoString
- SymbolInfoTick
- SymbolInfoSessionQuote
- SymbolInfoSessionTrade
- MarketBookAdd
- MarketBookGet
- запрос к таймсерии по паре символ/таймфрейм функциями:
- CopyBuffer
- CopyRates
- CopyTime
- CopyOpen
- CopyHigh
- CopyLow
- CopyClose
- CopyTickVolume
- CopyRealVolume
- CopySpread
Кроме того, можно явно запросить историю для нужных символов с помощью вызова функции SymbolSelect в обработчике OnInit — загрузка истории будет произведена заранее, до начала тестирования советника.
В тот момент, когда происходит первое обращение к чужому символу, процесс тестирования останавливается и происходит подкачка истории по паре символ/период от терминала к агенту тестирования. Одновременно включается генерация тиковой последовательности.
Для каждого инструмента генерируется собственная тиковая последовательность согласно установленному режиму генерации тиков.
Особую важность при реализации мультивалютных советников играет синхронизация баров разных символов, так как от этого зависит правильность расчетов. Синхронизированным считается состояние, когда последние бары всех используемых символов имеют одно и то же время открытия.
Тестер генерирует и проигрывает для каждого инструмента его тиковую последовательность. При этом новый бар на каждом инструменте открывается независимо от того, как открываются бары на других инструментах. Это означает, что при тестировании мультивалютного эксперта возможна ситуация (и чаще всего так и бывает), когда на одном инструменте новый бар уже открылся, а на другом еще нет.
Например, если мы тестируем эксперт на символе EURUSD, и здесь открылась новая часовая свеча, то мы получим событие OnTick. Но при этом нет никакой гарантии, что новая свеча открылась по символу GBPUSD, который, допустим, нас тоже интересует.
Таким образом, алгоритм синхронизации подразумевает, что нужно проверять котировки всех инструментов и дождаться равенства времен открытия последних баров.
Это не вызывает вопросов до тех пор, пока используются режимы тестирования на реальных тиках, эмуляции всех тиков или OHLC M1. При этих режимах в пределах одной свечи генерируется достаточное количество тиков, чтобы дождаться момента синхронизации баров с разных символов. Достаточно завершить работу функции OnTick и проверить появление нового бара на GBPUSD на следующем тике. Но при тестировании в режиме "Только цены открытия" другого тика не будет, так как эксперт вызывается только один раз за бар, и может показаться, что этот режим не годится для тестирования мультивалютных экспертов. На самом деле тестер позволяет засечь момент, когда на другом символе откроется новый бар с помощью функции Sleep (в цикле) или таймера.
Для начала рассмотрим пример эксперта SyncBarsBySleep.mq5, демонстрирующего синхронизацию баров через Sleep.
Пара входных параметров позволяет задать размер паузы (Pause) в секундах для ожидания "чужих" баров, а также название "чужого" символа (OtherSymbol) — он должен отличаться от символа графика.
input uint Pause = 1; // Pause (seconds)
|
Для выявления закономерностей в запаздывании времен открытия баров опишем простой класс BarTimeStatistics. Он содержит поле для подсчета общего числа баров (total) и числа баров, на которых не было изначально синхронизации (late), то есть "чужой" символ запаздывал.
class BarTimeStatistics
|
Полученную статистику объект данного класса печатает в своем деструкторе. Поскольку мы собираемся сделать этот объект статическим, отчет выведется в самом конце теста.
Если выбранный в тестере режим генерации тиков будет отличаться от цен открытия, обнаружим это с помощью рассмотренной ранее функции getTickModel и выдадим пользователю предупреждение.
void OnTick()
|
Далее в OnTick идет непосредственно рабочий алгоритм синхронизации.
// время последнего известного бара для _Symbol
|
Настроим тестер для работы эксперта на EURUSD, H1, как наиболее ликвидном инструменте. Параметры эксперта оставим по умолчанию, то есть "чужим" символом будет USDJPY.
В результате теста журнал будет содержать примерно такие записи (в журнале намеренно оставлены строки о подкачке истории USDJPY, которая произошла при первом обращении к iTime).
2022.04.15 00:00:00 Last bar on EURUSD is 2022.04.15 00:00 USDJPY: load 27 bytes of history data to synchronize in 0:00:00.001 USDJPY: history synchronized from 2020.01.02 to 2022.04.20 USDJPY,H1: history cache allocated for 8109 bars and contains 8006 bars from 2021.01.04 00:00 to 2022.04.14 23:00 USDJPY,H1: 1 bar from 2022.04.15 00:00 added USDJPY,H1: history begins from 2021.01.04 00:00 2022.04.15 00:00:00 Bars are in sync at 2022.04.15 00:00:00 2022.04.15 01:00:00 Last bar on EURUSD is 2022.04.15 01:00 2022.04.15 01:00:00 Wait 1 seconds... 2022.04.15 01:00:01 Bars are in sync at 2022.04.15 01:00:01 2022.04.15 02:00:00 Last bar on EURUSD is 2022.04.15 02:00 2022.04.15 02:00:00 Wait 1 seconds... 2022.04.15 02:00:01 Bars are in sync at 2022.04.15 02:00:01 ... 2022.04.20 23:59:59 95 bars on USDJPY was late among 96 total bars on EURUSD (99.0%) |
Видно, что бары на USDJPY регулярно запаздывают. Если выбрать в настройках тестера USDJPY, H1, а в параметрах эксперта EURUSD, получим обратную картину.
2022.04.15 00:00:00 Last bar on USDJPY is 2022.04.15 00:00 EURUSD: load 27 bytes of history data to synchronize in 0:00:00.002 EURUSD: history synchronized from 2018.01.02 to 2022.04.20 EURUSD,H1: history cache allocated for 8109 bars and contains 8006 bars from 2021.01.04 00:00 to 2022.04.14 23:00 EURUSD,H1: 1 bar from 2022.04.15 00:00 added EURUSD,H1: history begins from 2021.01.04 00:00 2022.04.15 00:00:00 Bars are in sync at 2022.04.15 00:00:00 2022.04.15 01:00:00 Last bar on USDJPY is 2022.04.15 01:00 2022.04.15 01:00:00 Wait 1 seconds... 2022.04.15 01:00:01 Bars are in sync at 2022.04.15 01:00:01 2022.04.15 02:00:00 Last bar on USDJPY is 2022.04.15 02:00 2022.04.15 02:00:00 Wait 1 seconds... 2022.04.15 02:00:01 Bars are in sync at 2022.04.15 02:00:01 ... 2022.04.20 23:59:59 23 bars on EURUSD was late among 96 total bars on USDJPY (24.0%) |
Здесь в большинстве случаев ждать не приходилось — бары на EURUSD уже существовали в момент формирования бара на USDJPY.
Есть и другой способ синхронизации баров — с помощью таймера. Пример такого эксперта SyncBarsByTimer.mq5 прилагается к книге. Обратите внимание, что в нем события таймера, как правило, приходятся внутрь бара (т.к. вероятность попасть точно на начало ничтожно мала). Из-за этого бары оказываются синхронизированными почти всегда.
Мы могли бы еще напомнить о возможности синхронизации баров с помощью индикатора шпиона EventTickSpy.mq5, но он основан на пользовательских событиях, которые работают только при визуальном тестировании. Кроме того, для подобных индикаторов, требующих реагировать на каждый тик, важно использовать директиву #property tester_everytick_calculate. Мы уже говорили о ней в разделе о Тестировании индикаторов и еще раз напомним в разделе о специфических директивах для тестера.