- Основные понятия календаря
- Получение списка и описаний доступных стран
- Запрос видов событий по странам и валютам
- Получение описания вида события по идентификатору
- Получение записей о событиях по странам или валютам
- Получение записей о событиях конкретного вида
- Чтение записи о событии по идентификатору
- Отслеживание изменений событий по стране или валюте
- Отслеживание изменений событий по типу
- Фильтрация событий по множеству условий
- Перенос базы календаря в тестер
- Торговля по календарю
Отслеживание изменений событий по стране или валюте
Как было сказано в разделе про основные понятия календаря, платформа регистрирует все изменения событий некими внутренними средствами. Каждое состояние характеризуется идентификатором изменений (change_id). Среди функций MQL5 есть две, которые позволяют этот идентификатор выяснить (в произвольный момент времени) и затем запрашивать записи календаря, измененные позднее. Одна из этих функций — CalendarValueLast, о которой речь пойдет в этом разделе, а про вторую — CalendarValueLastByEvent — мы расскажем в следующем разделе.
int CalendarValueLast(ulong &change_id, MqlCalendarValue &values[],
const string country = NULL, const string currency = NULL)
Функция CalendarValueLast предназначена для двух задач: получения последнего известного идентификатора изменений календаря change_id и заполнения массива values измененными записями с момента предыдущего изменения, заданного переданным идентификатором в том же change_id. Иными словами, параметр change_id работает и как входной, и как выходной. Именно поэтому он является ссылкой и требует указания переменной.
Если подать на вход функции change_id, равный 0, то функция заполнит переменную актуальным идентификатором, но не станет заполнять массив.
Дополнительно с помощью параметров country и currency можно установить фильтрацию записей по стране и валюте.
Функция возвращает количество скопированных элементов календаря. Поскольку в первом режиме работы (change_id = 0), массив не заполняется, возврат 0 не является ошибкой. Мы также можем получить 0, если с момента указанного изменения календарь более не модифицировался. Поэтому для проверки на ошибку следует анализировать _LastError.
Таким образом, обычный способ применения функции заключается в циклическом опросе календаря на предмет изменений.
ulong change = 0;
|
Это можно делать в цикле, по таймеру или по другим событиям.
Идентификаторы постоянно увеличиваются, но могут идти не по порядку, то есть перескакивать несколько значений.
Важно отметить, что каждая запись календаря доступна всегда только в одном единственном, последнем состоянии: история изменений в MQL5 не предоставляется. Как правило, это не является проблемой, поскольку жизненный цикл каждой новости стандартный: добавление в базу заранее за достаточно долгое время и дополнение актуальными данными в момент проведения мероприятия. Однако на практике могут случаться различные отклонения: редактирование прогноза, перенос времени, пересмотр факта. В какое именно время и что было изменено в записи — узнать через MQL5 API из истории календаря нельзя. Поэтому те торговые системы, которые принимают решения, исходя из сиюминутной обстановки, потребуют самостоятельного сохранения истории изменений и её интеграции в эксперт для прогона в тестере.
С помощью функции CalendarValueLast мы можем создать полезный сервис CalendarChangeSaver.mq5, который будет с заданной периодичностью проверять календарь на изменения и, при их наличии, сохранять в файл идентификаторы изменений вместе с текущим временем сервера. Это позволит в дальнейшем использовать информацию файла для более реалистичного тестирования экспертов на истории календаря. Разумеется, для этого потребуется организовать экспорт/импорт всей базы календаря, чем мы со временем займемся.
Предусмотрим входные переменные для задания имени файла и периода между опросами (в миллисекундах).
input string Filename = "calendar.chn";
|
В начале обработчика OnStart открываем двоичный файл на запись, а точнее на дозапись (если он уже существует). Формат существующего файла здесь не проверяется — добавьте защиту сами при встраивании в реальное приложение.
void OnStart()
|
Здесь следует сделать небольшое отступление.
При каждом изменении календаря в файл должна записываться как минимум пара целых 8-байтных чисел: текущее время (datetime) и идентификатор новости (ulong), однако одновременно измененных записей может быть и больше одной. Поэтому в первое число помимо даты упаковывается количество измененных записей. Для этого принимается во внимание, что даты умещаются в 0x7FFFFFFFF и, следовательно, старшие 3 байта остаются неиспользуемыми. Именно в два старших байта (по смещению влево на 48 битов) и помещается количество идентификаторов, которые сервис запишет после соответствующей временной метки. Макрос PACK_DATETIME_COUNTER создает "расширенную" дату, а два других — DATETIME и COUNTER — востребованы впоследствии при чтении архива изменений (другой программой).
#define PACK_DATETIME_COUNTER(D,C) (D | (((ulong)(C)) << 48))
|
Теперь вернемся к основному коду сервиса. В цикле, который "просыпается" каждые заданные PeriodMsc миллисекунд мы запрашиваем изменения с помощью CalendarValueLast. Если изменения есть, записываем текущее серверное время и массив полученных идентификаторов в файл.
while(!IsStopped())
|
Для удобного представления информации о каждой новости написана вспомогательная функция Description.
string Description(const MqlCalendarValue &value)
|
Таким образом, в журнале будет выведен не только идентификатор, но код страны, название и плановое время новости.
Предполагается, что сервис должен работать довольно продолжительное время, чтобы собрать сведения за период, достаточный для тестирования (дни, недели, месяцы). К сожалению, так же как и в случае со стаканами цен, платформа не предоставляет готовой истории стакана или правок календаря, так что их сбор ложится полностью на разработчика MQL-программ.
Посмотрим на сервис в действии. В следующем фрагменте журнала (на периоде 2022.06.28 15:30 — 16:00) обратите внимание, что некоторые новости относятся к отдаленному будущему (в них проставляются значения поля prev_value, которое является по совместительству полем actual_value одноименного текущего события). Однако более важно другое: реальное время выхода новостей может существенно, порой на несколько минут, отличаться от планового.
Requesting start ID...
|
Разумеется, это важно не для всех классов торговых стратегий, а только тех, что торгуют оперативно по рынку. Для них создаваемый архив хронологии правок календаря может обеспечить более точное тестирование новостных экспертов. О том, как можно "подключить" календарь к тестеру, мы разберем в дальнейшем, а пока покажем, как осуществить чтение полученного файла.
Для целей демонстрации подготовлен скрипт CalendarChangeReader.mq5. На практике приведенный исходный код должен размещаться в эксперте.
Входные переменные позволяют задать имя файл для чтения и начальную дату проверки. Если сервис продолжает работать (записывать файл), необходимо скопировать файл под другим именем или в другую папку (в примере скрипта файл переименован). Если параметр Start оставлен пустым, чтение новостных изменений начнется с начала текущего дня.
input string Filename = "calendar2.chn";
|
Для хранения информации об отдельной правке описана структура ChangeState.
struct ChangeState
|
Она используется в классе ChangeFileReader, который выполняет основную работу по чтению файла и предоставлению в вызывающий код тех изменений, которые соответствуют конкретному моменту времени.
Дескриптор файла передается как параметр в конструктор, также как и начальное время теста. Чтение файла и заполнение структуры ChangeState для одной правки календаря выполняется в методе readState.
class ChangeFileReader
|
Метод check читает файл до тех пор, пока очередная правка не окажется в будущем. При этом все предыдущие (по временным меткам) правки с момента предыдущего вызова метода помещаются в выходной массив records.
bool check(datetime now, ulong &records[], const bool fastforward = false)
|
Вот как класс используется в OnStart.
void OnStart()
|
А вот результаты работы скрипта для тех же изменений календаря, которые сохранялись сервисом в контексте предыдущего фрагмента журнала.
2022.06.28 15:31:00
|
Те же самые идентификаторы воспроизводятся в виртуальном времени с тем же запаздыванием, что и онлайн, правда, здесь видно округление до 1 минуты, которое получилось, потому что мы задали такой искусственный шаг в цикле. По идее, из соображений эффективности мы можем откладывать проверки вплоть до времени, хранящемся в структуре ChangeState current. В прилагаемом исходном коде определен метод getState для получения этого времени.