
Рецепты MQL5 – Экономический календарь
Введение
Терминал MetaTrader 5 и язык программирования MQL5 постоянно развиваются, расширяя возможности для анализа рынков, создания более сложных торговых роботов и прочее. Одним из новых инструментов терминала стал Экономический календарь, с которым можно работать как вручную, так и с помощью роботов.
Нужно сказать, что встроенный календарь достаточно гибкий. Его не только можно настраивать на вкладке терминала «Календарь», но и установить его на свой сайт, а также скачать мобильную версию. Но нас как алготрейдеров скорее интересуют программные возможности этого инструмента.
И в данной статье я постараюсь осветить их с этой стороны.
1. Экономический календарь — что есть в Документации?
Для начала бегло пройдёмся по задокументированному материалу. В целом особых сложностей в нём не вижу. Традиционно для MQL5 ресурса материал представлен системно и проиллюстрирован небольшими примерами.
1.1 Функции экономического календаря
Документация описывает 10 функций календаря:
- CalendarCountryById();
- CalendarEventById();
- CalendarValueById();
- CalendarCountries();
- CalendarEventByCountry();
- CalendarEventByCurrency();
- CalendarValueHistoryByEvent();
- CalendarValueHistory();
- CalendarValueLastByEvent();
- CalendarValueLast().
По большому счёту эти функции возвращают либо календарные свойства (страна, событие, значение), либо исторические значения событий.
1.2 Структуры экономического календаря
Разработчик предлагает использовать 3 структуры: MqlCalendarCountry, MqlCalendarEvent, MqlCalendarValue.
1.2.1 MqlCalendarCountry
Данная структура предоставляет детальную информацию о стране, события которой нас интересуют.
На текущий момент я сверил календарь на сайте с несколькими брокерами: доступна информация по 21 стране, Евросоюзу и всему миру (глобальные события).
[id] [name] [code] [currency] [currency_symbol] [url_name] [reserved] [ 0] 999 "European Union" "EU" "EUR" "€" "european-union" ... [ 1] 124 "Canada" "CA" "CAD" "$" "canada" ... [ 2] 36 "Australia" "AU" "AUD" "$" "australia" ... [ 3] 554 "New Zealand" "NZ" "NZD" "$" "new-zealand" ... [ 4] 392 "Japan" "JP" "JPY" "¥" "japan" ... [ 5] 156 "China" "CN" "CNY" "¥" "china" ... [ 6] 276 "Germany" "DE" "EUR" "€" "germany" ... [ 7] 250 "France" "FR" "EUR" "€" "france" ... [ 8] 380 "Italy" "IT" "EUR" "€" "italy" ... [ 9] 76 "Brazil" "BR" "BRL" "R$" "brazil" ... [10] 344 "Hong Kong" "HK" "HKD" "HK$" "hong-kong" ... [11] 702 "Singapore" "SG" "SGD" "R$" "singapore" ... [12] 484 "Mexico" "MX" "MXN" "Mex$" "mexico" ... [13] 710 "South Africa" "ZA" "ZAR" "R" "south-africa" ... [14] 356 "India" "IN" "INR" "₹" "india" ... [15] 578 "Norway" "NO" "NOK" "Kr" "norway" ... [16] 0 "Worldwide" "WW" "ALL" "" "worldwide" ... [17] 840 "United States" "US" "USD" "$" "united-states" ... [18] 826 "United Kingdom" "GB" "GBP" "£" "united-kingdom" ... [19] 756 "Switzerland" "CH" "CHF" "₣" "switzerland" ... [20] 410 "South Korea" "KR" "KRW" "₩" "south-korea" ... [21] 724 "Spain" "ES" "EUR" "€" "spain" ... [22] 752 "Sweden" "SE" "SEK" "Kr" "sweden" ...
Немного странно, что в этом списке нет России. Будем надеяться, что скоро появится.
1.2.2 MqlCalendarEvent
Данная структура предоставляет детальную информацию о самом событии. Нужно сказать, что у данной структуры достаточно много свойств. Потенциально неплохой инструмент для всестороннего фундаментального анализа. Позже рассмотрим, как можно фильтровать события в зависимости от того или иного критерия.
1.2.3 MqlCalendarValue
Данная структура предоставляет детальную информацию о значении события. Любопытно, что есть значения прошлого периода, текущего и прогноз.
Есть несколько нюансов при работе с данной структурой.
Значения в полях actual_value, forecast_value, prev_value и revised_prev_value хранятся увеличенными в миллион раз. Если значение поля не задано, то поле хранит значение LONG_MIN (-9223372036854775808). Но если значение в поле задано, то для получения значения необходимо разделить значение поля на 1 000 000 (миллион).
Правда, структура MqlCalendarValue имеет свои методы, использование которых облегчает работу со значениями указанных полей.
Методы можно разделить на 2 группы.
Первая группа проверяет, задано ли то или иное значение:
HasActualValue(void) — возвращает true, если актуальное значение события задано, иначе false HasForecastValue(void) — возвращает true, если прогнозное значение события задано, иначе false HasPreviousValue(void) — возвращает true, если предыдущее значение события задано, иначе false HasRevisedValue(void) — возвращает true, если пересмотренное значение события задано, в противном случае false
Вторая группа напрямую получает то или иное значение:
GetActualValue(void) — возвращает актуальное значение события (double) или nan, если оно не задано GetForecastValue(void) — возвращает прогнозное значение события (double) или nan, если оно не задано GetPreviousValue(void) — возвращает предыдущее значение события (double) или nan, если оно не задано GetRevisedValue(void) — возвращает пересмотренное значение события (double) или nan, если оно не задано
Продемонстрируем на примере, как структура MqlCalendarValue получает и проверяет значения полей. Пускай объектом нашего внимания будет последнее Решение Банка Японии по процентной ставке. С помощью скрипта Test_empty_value.mq5, в котором присутствуют 3 подхода относительно получения значения, выведем в журнал интересующую нас информацию.
//+------------------------------------------------------------------+ //| LongDouble | //+------------------------------------------------------------------+ union LongDouble { long long_value; double double_value; }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Bank of Japan (BoJ) Interest Rate Decision on 22 Sep 2021 02:47 GMT ulong event_id = 392060022; // "boj-interest-rate-decision" MqlCalendarValue values[]; datetime date_from, date_to; date_from = D'22.09.2021'; date_to = date_from + PeriodSeconds(PERIOD_D1); if(::CalendarValueHistoryByEvent(event_id, values, date_from, date_to)) { LongDouble forecast_val; //--- 1) "forecast_value" field forecast_val.long_value = values[0].forecast_value; ::PrintFormat("\"forecast_value\" field: %I64d", forecast_val.long_value); //--- 2) MqlCalendarValue::GetForecastValue() forecast_val.double_value = values[0].GetForecastValue(); ::PrintFormat("MqlCalendarValue::GetForecastValue(): %g", forecast_val.double_value); //--- 3) MqlCalendarValue::HasForecastValue() if(!values[0].HasForecastValue()) ::PrintFormat("MqlCalendarValue::HasForecastValue(): %s", (string)false); } } //+------------------------------------------------------------------+
Первый подход просто получает значение прогноза. Так как прогноза не было, то получим LONG_MIN (-9223372036854775808). Второй поход уже использует метод структуры MqlCalendarValue::GetForecastValue(). И он вернёт нам nan. И третий подход, пожалуй самый осторожный, проверит, есть ли вообще прогнозное значение.
После запуска скрипта в журнале появятся такие записи:
GR 0 21:23:36.076 Test_empty_value (USDCAD,H1) "forecast_value" field: -9223372036854775808 LH 0 21:23:36.080 Test_empty_value (USDCAD,H1) MqlCalendarValue::GetForecastValue(): nan HM 0 21:23:36.080 Test_empty_value (USDCAD,H1) MqlCalendarValue::HasForecastValue(): false
1.2.4 Структурные связи
Структуры связаны между собой следующими отношениями (Рис.1).
Рис.1. Отношения календарных структур
Структура MqlCalendarCountry связана с MqlCalendarEvent посредством идентификатора страны. Форма связи «один ко многим» (1..*) .
Структура MqlCalendarEvent связана с MqlCalendarValue посредством идентификатора события. Форма связи «один ко многим» (1..*) .
1.3 Ошибки
Разработчик выделяет группу ошибок времени выполнения при работе с экономическим календарём. К ним относятся:
Экономический календарь | ||
---|---|---|
ERR_CALENDAR_MORE_DATA | 5400 | Размер массива недостаточен для получения описаний всех значений |
ERR_CALENDAR_TIMEOUT | 5401 | Превышен лимит запроса по времени |
ERR_CALENDAR_NO_DATA | 5402 | Страна не найдена |
2. Вспомогательные структуры и класс CiCalendarInfo
Мои симпатии скорее на стороне ООП. Поэтому представлю пример класса, который обеспечивает доступ к свойствам календаря.
Тут хотел бы заметить, что календарь — это достаточно пёстрая субстанция. Я не специалист по базам данных, но насколько понимаю, по форме календарь — это реляционная база данных с несколькими таблицами.
Предлагаемая реализация класса CiCalendarInfo помимо получения свойств ещё нацелена и на создание временного ряда выбранного события.
Прежде посмотрим на вспомогательные структуры.
2.1 Структура временного ряда
Раз мы будем выуживать информацию для ВР (временного ряда), то придётся создать его программную сущность. За неё отвечает структура SiTimeSeries.
//+------------------------------------------------------------------+ //| Time series structure | //+------------------------------------------------------------------+ struct SiTimeSeries { private: bool init; // is initialized? uint size; datetime timevals[]; // time values double datavals[]; // data values string name; // ts name public: //--- constructor void SiTimeSeries(void); //--- destructor void ~SiTimeSeries(void); //--- copy consructor void SiTimeSeries(const SiTimeSeries &src_ts); //--- assignment operator void operator=(const SiTimeSeries &src_ts); //--- equality operator bool operator==(const SiTimeSeries &src_ts); //--- indexing operator SiTsObservation operator[](const uint idx) const; //--- initialization bool Init(datetime &ts_times[], const double &ts_values[], const string ts_name); //--- get series properties bool GetSeries(datetime &dst_times[], double &dst_values[], string &dst_name); bool GetSeries(SiTsObservation &dst_observations[], string &dst_name); //--- service bool IsInit(void) const { return init; }; uint Size(void) const { return size; }; void Print(const int digs = 2, const uint step = 0); }; //+------------------------------------------------------------------+
Главные элементы этой структуры — это массивы timevals[] и datavals[]. Первый включает в себя ряд времени, а второй — ряд значений.
Структура реализована так, что её элементы находятся в приватной секции. Т.е. после создания модифицировать временной ряд не получится.
Поработаем со структурой временного ряда в следующем примере. Скрипт Test_TS.mq5 получает данные по нон-фармам США с 1 января 2016 по 1 ноября 2021 и отображает их на научном графике. Сделаем так, чтобы на графике было две кривые - фактические и прогнозные значения. В качестве временной шкалы возьмём отчётный период события.
После запуска скрипта получим, во-первых, вывод значений временного ряда в журнал, а во-вторых — отрисовку диаграммы на графике (Рис.2).
Рис.2. Данные по американским нон-фармам (2016-2021)
В скрипте есть такие строки, где идёт заполнение значений временного ряда:
//--- prepare time and data values for the timeseries for(int v_idx = 0; v_idx < nfp_values_size; v_idx++) { MqlCalendarValue curr_nfp_val = nfp_values[v_idx]; datetime curr_nfp_time = curr_nfp_val.period; timevals[v_idx] = curr_nfp_time; double curr_nfp_dataval = curr_nfp_val.GetActualValue(); datavals1[v_idx] = curr_nfp_dataval; curr_nfp_dataval = curr_nfp_val.GetForecastValue(); datavals2[v_idx] = curr_nfp_dataval; }
С помощью функций MqlCalendarValue::GetActualValue() и MqlCalendarValue::GetForecastValue() сразу получим нужные нам значения.
2.2 Структура наблюдения временного ряда
Любой временной ряд состоит из наблюдений. Для наблюдения создана следующая простая структура SiTsObservation.
//+------------------------------------------------------------------+ //| Time series observation structure | //+------------------------------------------------------------------+ struct SiTsObservation { datetime time; // timestamp double val; // value //--- constructor void SiTsObservation(void): time(0), val(EMPTY_VALUE) {} }; //+------------------------------------------------------------------+
В структуре временного ряда SiTimeSeries объявлен оператор индексирования. По индексу он возвращает искомое наблюдение ряда. В примере выше, где проходила отрисовка значений по нон-фармам, ряд состоит из 70 значений. Тогда первое и последнее наблюдения можно получить таким образом:
SiTsObservation first_observation, last_observation; first_observation = nfp_ts1[0]; last_observation = nfp_ts1[nfp_values_size - 1]; string time_str = ::TimeToString(first_observation.time, TIME_DATE); string data_str = ::DoubleToString(first_observation.val, 0); ::PrintFormat("\nFirst observation: %s, %s", time_str, data_str); time_str = ::TimeToString(last_observation.time, TIME_DATE); data_str = ::DoubleToString(last_observation.val, 0); ::PrintFormat("Last observation: %s, %s", time_str, data_str);
После исполнения указанных строчек кода в журнале получим следующие записи:
KJ 0 21:27:16.386 Test_ts (USDCAD,H1) First observation: 2015.12.01, 292 HO 0 21:27:17.225 Test_ts (USDCAD,H1) Last observation: 2021.09.01, 194
2.3 Класс CiCalendarInfo
Сохраним преемственность - будем считать, что данный класс создаётся для упрощенного доступа к свойствам календаря и получения значений событий (по аналогии с торговыми классами CAccountInfo, CSymbolInfo и пр.).
Объявление класса представлено ниже.
//+------------------------------------------------------------------+ //| Class CiCalendarInfo. | //| Appointment: Class for access to calendar info. | //| Derives from class CObject. | //+------------------------------------------------------------------+ class CiCalendarInfo : public CObject { //--- === Data members === --- protected: string m_currency; ulong m_country_id; MqlCalendarCountry m_country_description; ulong m_event_id; MqlCalendarEvent m_event_description; static MqlCalendarCountry m_countries[]; bool m_is_init; //--- === Methods === --- public: //--- constructor/destructor void CiCalendarInfo(void); void ~CiCalendarInfo(void) {}; //--- initialization bool Init ( const string currency = NULL, // country currency code name const ulong country_id = WRONG_VALUE, // country ID const ulong event_id = WRONG_VALUE, // event ID const bool to_log = true // to log? ); void Deinit(void); //--- Сalendar structures descriptions bool CountryDescription(MqlCalendarCountry &country, const bool to_log = false); bool EventDescription(MqlCalendarEvent &event, const bool to_log = false); bool ValueDescription(ulong value_id, MqlCalendarValue &value, const bool to_log = false); bool EventsByCountryDescription(MqlCalendarEvent &events[], const bool to_log = false); bool EventsByCurrencyDescription(MqlCalendarEvent &events[], const bool to_log = false); bool EventsBySector(const ENUM_CALENDAR_EVENT_SECTOR event_sector, MqlCalendarEvent &events[], const bool to_log = false); //--- Сalendar enum descriptions string EventTypeDescription(const ENUM_CALENDAR_EVENT_TYPE event_type); string EventSectorDescription(const ENUM_CALENDAR_EVENT_SECTOR event_sector); string EventFrequencyDescription(const ENUM_CALENDAR_EVENT_FREQUENCY event_frequency); string EventTimeModeDescription(const ENUM_CALENDAR_EVENT_TIMEMODE event_time_mode); string EventUnitDescription(const ENUM_CALENDAR_EVENT_UNIT event_unit); string EventImportanceDescription(const ENUM_CALENDAR_EVENT_IMPORTANCE event_importance); string EventMultiplierDescription(const ENUM_CALENDAR_EVENT_MULTIPLIER event_multiplier); string ValueImpactDescription(const ENUM_CALENDAR_EVENT_IMPACT event_impact); //--- history bool ValueHistorySelectByEvent ( MqlCalendarValue &values[], // array for value descriptions datetime datetime_from, // left border of a time range datetime datetime_to = 0 // right border of a time range ) const; bool ValueHistorySelectByEvent ( SiTimeSeries &dst_ts, // timeseries for value descriptions datetime datetime_from, // left border of a time range datetime datetime_to = 0 // right border of a time range ) const; bool ValueHistorySelect ( MqlCalendarValue &values[], // array for value descriptions datetime datetime_from, // left border of a time range datetime datetime_to = 0 // right border of a time range ) const; bool ValueHistorySelect ( SiTimeSeries &dst_ts[], // array of timeseries for value descriptions datetime datetime_from, // left border of a time range datetime datetime_to = 0 // right border of a time range ); //--- the calendar database status int ValueLastSelectByEvent ( ulong& change_id, // Calendar change ID MqlCalendarValue& values[] // array for value descriptions ) const; int ValueLastSelect ( ulong& change_id, // Calendar change ID MqlCalendarValue& values[] // array for value descriptions ) const; //--- countries and continents bool GetCountries(CArrayString &countries_arr); bool GetCountries(MqlCalendarCountry &countries[]); bool GetUniqueContinents(string &continents[]); bool GetCountriesByContinent(const ENUM_CONTINENT src_continent, CArrayString &countries_arr); string GetCountryNameById(const ulong country_id); //--- events bool GetEventsByName(CArrayString &events_arr, const string name = NULL); bool GetEventsByName(MqlCalendarEvent &events[], const string name = NULL); bool FilterEvents(MqlCalendarEvent &filtered_events[], MqlCalendarEvent &src_events[], const ulong filter); //--- print void PrintCountryDescription(const MqlCalendarCountry &country); void PrintEventDescription(const MqlCalendarEvent &event); void PrintValueDescription(const MqlCalendarValue &value); //--- private: bool ValidateProperties(void); bool CountryById(const ulong country_id); bool EventId(void); }; MqlCalendarCountry CiCalendarInfo::m_countries[];
Данный класс состоит из следующих членов-данных:
- m_currency — это код валюты страны;
- m_country_id — идентификатор страны по стандарту ISO 3166-1;
- m_country_description — описание страны;
- m_event_id — идентификатор события;
- m_event_description — описание события;
- m_countries — массив описаний стран, доступных в Календаре;
- m_is_init — признак инициализации.
Члены-данные m_currency, m_country_id и m_event_id — это набор критериев для создания запросов на получение информации из календарной базы данных.
Члены-данные m_country_description и m_event_description предоставляют быстрый доступ к описаниям, если известны страна и событие.
Член-данные m_countries является статическим. Информация о странах постоянна, поэтому нет необходимости запрашивать её каждый раз при инициализации нового объекта CiCalendarInfo.
Что касается методов, то упомяну некоторые.
2.3.1 Метод инициализации
С помощью данного метода начинаем работу с объектом класса. Является обязательным для получения календарной информации. В качестве параметров метод принимает набор критериев (код валюты, идентификатор страны, идентификатор события) и один параметр разрешения вывода информации в журнал о произошедшей инициализации. Критерии позволяют сделать обращение к календарю более конкретным.
//+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ bool CiCalendarInfo::Init(const string currency = NULL, // country currency code name const ulong country_id = WRONG_VALUE, // country ID const ulong event_id = WRONG_VALUE, // event ID const bool to_log = true // to log? ) { //--- check reinitialization if(m_is_init) { ::PrintFormat(__FUNCTION__ + ": CiCalendarInfo object already initialized!"); return false; } //--- check countries int countries_cnt = ::ArraySize(m_countries); if(countries_cnt < 1) { ::ResetLastError(); countries_cnt = ::CalendarCountries(m_countries); if(countries_cnt < 1) { ::PrintFormat(__FUNCTION__ + ": CalendarCountries() returned 0! Error %d", ::GetLastError()); return false; } } for(int c_idx = 0; c_idx < countries_cnt; c_idx++) { MqlCalendarCountry curr_country = m_countries[c_idx]; //--- check currency if(!::StringCompare(curr_country.currency, currency)) { m_currency = currency; } //--- check country if(country_id != WRONG_VALUE) if(curr_country.id == country_id) { m_country_id = country_id; } } //--- check event if(event_id != WRONG_VALUE) { m_event_id = event_id; } //--- validate properties if(!this.ValidateProperties()) return false; //--- if(to_log) { ::Print("\n---== New Calendar Info object ==---"); if(m_currency != NULL) ::PrintFormat(" Currency: %s", m_currency); if(m_country_id != WRONG_VALUE) ::PrintFormat(" Country id: %I64u", m_country_id); if(m_event_id != WRONG_VALUE) ::PrintFormat(" Event id: %I64u", m_event_id); } m_is_init = true; return true; } //+------------------------------------------------------------------+
Проиллюстрируем работу метода с помощью простого скрипта Test_initialization.mq5.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- TRUE //--- 1) all currencies, all countries, all events CiCalendarInfo calendar_info1; bool is_init = calendar_info1.Init(); //--- 2) EUR, all countries, all events CiCalendarInfo calendar_info2; is_init = calendar_info2.Init("EUR"); //--- 3) EUR, Germany, all events CiCalendarInfo calendar_info3; is_init = calendar_info3.Init("EUR", 276); //--- 4) EUR, Germany, HICP m/m CiCalendarInfo calendar_info4; is_init = calendar_info4.Init("EUR", 276, 276010022); //--- FALSE //--- 5) EUR, Germany, nonfarm-payrolls CiCalendarInfo calendar_info5; is_init = calendar_info5.Init("EUR", 276, 840030016); //--- 6) EUR, US, nonfarm-payrolls CiCalendarInfo calendar_info6; is_init = calendar_info6.Init("EUR", 840, 840030016); //--- 7) EUR, all countries, nonfarm-payrolls CiCalendarInfo calendar_info7; is_init = calendar_info7.Init("EUR", WRONG_VALUE, 840030016); } //+------------------------------------------------------------------+
После запуска скрипта получим такой вывод в журнал:
DO 0 21:30:19.703 Test_initialization (USDCAD,H1) ---== New Calendar Info object ==--- GE 0 21:30:19.703 Test_initialization (USDCAD,H1) LL 0 21:30:19.703 Test_initialization (USDCAD,H1) ---== New Calendar Info object ==--- FI 0 21:30:19.703 Test_initialization (USDCAD,H1) Currency: EUR GO 0 21:30:19.703 Test_initialization (USDCAD,H1) LJ 0 21:30:19.703 Test_initialization (USDCAD,H1) ---== New Calendar Info object ==--- FS 0 21:30:19.703 Test_initialization (USDCAD,H1) Currency: EUR KO 0 21:30:19.703 Test_initialization (USDCAD,H1) Country id: 276 CH 0 21:30:19.703 Test_initialization (USDCAD,H1) PI 0 21:30:19.703 Test_initialization (USDCAD,H1) ---== New Calendar Info object ==--- JF 0 21:30:19.703 Test_initialization (USDCAD,H1) Currency: EUR OL 0 21:30:19.703 Test_initialization (USDCAD,H1) Country id: 276 HD 0 21:30:19.703 Test_initialization (USDCAD,H1) Event id: 276010022 HR 0 21:30:19.703 Test_initialization (USDCAD,H1) CiCalendarInfo::ValidateProperties: failed! Country ids must be the same! OP 0 21:30:19.703 Test_initialization (USDCAD,H1) CiCalendarInfo::ValidateProperties: failed! Currencies must be the same! GP 0 21:30:19.703 Test_initialization (USDCAD,H1) CiCalendarInfo::ValidateProperties: failed! Currencies must be the same!
Метод инициализации проверяет заданные параметры на предмет принадлежности к одной стране или валюте. Поэтому следующие комбинации возвращают «ложь»: EUR – Germany - nonfarm-payrolls, EUR – US - nonfarm-payrolls и EUR – all countries - nonfarm-payrolls.
Кроме того, нужно сказать, что в методе инициализации в самом начале стоит защита от повторной инициализации (реинициализации). Календарный объект всё же можно повторно инициализировать, но прежде нужно вызвать метод деинициализации. Например, сначала задали, что календарный объект собирает информацию по событиям с валютой Euro. А потом нужно переориентировать объект на валюту USD. В скрипте Test_reinitialization.mq5 проиллюстрировано неправильное и правильное решение этой задачи.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ERROR CiCalendarInfo calendar_info1; bool is_init = calendar_info1.Init("EUR"); is_init = calendar_info1.Init("USD"); //--- OK CiCalendarInfo calendar_info2; is_init = calendar_info2.Init("EUR"); calendar_info2.Deinit(); is_init = calendar_info2.Init("USD"); } //+------------------------------------------------------------------+
В первом неправильном случае в журнале увидим такую запись:
MP 0 21:34:19.397 Test_reinitialization (USDCAD,H1) FQ 0 21:34:19.397 Test_reinitialization (USDCAD,H1) ---== New Calendar Info object ==--- HO 0 21:34:19.397 Test_reinitialization (USDCAD,H1) Currency: EUR KI 0 21:34:19.397 Test_reinitialization (USDCAD,H1) CiCalendarInfo::Init: CiCalendarInfo object already initialized! EI 0 21:34:19.397 Test_reinitialization (USDCAD,H1) NO 0 21:34:19.397 Test_reinitialization (USDCAD,H1) ---== New Calendar Info object ==--- PF 0 21:34:19.397 Test_reinitialization (USDCAD,H1) Currency: EUR QL 0 21:34:19.397 Test_reinitialization (USDCAD,H1) RD 0 21:34:19.397 Test_reinitialization (USDCAD,H1) ---== New Calendar Info object ==--- DS 0 21:34:19.397 Test_reinitialization (USDCAD,H1) Currency: USD
2.3.2 Методы получения описаний календарных структур
Данные методы в какой-то степени являются обёртками и позволяют вызывать стандартные календарные функции. Методы CiCalendarInfo::CountryDescription() и CiCalendarInfo::EventDescription() возвращают описания страны и события, если те были подтверждены при инициализации календарного объекта.
Кроме того, методы позволяют выводить в журнал описание запрашиваемого свойства.
Проиллюстрируем работу методов, получающих описания, с помощью простого скрипта Test_structures_descriptions.mq5.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- 1) events by country CiCalendarInfo calendar_info; ulong country_id = 276; // Germany if(calendar_info.Init(NULL, country_id)) { MqlCalendarEvent events[]; if(calendar_info.EventsByCountryDescription(events)) { Print("\n---== Events selected by country ==---"); PrintFormat(" Country id: %I64u", country_id); PrintFormat(" Events number: %d", ::ArraySize(events)); } } calendar_info.Deinit(); //--- 2) events by currency string country_currency = "EUR"; if(calendar_info.Init(country_currency)) { MqlCalendarEvent events[]; if(calendar_info.EventsByCurrencyDescription(events)) { Print("\n---== Events selected by currency ==---"); PrintFormat(" Currency: %s", country_currency); PrintFormat(" Events number: %d", ::ArraySize(events)); } } } //+------------------------------------------------------------------+
В строчках журнала можно будет найти следующие:
MK 0 21:36:35.659 Test_structures_descriptions (USDCAD,H1) DM 0 21:36:35.659 Test_structures_descriptions (USDCAD,H1) ---== New Calendar Info object ==--- MP 0 21:36:35.659 Test_structures_descriptions (USDCAD,H1) Country id: 276 FH 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) ON 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) ---== Events selected by country ==--- RR 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) Country id: 276 GD 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) Events number: 61 FP 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) OG 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) ---== New Calendar Info object ==--- KI 0 21:36:35.793 Test_structures_descriptions (USDCAD,H1) Currency: EUR MN 0 21:36:35.794 Test_structures_descriptions (USDCAD,H1) QE 0 21:36:35.794 Test_structures_descriptions (USDCAD,H1) ---== Events selected by currency ==--- FO 0 21:36:35.794 Test_structures_descriptions (USDCAD,H1) Currency: EUR FJ 0 21:36:35.794 Test_structures_descriptions (USDCAD,H1) Events number: 276
Т.е. для Германии было найдено 61 событие, а для стран с валютой Euro - 276 событий.
2.3.3 Методы получения описаний календарных перечислений
В состав календарных структур входит 8 перечислений:
- ENUM_CALENDAR_EVENT_TYPE;
- ENUM_CALENDAR_EVENT_SECTOR;
- ENUM_CALENDAR_EVENT_FREQUENCY;
- ENUM_CALENDAR_EVENT_TIMEMODE;
- ENUM_CALENDAR_EVENT_UNIT;
- ENUM_CALENDAR_EVENT_IMPORTANCE;
- ENUM_CALENDAR_EVENT_MULTIPLIER;
- ENUM_CALENDAR_EVENT_IMPACT.
Первые семь относятся к структуре MqlCalendarEvent, а последнее восьмое - к структуре MqlCalendarValue.
В классе CiCalendarInfo определено соответственно 8 методов, которые описывают значение перечисления, выбранного из предлагаемого списка. Давайте протестируем методы с помощью скрипта Test_enums_descriptions.mq5. Данный скрипт будет случайным образом выбирать 10 британских событий и выводить в журнал информацию по каждому критерию.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CiCalendarInfo calendar_info; ulong country_id = 826; // UK if(calendar_info.Init(NULL, country_id)) { MqlCalendarEvent events[]; if(calendar_info.EventsByCountryDescription(events)) { ::MathSrand(77); int events_num =::ArraySize(events); int n = 10; MqlCalendarEvent events_selected[]; ::ArrayResize(events_selected, n); for(int ev_idx = 0; ev_idx < n; ev_idx++) { int rand_val =::MathRand(); int rand_idx = rand_val % events_num; events_selected[ev_idx] = events[rand_idx]; } //--- 0) name ::Print("\n---== Name ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, curr_event.name); } //--- 1) type ::Print("\n---== Type ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventTypeDescription(curr_event.type)); } //--- 2) sector ::Print("\n---== Sector ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventSectorDescription(curr_event.sector)); } //--- 3) frequency ::Print("\n---== Frequency ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventFrequencyDescription(curr_event.frequency)); } //--- 3) time mode ::Print("\n---== Time mode ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventTimeModeDescription(curr_event.time_mode)); } //--- 4) unit ::Print("\n---== Unit ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventUnitDescription(curr_event.unit)); } //--- 5) importance ::Print("\n---== Importance ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventImportanceDescription(curr_event.importance)); } //--- 6) multiplier ::Print("\n---== Multiplier ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.EventMultiplierDescription(curr_event.multiplier)); } //--- 7) impact MqlCalendarValue values_by_event[]; datetime start_dt, stop_dt; start_dt = D'01.01.2021'; stop_dt = D'01.11.2021'; ::Print("\n---== Impact ==---"); for(int ev_idx = 0; ev_idx < n; ev_idx++) { MqlCalendarEvent curr_event = events_selected[ev_idx]; CiCalendarInfo event_info; MqlCalendarValue ev_values[]; if(event_info.Init(NULL, WRONG_VALUE, curr_event.id)) if(event_info.ValueHistorySelectByEvent(ev_values, start_dt, stop_dt)) { int ev_values_size =::ArraySize(ev_values); ::PrintFormat(" [%d] - %s", ev_idx + 1, calendar_info.ValueImpactDescription(ev_values[--ev_values_size].impact_type)); } } } } } //+------------------------------------------------------------------+
Для воспроизведения полученного результата зададим начальное состояние генератора ряда псевдослучайных целых чисел некоторым числом (::MathSrand(77)). Итак, скрипт выбрал следующие события:
- BoE Housing Equity Withdrawal q/q;
- BoE Deputy Governor Markets and Banking Ramsden Speech;
- Claimant Count Change;
- Core CPI y/y;
- Average Weekly Earnings, Total Pay y/y;
- Easter Monday;
- BoE Mortgage Lending m/m;
- BoE MPC Member Vlieghe Speech;
- Core RPI y/y;
- Claimant Count Change.
После чего в журнале получим такие описания:
FP 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Type ==--- CG 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Indicator EN 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - Event EI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - Indicator LP 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Indicator OK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Indicator OD 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - Holiday EL 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - Indicator GG 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - Event ON 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Indicator CJ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - Indicator DO 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) PE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Sector ==--- JR 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Money KJ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - Money NQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - Labor market QS 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Prices HD 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Labor market JP 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - Holidays OI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - Housing EQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - Money LD 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Prices JR 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - Labor market RS 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) NF 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Frequency ==--- ML 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Quarterly QH 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - None MN 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - Monthly PI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Monthly OP 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Monthly CE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - None CR 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - Monthly CS 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - None GE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Monthly OO 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - Monthly PI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) NQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Time mode ==--- FE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Exact time MS 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - Exact time PH 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - Exact time CQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Exact time RO 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Exact time PF 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - Takes all day NR 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - Exact time MK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - Exact time DQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Exact time RM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - Exact time FK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) HP 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Unit ==--- CI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - National currency OO 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - None MG 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - People CO 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Percentage NE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Percentage OK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - None KQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - National currency CH 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - None LQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Percentage CE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - People LL 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) PD 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Importance ==--- FS 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Low PD 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - Moderate DK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - High EM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Low QJ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Moderate GQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - None PG 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - Low RO 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - Moderate LI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Low FM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - High ND 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) CM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Multiplier ==--- HE 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Billions MK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - None IM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - Thousands MI 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - None HQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - None OH 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - None DN 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - Billions IF 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - None LN 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - None OH 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - Thousands DM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) FF 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) ---== Impact ==--- OK 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [1] - Positive OR 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [2] - None EJ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [3] - Positive RQ 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [4] - Negative CG 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [5] - Negative KN 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [6] - None JF 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [7] - None EM 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [8] - None GD 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [9] - Positive QH 0 21:14:19.340 Test_enums_descriptions (USDCAD,H1) [10] - Positive
К примеру первое событие "BoE Housing Equity Withdrawal q/q" описано так:
- "Type" - Indicator;
- "Sector" - Money;
- "Frequency" - Quarterly;
- "Time mode" - Exact time;
- "Unit" - National currency;
- "Importance" - Low;
- "Multiplier" - Billions;
- "Impact" - Positive.
А последнее событие "Claimant Count Change" описано так:
- "Type" - Indicator;
- "Sector" - Labor;
- "Frequency" - Quarterly;
- "Time mode" - Exact time;
- "Unit" - National currency;
- "Importance" - Low;
- "Multiplier" - Billions;
- "Impact" - Positive.
2.3.4 Методы обращения к истории
Данные методы также используют встроенные календарные функции и получают информацию о значениях событий. К примеру к функции ::CalendarValueHistoryByEvent() обращаются два перегруженных метода CiCalendarInfo::ValueHistorySelectByEvent(). Если первый возвращает массив значений по всем событиям на заданном диапазоне времени по идентификатору события в виде структуры MqlCalendarValue, то второй - массив значений, преобразованный во временной ряд.
Посмотрим, как работает метод CiCalendarInfo::ValueHistorySelectByEvent() с помощью скрипта Test_value_history_by_event.mq5.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- NFP CiCalendarInfo nfp_info; ulong nfp_id = 840030016; if(nfp_info.Init(NULL, WRONG_VALUE, nfp_id)) { SiTimeSeries nfp_ts; if(nfp_info.ValueHistorySelectByEvent(nfp_ts, 0, ::TimeTradeServer())) nfp_ts.Print(0); } } //+------------------------------------------------------------------+
Выбираем всю историю по такому показателю, как United States Nonfarm Payrolls.
В журнале получим такие записи:
PL 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) ---== New Calendar Info object ==--- NI 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) Event id: 840030016 OM 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) HG 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) ---== Times series - Nonfarm Payrolls==--- CJ 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) [1]: time - 2007.03.09 16:30, value - 97 RE 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) [2]: time - 2007.04.06 15:30, value - 177 MS 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) [3]: time - 2007.05.04 15:30, value - 80 FL 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) [4]: time - 2007.06.01 15:30, value - 190 LH 0 21:45:03.581 Test_value_history_by_event (USDCAD,H1) [5]: time - 2007.07.06 15:30, value - 69 ... JE 0 21:45:03.583 Test_value_history_by_event (USDCAD,H1) [172]: time - 2021.06.04 15:30, value - 559 JP 0 21:45:03.583 Test_value_history_by_event (USDCAD,H1) [173]: time - 2021.07.02 15:30, value - 850 IO 0 21:45:03.583 Test_value_history_by_event (USDCAD,H1) [174]: time - 2021.08.06 15:30, value - 943 NJ 0 21:45:03.583 Test_value_history_by_event (USDCAD,H1) [175]: time - 2021.09.03 15:30, value - 235 HI 0 21:45:03.583 Test_value_history_by_event (USDCAD,H1) [176]: time - 2021.10.08 15:30, value - 194
*Здесь для компактного отображения указал первые и последние 5 значений события.
2.3.5 Методы проверки состояния базы Календаря
Данные методы тоже задействуют соответствующие встроенные календарные функции. Информируют об ошибке только тогда, когда количество полученных значений события равно нулю, а сама ошибка больше нуля.
Пример работы метода CiCalendarInfo::ValueLastSelectByEvent() будет представлен в третьем разделе "Индикатор чистого объёма спекулятивных позиций", где понадобится отлавливать появление нового значения.
2.3.6 Методы получения данных о странах и континентах
Данные методы занимаются тем, что возвращают некоторую информацию о странах. Несколько слов о каждом.
Метод CiCalendarInfo::GetCountries(CArrayString &countries_arr) возвращает список стран, полученный при инициализации, в виде динамического массива переменных типа string.
Метод CiCalendarInfo::GetCountries(MqlCalendarCountry &countries[]) возвращает список стран, полученный при инициализации, в виде массива переменных типа MqlCalendarCountry.
Метод CiCalendarInfo::GetUniqueContinents(string & continents[]) возвращает список континентов, на которых расположены страны. Последние также были получены при инициализации.
Метод CiCalendarInfo:: GetCountriesByContinent(const ENUM_CONTINENT src_continent, CArrayString &countries_arr) возвращает список стран по заданному континенту.
Метод CiCalendarInfo::GetCountryNameById(const ulong country_id) возвращает уникальное имя страны по её идентификатору.
Для работы с такой географической категорией как континент было создано перечисление ENUM_CONTINENT. Оно описывает следующие континенты:
- World;
- Asia;
- Africa;
- Europe;
- North America;
- South America;
- Australia/Oceania;
- Antarctica.
Может показаться забавным, что я включил в состав перечисления Антарктику. Но пускай она там останется, чтобы иметь исчерпывающий список континентов. Да, и отдельным континентом выступает константа "World".
Кроме того, для работы с континентами была создана структура SCountryByContinent. В методе инициализации есть константные массивы кодов стран, имён стран и соответствующих им континентов. В текущей версии задано 197 стран, включая Евросоюз и весь мир.
Создадим скрипт Test_get_countries.mq5, в котором проверим работу методов получения данных о странах и континентах.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CiCalendarInfo country_calendar_info; if(country_calendar_info.Init()) { //--- 1) get countries (CArrayString) CArrayString countries_arr; if(country_calendar_info.GetCountries(countries_arr)) { int countries_num = countries_arr.Total(); if(countries_num > 0) { ::Print("\n---== CArrayString list ==---"); ::PrintFormat(" Countries list consists of %d countries.", countries_num); ::PrintFormat(" First country: %s", countries_arr.At(0)); ::PrintFormat(" Last country: %s", countries_arr.At(countries_num - 1)); } } //--- 2) get countries (MqlCalendarCountry) MqlCalendarCountry countries[]; if(country_calendar_info.GetCountries(countries)) { int countries_num = ::ArraySize(countries); if(countries_num > 0) { ::Print("\n---== MqlCalendarCountry array ==---"); ::PrintFormat(" Countries array consists of %d countries.", countries_num); ::PrintFormat(" First country: %s", countries[0].name); ::PrintFormat(" Last country: %s", countries[countries_num - 1].name); } } //--- 3) get unique continents string continent_names[]; int continents_num = 0; if(country_calendar_info.GetUniqueContinents(continent_names)) { continents_num = ::ArraySize(continent_names); if(continents_num > 0) { ::Print("\n---== Unique continent names ==---"); for(int c_idx = 0; c_idx < continents_num; c_idx++) { string curr_continent_name = continent_names[c_idx]; ::PrintFormat(" [%d] - %s", c_idx + 1, curr_continent_name); } } } //--- 4) get countries by continent if(continents_num) { ENUM_CONTINENT continents[]; ::ArrayResize(continents, continents_num); ::Print("\n---== Countries by continent ==---"); for(int c_idx = 0; c_idx < continents_num; c_idx++) { ENUM_CONTINENT curr_continent = SCountryByContinent::ContinentByDescription(continent_names[c_idx]); if(countries_arr.Shutdown()) if(country_calendar_info.GetCountriesByContinent(curr_continent, countries_arr)) { int countries_by_continent = countries_arr.Total(); ::PrintFormat(" Continent \"%s\" includes %d country(-ies):", continent_names[c_idx], countries_by_continent); for(int c_jdx = 0; c_jdx < countries_by_continent; c_jdx++) { ::PrintFormat(" [%d] - %s", c_jdx + 1, countries_arr.At(c_jdx)); } } } } //--- 5) get country description string country_code = "RU"; SCountryByContinent country_continent_data; if(country_continent_data.Init(country_code)) { ::Print("\n---== Country ==---"); ::PrintFormat(" Name: %s", country_continent_data.Country()); ::PrintFormat(" Code: %s", country_continent_data.Code()); ENUM_CONTINENT curr_continent = country_continent_data.Continent(); ::PrintFormat(" Continent enum: %s", ::EnumToString(curr_continent)); ::PrintFormat(" Continent description: %s", country_continent_data.ContinentDescription()); } } } //+------------------------------------------------------------------+
По итогам работы скрипта в журнале появится такая информация:
EH 0 23:05:16.492 Test_get_countries (USDCAD,H1) ---== New Calendar Info object ==--- HR 0 23:05:16.492 Test_get_countries (USDCAD,H1) QH 0 23:05:16.492 Test_get_countries (USDCAD,H1) ---== CArrayString list ==--- NR 0 23:05:16.492 Test_get_countries (USDCAD,H1) Countries list consists of 23 countries. NP 0 23:05:16.492 Test_get_countries (USDCAD,H1) First country: European Union LF 0 23:05:16.492 Test_get_countries (USDCAD,H1) Last country: Norway LQ 0 23:05:16.492 Test_get_countries (USDCAD,H1) GG 0 23:05:16.492 Test_get_countries (USDCAD,H1) ---== MqlCalendarCountry array ==--- IL 0 23:05:16.492 Test_get_countries (USDCAD,H1) Countries array consists of 23 countries. JP 0 23:05:16.492 Test_get_countries (USDCAD,H1) First country: European Union HG 0 23:05:16.492 Test_get_countries (USDCAD,H1) Last country: Norway OR 0 23:05:16.493 Test_get_countries (USDCAD,H1) FJ 0 23:05:16.493 Test_get_countries (USDCAD,H1) ---== Unique continent names ==--- KS 0 23:05:16.493 Test_get_countries (USDCAD,H1) [1] - Africa NK 0 23:05:16.493 Test_get_countries (USDCAD,H1) [2] - Asia HR 0 23:05:16.493 Test_get_countries (USDCAD,H1) [3] - Australia/Oceania HM 0 23:05:16.493 Test_get_countries (USDCAD,H1) [4] - Europe RE 0 23:05:16.493 Test_get_countries (USDCAD,H1) [5] - North America CO 0 23:05:16.493 Test_get_countries (USDCAD,H1) [6] - South America GH 0 23:05:16.493 Test_get_countries (USDCAD,H1) [7] - World GP 0 23:05:18.606 Test_get_countries (USDCAD,H1) LE 0 23:05:18.606 Test_get_countries (USDCAD,H1) ---== Countries by continent ==--- HO 0 23:05:18.608 Test_get_countries (USDCAD,H1) Continent "Africa" includes 1 country(-ies): RR 0 23:05:18.608 Test_get_countries (USDCAD,H1) [1] - South Africa NH 0 23:05:18.610 Test_get_countries (USDCAD,H1) Continent "Asia" includes 6 country(-ies): CM 0 23:05:18.610 Test_get_countries (USDCAD,H1) [1] - China RK 0 23:05:18.610 Test_get_countries (USDCAD,H1) [2] - Hong Kong CL 0 23:05:18.610 Test_get_countries (USDCAD,H1) [3] - India LJ 0 23:05:18.610 Test_get_countries (USDCAD,H1) [4] - South Korea LJ 0 23:05:18.610 Test_get_countries (USDCAD,H1) [5] - Japan IR 0 23:05:18.610 Test_get_countries (USDCAD,H1) [6] - Singapore OK 0 23:05:18.614 Test_get_countries (USDCAD,H1) Continent "Australia/Oceania" includes 2 country(-ies): RM 0 23:05:18.614 Test_get_countries (USDCAD,H1) [1] - Australia NJ 0 23:05:18.614 Test_get_countries (USDCAD,H1) [2] - New Zealand MM 0 23:05:18.616 Test_get_countries (USDCAD,H1) Continent "Europe" includes 9 country(-ies): LO 0 23:05:18.616 Test_get_countries (USDCAD,H1) [1] - European Union DF 0 23:05:18.616 Test_get_countries (USDCAD,H1) [2] - Germany OQ 0 23:05:18.616 Test_get_countries (USDCAD,H1) [3] - France CE 0 23:05:18.616 Test_get_countries (USDCAD,H1) [4] - United Kingdom OM 0 23:05:18.616 Test_get_countries (USDCAD,H1) [5] - Switzerland RS 0 23:05:18.616 Test_get_countries (USDCAD,H1) [6] - Spain FE 0 23:05:18.616 Test_get_countries (USDCAD,H1) [7] - Sweden JS 0 23:05:18.616 Test_get_countries (USDCAD,H1) [8] - Italy DD 0 23:05:18.616 Test_get_countries (USDCAD,H1) [9] - Norway LR 0 23:05:18.618 Test_get_countries (USDCAD,H1) Continent "North America" includes 3 country(-ies): LK 0 23:05:18.618 Test_get_countries (USDCAD,H1) [1] - Canada HS 0 23:05:18.618 Test_get_countries (USDCAD,H1) [2] - United States CK 0 23:05:18.618 Test_get_countries (USDCAD,H1) [3] - Mexico GL 0 23:05:18.619 Test_get_countries (USDCAD,H1) Continent "South America" includes 1 country(-ies): EQ 0 23:05:18.619 Test_get_countries (USDCAD,H1) [1] - Brazil DH 0 23:05:18.622 Test_get_countries (USDCAD,H1) Continent "World" includes 1 country(-ies): JK 0 23:05:18.622 Test_get_countries (USDCAD,H1) [1] - Worldwide QM 0 23:05:18.622 Test_get_countries (USDCAD,H1) KH 0 23:05:18.622 Test_get_countries (USDCAD,H1) ---== Country ==--- PQ 0 23:05:18.622 Test_get_countries (USDCAD,H1) Name: Russian Federation KG 0 23:05:18.622 Test_get_countries (USDCAD,H1) Code: RU MR 0 23:05:18.622 Test_get_countries (USDCAD,H1) Continent enum: CONTINENT_EUROPE MI 0 23:05:18.622 Test_get_countries (USDCAD,H1) Continent description: Europe
Таким образом, в текущей версии календаря описываются события, относящиеся к экономикам 23 стран, расположенных на 7 континентах (с учётом константы "World").
2.3.7 Методы получения данных о событиях
Данные методы позволяют производить выборку событий по некоторому критерию.
Метод CiCalendarInfo::GetEventsByName(CArrayString &events_arr, const string name = NULL) формирует выборку в виде динамического массива переменных типа string. Критерием выборки является имя события.
Метод CiCalendarInfo::GetEventsByName(MqlCalendarEvent & events[], const string name = NULL) аналогичен предыдущему, разница лишь в том, что он формирует выборку в виде массива переменных типа MqlCalendarCountry.
Метод CiCalendarInfo::FilterEvents(MqlCalendarEvent &filtered_events[], MqlCalendarEvent &src_events[], const ulong filter) также формирует выборку в виде массива переменных типа MqlCalendarCountry. Но здесь уже есть множественный критерий, реализованный в наборе флагов. Всего таких критериев 49. Они охватывают все значения перечислений: ENUM_CALENDAR_EVENT_TYPE, ENUM_CALENDAR_EVENT_SECTOR, ENUM_CALENDAR_EVENT_FREQUENCY, ENUM_CALENDAR_EVENT_TIMEMODE, ENUM_CALENDAR_EVENT_UNIT, ENUM_CALENDAR_EVENT_IMPORTANCE, ENUM_CALENDAR_EVENT_MULTIPLIER.
Создать новое всеобъемлющее мегаперечисление не получилось, т.к. тип enum относится к 4-байтовому типу данных (32 бита), тогда как в этой ситуации нужно располагать 49 битами. С другой стороны, хорошо, что есть тип long, предоставляющий 64 бита.
Для решения задачи задействован такой код:
//--- defines for events filtering //--- 1) type (3) #define FILTER_BY_TYPE_EVENT 0x1 // 1 by type "event" #define FILTER_BY_TYPE_INDICATOR 0x2 // 2 by type "indicator" #define FILTER_BY_TYPE_HOLIDAY 0x4 // 3 by type "holiday" //--- 2) sector (13) #define FILTER_BY_SECTOR_NONE 0x8 // 4 by sector "none" #define FILTER_BY_SECTOR_MARKET 0x10 // 5 by sector "market" #define FILTER_BY_SECTOR_GDP 0x20 // 6 by sector "GDP" #define FILTER_BY_SECTOR_JOBS 0x40 // 7 by sector "jobs" #define FILTER_BY_SECTOR_PRICES 0x80 // 8 by sector "prices" #define FILTER_BY_SECTOR_MONEY 0x100 // 9 by sector "money" #define FILTER_BY_SECTOR_TRADE 0x200 // 10 by sector "trade" #define FILTER_BY_SECTOR_GOVERNMENT 0x400 // 11 by sector "government" #define FILTER_BY_SECTOR_BUSINESS 0x800 // 12 by sector "business" #define FILTER_BY_SECTOR_CONSUMER 0x1000 // 13 by sector "consumer" #define FILTER_BY_SECTOR_HOUSING 0x2000 // 14 by sector "housing" #define FILTER_BY_SECTOR_TAXES 0x4000 // 15 by sector "taxes" #define FILTER_BY_SECTOR_HOLIDAYS 0x8000 // 16 by sector "holidays" //--- 3) frequency (6) #define FILTER_BY_FREQUENCY_NONE 0x10000 // 17 by frequency "none" #define FILTER_BY_FREQUENCY_WEEK 0x20000 // 18 by frequency "week" #define FILTER_BY_FREQUENCY_MONTH 0x40000 // 19 by frequency "month" #define FILTER_BY_FREQUENCY_QUARTER 0x80000 // 20 by frequency "quarter" #define FILTER_BY_FREQUENCY_YEAR 0x100000 // 21 by frequency "year" #define FILTER_BY_FREQUENCY_DAY 0x200000 // 22 by frequency "day" //--- 4) importance (4) #define FILTER_BY_IMPORTANCE_NONE 0x400000 // 23 by importance "none" #define FILTER_BY_IMPORTANCE_LOW 0x800000 // 24 by importance "low" #define FILTER_BY_IMPORTANCE_MODERATE 0x1000000 // 25 by importance "medium" #define FILTER_BY_IMPORTANCE_HIGH 0x2000000 // 26 by importance "high" //--- 5) unit (14) #define FILTER_BY_UNIT_NONE 0x4000000 // 27 by unit "none" #define FILTER_BY_UNIT_PERCENT 0x8000000 // 28 by unit "percentage" #define FILTER_BY_UNIT_CURRENCY 0x10000000 // 29 by unit "currency" #define FILTER_BY_UNIT_HOUR 0x20000000 // 30 by unit "hours" #define FILTER_BY_UNIT_JOB 0x40000000 // 31 by unit "jobs" #define FILTER_BY_UNIT_RIG 0x80000000 // 32 by unit "drilling rigs" #define FILTER_BY_UNIT_USD 0x100000000 // 33 by unit "USD" #define FILTER_BY_UNIT_PEOPLE 0x200000000 // 34 by unit "people" #define FILTER_BY_UNIT_MORTGAGE 0x400000000 // 35 by unit "mortgage loans" #define FILTER_BY_UNIT_VOTE 0x800000000 // 36 by unit "votes" #define FILTER_BY_UNIT_BARREL 0x1000000000 // 37 by unit "barrels" #define FILTER_BY_UNIT_CUBICFEET 0x2000000000 // 38 by unit "cubic feet" #define FILTER_BY_UNIT_POSITION 0x4000000000 // 39 by unit "net positions" #define FILTER_BY_UNIT_BUILDING 0x8000000000 // 40 by unit "buildings" //--- 6) multiplier (5) #define FILTER_BY_MULTIPLIER_NONE 0x10000000000 // 41 by multiplier "none" #define FILTER_BY_MULTIPLIER_THOUSANDS 0x20000000000 // 42 by multiplier "thousands" #define FILTER_BY_MULTIPLIER_MILLIONS 0x40000000000 // 43 by multiplier "millions" #define FILTER_BY_MULTIPLIER_BILLIONS 0x80000000000 // 44 by multiplier "billions" #define FILTER_BY_MULTIPLIER_TRILLIONS 0x100000000000 // 45 by multiplier "trillions" //--- 7) time mode (4) #define FILTER_BY_TIMEMODE_DATETIME 0x200000000000 // 46 by time mode "na" #define FILTER_BY_TIMEMODE_DATE 0x400000000000 // 47 by time mode "positive" #define FILTER_BY_TIMEMODE_NOTIME 0x800000000000 // 48 by time mode "negative" #define FILTER_BY_TIMEMODE_TENTATIVE 0x1000000000000 // 49 by time mode "na" //--- type #define IS_TYPE_EVENT(filter) ((filter&FILTER_BY_TYPE_EVENT)!=0) #define IS_TYPE_INDICATOR(filter) ((filter&FILTER_BY_TYPE_INDICATOR)!=0) #define IS_TYPE_HOLIDAY(filter) ((filter&FILTER_BY_TYPE_HOLIDAY)!=0) //--- sector #define IS_SECTOR_NONE(filter) ((filter&FILTER_BY_SECTOR_NONE)!=0) #define IS_SECTOR_MARKET(filter) ((filter&FILTER_BY_SECTOR_MARKET)!=0) #define IS_SECTOR_GDP(filter) ((filter&FILTER_BY_SECTOR_GDP)!=0) #define IS_SECTOR_JOBS(filter) ((filter&FILTER_BY_SECTOR_JOBS)!=0) #define IS_SECTOR_PRICES(filter) ((filter&FILTER_BY_SECTOR_PRICES)!=0) #define IS_SECTOR_MONEY(filter) ((filter&FILTER_BY_SECTOR_MONEY)!=0) #define IS_SECTOR_TRADE(filter) ((filter&FILTER_BY_SECTOR_TRADE)!=0) #define IS_SECTOR_CONSUMER(filter) ((filter&FILTER_BY_SECTOR_CONSUMER)!=0) #define IS_SECTOR_HOUSING(filter) ((filter&FILTER_BY_SECTOR_HOUSING)!=0) #define IS_SECTOR_TAXES(filter) ((filter&FILTER_BY_SECTOR_TAXES)!=0) #define IS_SECTOR_HOLIDAYS(filter) ((filter&FILTER_BY_SECTOR_HOLIDAYS)!=0) //--- frequency #define IS_FREQUENCY_NONE(filter) ((filter&FILTER_BY_FREQUENCY_NONE)!=0) #define IS_FREQUENCY_WEEK(filter) ((filter&FILTER_BY_FREQUENCY_WEEK)!=0) #define IS_FREQUENCY_MONTH(filter) ((filter&FILTER_BY_FREQUENCY_MONTH)!=0) #define IS_FREQUENCY_QUARTER(filter) ((filter&FILTER_BY_FREQUENCY_QUARTER)!=0) #define IS_FREQUENCY_YEAR(filter) ((filter&FILTER_BY_FREQUENCY_YEAR)!=0) #define IS_FREQUENCY_DAY(filter) ((filter&FILTER_BY_FREQUENCY_DAY)!=0) //--- importance #define IS_IMPORTANCE_NONE(filter) ((filter&FILTER_BY_IMPORTANCE_NONE)!=0) #define IS_IMPORTANCE_LOW(filter) ((filter&FILTER_BY_IMPORTANCE_LOW)!=0) #define IS_IMPORTANCE_MODERATE(filter) ((filter&FILTER_BY_IMPORTANCE_MODERATE)!=0) #define IS_IMPORTANCE_HIGH(filter) ((filter&FILTER_BY_IMPORTANCE_HIGH)!=0) //--- unit #define IS_UNIT_NONE(filter) ((filter&FILTER_BY_UNIT_NONE)!=0) #define IS_UNIT_PERCENT(filter) ((filter&FILTER_BY_UNIT_PERCENT)!=0) #define IS_UNIT_CURRENCY(filter) ((filter&FILTER_BY_UNIT_CURRENCY)!=0) #define IS_UNIT_HOUR(filter) ((filter&FILTER_BY_UNIT_HOUR)!=0) #define IS_UNIT_JOB(filter) ((filter&FILTER_BY_UNIT_JOB)!=0) #define IS_UNIT_RIG(filter) ((filter&FILTER_BY_UNIT_RIG)!=0) #define IS_UNIT_USD(filter) ((filter&FILTER_BY_UNIT_USD)!=0) #define IS_UNIT_PEOPLE(filter) ((filter&FILTER_BY_UNIT_PEOPLE)!=0) #define IS_UNIT_MORTGAGE(filter) ((filter&FILTER_BY_UNIT_MORTGAGE)!=0) #define IS_UNIT_VOTE(filter) ((filter&FILTER_BY_UNIT_VOTE)!=0) #define IS_UNIT_BARREL(filter) ((filter&FILTER_BY_UNIT_BARREL)!=0) #define IS_UNIT_CUBICFEET(filter) ((filter&FILTER_BY_UNIT_CUBICFEET)!=0) #define IS_UNIT_POSITION(filter) ((filter&FILTER_BY_UNIT_POSITION)!=0) #define IS_UNIT_BUILDING(filter) ((filter&FILTER_BY_UNIT_BUILDING)!=0) //--- multiplier #define IS_MULTIPLIER_NONE(filter) ((filter&FILTER_BY_MULTIPLIER_NONE)!=0) #define IS_MULTIPLIER_THOUSANDS(filter) ((filter&FILTER_BY_MULTIPLIER_THOUSANDS)!=0) #define IS_MULTIPLIER_MILLIONS(filter) ((filter&FILTER_BY_MULTIPLIER_MILLIONS)!=0) #define IS_MULTIPLIER_BILLIONS(filter) ((filter&FILTER_BY_MULTIPLIER_BILLIONS)!=0) #define IS_MULTIPLIER_TRILLIONS(filter) ((filter&FILTER_BY_MULTIPLIER_TRILLIONS)!=0) //--- time mode #define IS_TIMEMODE_DATETIME(filter) ((filter&FILTER_BY_TIMEMODE_DATETIME)!=0) #define IS_TIMEMODE_DATE(filter) ((filter&FILTER_BY_TIMEMODE_DATE)!=0) #define IS_TIMEMODE_NOTIME(filter) ((filter&FILTER_BY_TIMEMODE_NOTIME)!=0) #define IS_TIMEMODE_TENTATIVE(filter) ((filter&FILTER_BY_TIMEMODE_TENTATIVE)!=0)
Обратимся к тестовому примеру - скрипту Test_filter_events.mq5. Сначала создаётся календарный объект для заданной валюты Euro.
Затем в блоке 1 выбираем все события, относящиеся к валюте Euro, где в имени есть "Unemployment". Таких событий будет всего 33. Имена событий попадут в динамический массив переменных типа string.
В блоке 2 проделываем ту же процедуру, только уже заполним массив типа MqlCalendarEvent.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CiCalendarInfo event_calendar_info; if(event_calendar_info.Init("EUR")) { //--- 1) get events by name (CArrayString) CArrayString events_arr; string ev_name = "Unemployment"; if(event_calendar_info.GetEventsByName(events_arr, ev_name)) { int events_num = events_arr.Total(); if(events_num > 0) { ::Print("\n---== CArrayString list ==---"); ::PrintFormat(" Events list consists of %d events.", events_num); ::PrintFormat(" First event: %s", events_arr.At(0)); ::PrintFormat(" Last event: %s", events_arr.At(events_num - 1)); } } //--- 2) get events by name (MqlCalendarEvent) MqlCalendarEvent events[]; if(event_calendar_info.GetEventsByName(events, ev_name)) { int events_num = ::ArraySize(events); if(events_num > 0) { ::Print("\n---== MqlCalendarEvent array ==---"); ::PrintFormat(" Events array consists of %d events.", events_num); ::PrintFormat(" First event: %s", events[0].name); ::PrintFormat(" Last event: %s", events[events_num - 1].name); } } //--- 3) filter events MqlCalendarEvent filtered_events[]; int indices[2]; indices[0] = 0; string events_str[2]; events_str[0] = "First"; events_str[1] = "Last"; ulong filter = 0; filter |= FILTER_BY_IMPORTANCE_HIGH; if(event_calendar_info.FilterEvents(filtered_events, events, filter)) { int f_events_num = ::ArraySize(filtered_events); ::Print("\n---== Filtered events array ==---"); ::Print(" Filtered by: importance high"); ::PrintFormat(" Events array consists of %d events.", ::ArraySize(filtered_events)); if(f_events_num > 0) { indices[1] = f_events_num - 1; for(int ind = 0; ind <::ArraySize(indices); ind++) { MqlCalendarEvent curr_event = filtered_events[indices[ind]]; ::PrintFormat(" \n%s event:", events_str[ind]); event_calendar_info.PrintEventDescription(curr_event); } } ::ArrayFree(filtered_events); filter ^= FILTER_BY_IMPORTANCE_HIGH; } filter |= FILTER_BY_IMPORTANCE_MODERATE; if(event_calendar_info.FilterEvents(filtered_events, events, filter)) { int f_events_num = ::ArraySize(filtered_events); ::Print("\n---== Filtered events array ==---"); ::Print(" Filtered by: importance medium"); ::PrintFormat(" Events array consists of %d events.", ::ArraySize(filtered_events)); if(f_events_num > 0) { indices[1] = f_events_num - 1; for(int ind = 0; ind <::ArraySize(indices); ind++) { MqlCalendarEvent curr_event = filtered_events[indices[ind]]; ::PrintFormat(" \n%s event:", events_str[ind]); event_calendar_info.PrintEventDescription(curr_event); } } ::ArrayFree(filtered_events); filter ^= FILTER_BY_IMPORTANCE_MODERATE; } filter |= FILTER_BY_IMPORTANCE_LOW; if(event_calendar_info.FilterEvents(filtered_events, events, filter)) { int f_events_num = ::ArraySize(filtered_events); ::Print("\n---== Filtered events array ==---"); ::Print(" Filtered by: importance low"); ::PrintFormat(" Events array consists of %d events.", ::ArraySize(filtered_events)); if(f_events_num > 0) { indices[1] = f_events_num - 1; for(int ind = 0; ind <::ArraySize(indices); ind++) { MqlCalendarEvent curr_event = filtered_events[indices[ind]]; ::PrintFormat(" \n%s event:", events_str[ind]); event_calendar_info.PrintEventDescription(curr_event); } } ::ArrayFree(filtered_events); filter ^= FILTER_BY_IMPORTANCE_LOW; } filter |= FILTER_BY_IMPORTANCE_NONE; if(event_calendar_info.FilterEvents(filtered_events, events, filter)) { int f_events_num = ::ArraySize(filtered_events); ::Print("\n---== Filtered events array ==---"); ::Print(" Filtered by: importance none"); ::PrintFormat(" Events array consists of %d events.", ::ArraySize(filtered_events)); if(f_events_num > 0) { indices[1] = f_events_num - 1; for(int ind = 0; ind <::ArraySize(indices); ind++) { MqlCalendarEvent curr_event = filtered_events[indices[ind]]; ::PrintFormat(" \n%s event:", events_str[ind]); event_calendar_info.PrintEventDescription(curr_event); } } } } } //+------------------------------------------------------------------+
В блоке 3 отфильтруем события по критерию важности. Сначала посмотрим, сколько событий из тридцати трёх событий, выбранных ранее по имени, относятся к важным. Их не будет совсем. К средней степени важности относятся 27 событий, к низкой относятся 6 событий, а к категории, где степень важности не задана, - 0.
В журнале увидим такие записи:
JL 0 13:18:48.419 Test_filter_events (USDCAD,H1) FM 0 13:18:48.421 Test_filter_events (USDCAD,H1) ---== New Calendar Info object ==--- JP 0 13:18:48.421 Test_filter_events (USDCAD,H1) Currency: EUR CE 0 13:18:48.630 Test_filter_events (USDCAD,H1) EL 0 13:18:48.631 Test_filter_events (USDCAD,H1) ---== CArrayString list ==--- IF 0 13:18:48.631 Test_filter_events (USDCAD,H1) Events list consists of 33 events. MQ 0 13:18:48.631 Test_filter_events (USDCAD,H1) First event: Unemployment Rate RK 0 13:18:48.631 Test_filter_events (USDCAD,H1) Last event: NAV Unemployment Change HF 0 13:18:48.635 Test_filter_events (USDCAD,H1) OR 0 13:18:48.635 Test_filter_events (USDCAD,H1) ---== MqlCalendarEvent array ==--- JH 0 13:18:48.635 Test_filter_events (USDCAD,H1) Events array consists of 33 events. ER 0 13:18:48.635 Test_filter_events (USDCAD,H1) First event: Unemployment Rate JM 0 13:18:48.635 Test_filter_events (USDCAD,H1) Last event: NAV Unemployment Change DH 0 13:18:48.635 Test_filter_events (USDCAD,H1) CR 0 13:18:48.635 Test_filter_events (USDCAD,H1) ---== Filtered events array ==--- HH 0 13:18:48.635 Test_filter_events (USDCAD,H1) Filtered by: importance high DO 0 13:18:48.635 Test_filter_events (USDCAD,H1) Events array consists of 0 events. CN 0 13:18:48.636 Test_filter_events (USDCAD,H1) PI 0 13:18:48.636 Test_filter_events (USDCAD,H1) ---== Filtered events array ==--- NO 0 13:18:48.636 Test_filter_events (USDCAD,H1) Filtered by: importance medium PE 0 13:18:48.636 Test_filter_events (USDCAD,H1) Events array consists of 27 events. KG 0 13:18:48.636 Test_filter_events (USDCAD,H1) KI 0 13:18:48.636 Test_filter_events (USDCAD,H1) First event: IS 0 13:18:48.636 Test_filter_events (USDCAD,H1) EJ 0 13:18:48.636 Test_filter_events (USDCAD,H1) ---== Event description ==--- JF 0 13:18:48.636 Test_filter_events (USDCAD,H1) Id: 999030020 DP 0 13:18:48.636 Test_filter_events (USDCAD,H1) Type: Indicator KJ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Sector: Labor market JM 0 13:18:48.636 Test_filter_events (USDCAD,H1) Frequency: Monthly QJ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Time mode: Exact time CN 0 13:18:48.636 Test_filter_events (USDCAD,H1) Country id: 999 KK 0 13:18:48.636 Test_filter_events (USDCAD,H1) Unit: Percentage JP 0 13:18:48.636 Test_filter_events (USDCAD,H1) Importance: Moderate JH 0 13:18:48.636 Test_filter_events (USDCAD,H1) Multiplier: None JF 0 13:18:48.636 Test_filter_events (USDCAD,H1) Digits: 1 PL 0 13:18:48.636 Test_filter_events (USDCAD,H1) Source URL: https://ec.europa.eu/eurostat NH 0 13:18:48.636 Test_filter_events (USDCAD,H1) Event code: unemployment-rate MQ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Name: Unemployment Rate GI 0 13:18:48.636 Test_filter_events (USDCAD,H1) OO 0 13:18:48.636 Test_filter_events (USDCAD,H1) Last event: OJ 0 13:18:48.636 Test_filter_events (USDCAD,H1) OP 0 13:18:48.636 Test_filter_events (USDCAD,H1) ---== Event description ==--- QH 0 13:18:48.636 Test_filter_events (USDCAD,H1) Id: 578040001 NO 0 13:18:48.636 Test_filter_events (USDCAD,H1) Type: Indicator ID 0 13:18:48.636 Test_filter_events (USDCAD,H1) Sector: Labor market DF 0 13:18:48.636 Test_filter_events (USDCAD,H1) Frequency: Monthly KS 0 13:18:48.636 Test_filter_events (USDCAD,H1) Time mode: Exact time LI 0 13:18:48.636 Test_filter_events (USDCAD,H1) Country id: 578 QR 0 13:18:48.636 Test_filter_events (USDCAD,H1) Unit: Percentage LJ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Importance: Moderate DQ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Multiplier: None LH 0 13:18:48.636 Test_filter_events (USDCAD,H1) Digits: 1 IS 0 13:18:48.636 Test_filter_events (USDCAD,H1) Source URL: https://www.nav.no/en/Home EQ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Event code: nav-unemployment-rate-nsa PJ 0 13:18:48.636 Test_filter_events (USDCAD,H1) Name: NAV Unemployment Rate n.s.a. ED 0 13:18:48.636 Test_filter_events (USDCAD,H1) FF 0 13:18:48.636 Test_filter_events (USDCAD,H1) ---== Filtered events array ==--- PK 0 13:18:48.637 Test_filter_events (USDCAD,H1) Filtered by: importance low JS 0 13:18:48.637 Test_filter_events (USDCAD,H1) Events array consists of 6 events. FH 0 13:18:48.637 Test_filter_events (USDCAD,H1) FS 0 13:18:48.637 Test_filter_events (USDCAD,H1) First event: LI 0 13:18:48.637 Test_filter_events (USDCAD,H1) LO 0 13:18:48.637 Test_filter_events (USDCAD,H1) ---== Event description ==--- EK 0 13:18:48.637 Test_filter_events (USDCAD,H1) Id: 276060003 IM 0 13:18:48.637 Test_filter_events (USDCAD,H1) Type: Indicator FE 0 13:18:48.637 Test_filter_events (USDCAD,H1) Sector: Labor market OP 0 13:18:48.637 Test_filter_events (USDCAD,H1) Frequency: Monthly HQ 0 13:18:48.637 Test_filter_events (USDCAD,H1) Time mode: Exact time HH 0 13:18:48.637 Test_filter_events (USDCAD,H1) Country id: 276 KM 0 13:18:48.637 Test_filter_events (USDCAD,H1) Unit: People DJ 0 13:18:48.637 Test_filter_events (USDCAD,H1) Importance: Low RM 0 13:18:48.637 Test_filter_events (USDCAD,H1) Multiplier: Millions KJ 0 13:18:48.637 Test_filter_events (USDCAD,H1) Digits: 3 LS 0 13:18:48.637 Test_filter_events (USDCAD,H1) Source URL: https://www.arbeitsagentur.de/en/welcome MN 0 13:18:48.637 Test_filter_events (USDCAD,H1) Event code: unemployment-nsa ND 0 13:18:48.637 Test_filter_events (USDCAD,H1) Name: Unemployment n.s.a. LP 0 13:18:48.637 Test_filter_events (USDCAD,H1) LE 0 13:18:48.637 Test_filter_events (USDCAD,H1) Last event: DP 0 13:18:48.637 Test_filter_events (USDCAD,H1) DG 0 13:18:48.637 Test_filter_events (USDCAD,H1) ---== Event description ==--- CS 0 13:18:48.637 Test_filter_events (USDCAD,H1) Id: 578040002 QE 0 13:18:48.637 Test_filter_events (USDCAD,H1) Type: Indicator NM 0 13:18:48.637 Test_filter_events (USDCAD,H1) Sector: Labor market GH 0 13:18:48.637 Test_filter_events (USDCAD,H1) Frequency: Monthly PI 0 13:18:48.637 Test_filter_events (USDCAD,H1) Time mode: Exact time GS 0 13:18:48.637 Test_filter_events (USDCAD,H1) Country id: 578 CE 0 13:18:48.637 Test_filter_events (USDCAD,H1) Unit: People LS 0 13:18:48.637 Test_filter_events (USDCAD,H1) Importance: Low HJ 0 13:18:48.637 Test_filter_events (USDCAD,H1) Multiplier: Thousands QR 0 13:18:48.637 Test_filter_events (USDCAD,H1) Digits: 3 NI 0 13:18:48.637 Test_filter_events (USDCAD,H1) Source URL: https://www.nav.no/en/Home MQ 0 13:18:48.637 Test_filter_events (USDCAD,H1) Event code: nav-unemployment-change ES 0 13:18:48.637 Test_filter_events (USDCAD,H1) Name: NAV Unemployment Change PI 0 13:18:48.637 Test_filter_events (USDCAD,H1) CS 0 13:18:48.637 Test_filter_events (USDCAD,H1) ---== Filtered events array ==--- DK 0 13:18:48.637 Test_filter_events (USDCAD,H1) Filtered by: importance none DH 0 13:18:48.637 Test_filter_events (USDCAD,H1) Events array consists of 0 events.
Повторюсь, что есть 49 критериев для отбора событий. Их можно использовать отдельно, а можно и совмещать.
3. Индикатор чистого объёма спекулятивных позиций
В экономическом календаре есть масса различных событий. В качестве примера я выбрал одно из самых интересных - еженедельный отчёт Комиссии по торговле товарными фьючерсами, который отражает разницу между общим объемом длинных и коротких позиций.
Давайте создадим индикатор, который будет отображать на графике в отдельном окне данные выбранного товарного актива.
Таких активов 11. Создадим следующее перечисление:
//+------------------------------------------------------------------+ //| CFTC Non-Commercial Net Positions | //+------------------------------------------------------------------+ enum ENUM_NON_COM_NET_POSITIONS { NON_COM_NET_POSITIONS_COPPER = 0, // Copper NON_COM_NET_POSITIONS_SILVER = 1, // Silver NON_COM_NET_POSITIONS_GOLD = 2, // Gold NON_COM_NET_POSITIONS_CRUDE_OIL = 3, // Crude oil NON_COM_NET_POSITIONS_SP_500 = 4, // S&P 500 NON_COM_NET_POSITIONS_AlUMINIUM = 5, // Aluminium NON_COM_NET_POSITIONS_CORN = 6, // Corn NON_COM_NET_POSITIONS_NGAS = 7, // Natural gas NON_COM_NET_POSITIONS_SOYBEANS = 8, // Soybeans NON_COM_NET_POSITIONS_WHEAT = 9, // Wheat NON_COM_NET_POSITIONS_NASDAQ_100 = 10, // Nasdaq 100 };
Индикатор будет отображать данные прошлых значений и следить за появлением новых. Для первой задачи задействован следующий блок кода в обработчике OnCalculate():
//--- first call if(prev_calculated == 0) { //--- initialize buffer ::ArrayInitialize(gBuffer, EMPTY_VALUE); //--- 1) collect all events by country ulong country_id = 840; // US if(gPtrEventsInfo.Init(NULL, country_id)) { MqlCalendarEvent events[]; if(gPtrEventsInfo.EventsByCountryDescription(events, false)) { string event_code_substr = GetEventCodeSubstring(); if(event_code_substr != NULL) for(int ev_idx = 0; ev_idx <::ArraySize(events); ev_idx++) { MqlCalendarEvent curr_event = events[ev_idx]; if(::StringFind(curr_event.event_code, event_code_substr) > -1) { //--- 2) collect all values by event id if(gPtrValuesInfo.Init(NULL, WRONG_VALUE, curr_event.id)) { SiTimeSeries net_positions_ts; if(gPtrValuesInfo.ValueHistorySelectByEvent(net_positions_ts, 0)) { string net_positions_name; SiTsObservation ts_observations[]; if(net_positions_ts.GetSeries(ts_observations, net_positions_name)) { //--- consider only past observations int new_size = 0; for(int obs_idx =::ArraySize(ts_observations) - 1; obs_idx >= 0; obs_idx--) { if(ts_observations[obs_idx].val != EMPTY_VALUE) break; new_size = obs_idx; } if(new_size > 0) ::ArrayResize(ts_observations, new_size); //--- find the starting date datetime start_dtime, ts_start_dtime; start_dtime = time[0]; ts_start_dtime = ts_observations[0].time; if(ts_start_dtime > start_dtime) start_dtime = ts_start_dtime; ::IndicatorSetString(INDICATOR_SHORTNAME, net_positions_name); ::IndicatorSetInteger(INDICATOR_DIGITS, 1); //--- int start_bar_idx =::iBarShift(_Symbol, _Period, ts_start_dtime); if(start_bar_idx > -1) { start_bar_idx = rates_total - start_bar_idx; uint observations_cnt = 0; SiTsObservation curr_observation = ts_observations[observations_cnt]; uint ts_size = ::ArraySize(ts_observations); for(int bar = start_bar_idx; bar < rates_total; bar++) { if((observations_cnt + 1) < ts_size) { SiTsObservation next_observation = ts_observations[observations_cnt + 1]; if(time[bar] >= next_observation.time) { curr_observation = next_observation; gLastValueDate = curr_observation.time; gLastValue = curr_observation.val; observations_cnt++; } } gBuffer[bar] = curr_observation.val; } //--- just to get a change id MqlCalendarValue values[]; gPtrValuesInfo.ValueLastSelectByEvent(gChangeId, values); } } } } break; } } } } }
В нём инициализируем первый календарный объект. Причём укажем только идентификатор страны — США. Затем выберем все события по стране и найдём по коду события свой актив. Он задаётся в input-переменной. После этого инициализируем второй календарный объект и запросим историю. После чего заполним буфер индикатора.
Второй блок будет отлавливать в обработчике OnCalculate() появление нового значения:
MqlCalendarValue values[]; if(gPtrValuesInfo.ValueLastSelectByEvent(gChangeId, values) > 0) if(values[0].time > gLastValueDate) { gLastValueDate = values[0].time; gLastValue = values[0].GetActualValue(); //--- to log if(InpTpLog) { ::Print("\n---== New event value ==---"); ::PrintFormat(" Time: %s", ::TimeToString(gLastValueDate)); datetime server_time =::TimeTradeServer(); ::PrintFormat(" Release time: %s", ::TimeToString(server_time)); ::PrintFormat(" Actual value: %0.1f", gLastValue); } } //--- if a new bar if(rates_total > prev_calculated) for(int bar = prev_calculated; bar < rates_total; bar++) gBuffer[bar] = gLastValue;
В итоге должна быть примерно такая картинка (Рис.3).
Рис.3. Чистый объем спекулятивных позиций по S&P 500 от CFTC
В коде индикатора можно заметить, что календарные объекты создаются динамически. Это связано с переинициализацией глобальных переменных в индикаторах.
Заключение
В рамках данной статьи был создан класс календарного объекта — класс для упрощенного доступа к свойствам календаря и получения значений событий. База календаря достаточно обширна и позволяет анализировать важные экономические события, не обращаясь к сторонним ресурсам.
В архиве находятся исходники, которые были использованы в статье. У меня все файлы и папки расположены в папке %MQL5\Shared Projects\Testing\Calendar. При ином расположении исходников обратите внимание на правильное включение заголовочного файла CalendarInfo.mqh с помощью директивы #include.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Делаю так. Но там куда больше мороки с пониманием, являлся ли показатель известным на определенную дату времени по факту, а не в теории.
Я также замечал несколько раз расхождения MQL5 календаря с investing.com. Как правило, расхождение было в том, что важная новость на investing.com была средней важности в MQL5 календаре.
Делаю так. Но там куда больше мороки с пониманием, являлся ли показатель известным на определенную дату времени по факту, а не в теории.
а еще не очень понятно, как с DST переходами всё это дело согласуется. Время прошлых событий, которое мы видим сейчас, это реальное время этих событий или если был перевод времени, надо еще корректировать это время?
а еще не очень понятно, как с DST переходами всё это дело согласуется. Время прошлых событий, которое мы видим сейчас, это реальное время этих событий или если был перевод времени, надо еще корректировать это время?
Каждый раз забываю ответ на этот вопрос. Нужно смотреть.
Я также замечал несколько раз расхождения MQL5 календаря с investing.com. Как правило, расхождение было в том, что важная новость на investing.com была средней важности в MQL5 календаре.
Лучше не полагаться на субъективность. Важность событий нужно определить самостоятельно и уже после этого смотреть, можно ли там получить профит.
Лучше не полагаться на субъективность. Важность событий нужно определить самостоятельно и уже после этого смотреть, можно ли там получить профит.
ого, а как это "самостоятельно" сделать в автоматическом режиме?
сверять бы с investing. Потому что, как мне кажется, косяки скорее в MQL5 календаре. Что за эталон брать?