English
preview
MQL5 Bootstrap (III): Упрощенные средства работы с новостями

MQL5 Bootstrap (III): Упрощенные средства работы с новостями

MetaTrader 5 — Тестер |
44 0
Omega J Msigwa
Omega J Msigwa

Содержание


Введение

В торговле на Форекс новости играют ключевую роль в движении рынков. Экономические события высокой важности — решения по процентным ставкам, отчеты по инфляции, данные по занятости и публикации данных по ВВП — могут вызвать резкий рост рыночной волатильности всего за несколько секунд. Некоторые трейдеры считают такой период повышенной волатильности подходящим для торговли, но многие воздерживаются от торговли или корректируют свои стратегии с учетом ожидаемого влияния предстоящих публикаций.

Хотя MetaTrader 5 предоставляет мощный API встроенного Экономического календаря, доступный на языке MQL5, обеспечить его корректную работу во всех сценариях может быть непросто.

Например, проверка наличия предстоящих новостей.

//+------------------------------------------------------------------+
//|  Checking if there are news of a specified impact on the current |
//|  symbol 15 minutes=900sec from the current time.                 |
//+------------------------------------------------------------------+ 
bool IsThereNewsForTheSymbol(ENUM_CALENDAR_EVENT_IMPORTANCE importance, uint seconds=900)
 {
   MqlCalendarValue values[]; //https://www.mql5.com/ru/docs/constants/structures/mqlcalendar#mqlcalendarvalue

   ResetLastError();
   int all_news  = CalendarValueHistory(values,TimeCurrent(), TimeCurrent()+seconds, NULL, NULL); //All news from the current time to some time in the future
   
   if (all_news <= 0) //if CalendarValue History returns a value less than zero it is an indicator that there is either an error or there are no news
     {
       if (GetLastError()>0) //we check if there was an error
        {
          printf("Failed to get the news for Symbol=%s Err=%d",Symbol(),GetLastError());
          return false;
        } 
       else //if there was no error then there are no news for this symbol available since not all symbols have news
        {
          printf("No news available for all symbols");
          return false;
        }
     }

   for (int i=0; i<all_news; i++) //we loop through all the news
     {
       MqlCalendarEvent event;
       CalendarEventById(values[i].event_id, event); //Here among all the news we select one after the other by its id https://www.mql5.com/ru/docs/calendar/calendareventbyid
             
       MqlCalendarCountry country; //The country where the currency pair originates
       CalendarCountryById(event.country_id, country); //https://www.mql5.com/ru/docs/calendar/calendarcountrybyid
       
       if (StringFind(Symbol(), country.currency)>-1) //We want to ensure that we filter news that has nothing to do with the base and the quote currency for the current symbol pair
         {
          if (event.importance==importance) //filter the news by importance
            { 
              if ((long)MathAbs(TimeCurrent()-values[i].time)<=seconds) //filter the news by time | do not trade 15 minutes before or after the news | NB: the difference in time when subtracted gives out seconds
               { 
                 Comment(StringFormat("<--------- News Alert ------------>\n\nEST %d Mins remaining\nCurrency %s Time[%s]\nname[%s]\nsector[%s]\nimportance[%s]\nactual value[%.3f]\nforecast value[%.3f]\nprevious value[%.3f]",(long)MathAbs(TimeCurrent()-values[i].time)/60,country.currency,TimeToString(values[i].time,TIME_DATE|TIME_MINUTES),event.name, EnumToString(event.sector), EnumToString(event.importance),values[i].GetActualValue(),values[i].GetForecastValue(),values[i].GetPreviousValue()));
                 return true; //There is a high impact new(s) coming shortly
               }
            }
         }
     }
   return false;
 }

Хотя это не так уж сложно, для такой простой задачи требуется немало работы. Кроме того, в Тестере стратегий этот вариант может не сработать.

Если вам понадобятся исторические новости или иная логика обнаружения, выходящая за рамки приведенной выше функции, возможно, придется заново писать один и тот же код для похожих задач. В этой статье мы рассмотрим классы и методы для простого и эффективного сбора новостей и работы с ними, что поможет вам сэкономить много ценного времени на разработку и избавит от лишних хлопот.


Как устроены новости в MQL5

Важно разобраться в трех структурах, используемых API Экономического календаря MetaTrader 5, поскольку далее мы создадим собственную структуру для новостей.

Их можно представить как три набора данных, которые вместе описывают новостное событие.

01 — MqlCalendarCountry

Эта структура содержит информацию о стране, связанной с экономическим событием. Она включает такие сведения, как название страны, валюта и символ валюты.

Например, для публикации Non-Farm Payrolls (данных по занятости вне сельского хозяйства) в США структура страны будет содержать информацию о Соединенных Штатах и их валюте (USD).

Эта структура содержит следующие поля:

  • id — уникальный идентификатор страны;
  • name — название страны, например United States или Japan;
  • code — двухбуквенный код страны, например US или JP;
  • currency — код валюты страны, например USD, EUR или JPY;
  • currency_symbol — символ валюты, например $ или ¥;
  • url_name — название страны, используемое в URL-адресе сайта mql5.com.

02 — MqlCalendarEvent

Эта структура описывает само экономическое событие. В ней содержится информация о том, что представляет собой событие, к чему оно относится и насколько оно важно. Например, это может быть публикация индекса потребительских цен (CPI), данных о валовом внутреннем продукте (GDP) или решения по процентной ставке. Параметры события включают:

  • id — уникальный идентификатор события;
  • name — название экономического события;
  • country_id — идентификатор страны, связанной с событием;
  • importance — важность события (низкая, средняя или высокая);
  • frequency — периодичность публикации данных по событию (ежемесячно, ежеквартально, ежегодно и т.д.);
  • sector — сектор экономики, к которому относится событие, например торговля, государственный сектор, потребительский сектор и т.д.;
  • time_mode — способ задания времени публикации события (точное время, весь день, без указания времени и т.д.);
  • unit — единица измерения публикуемого значения, например процент или валюта;
  • digits — количество знаков после запятой при отображении значений;
  • source_url — веб-сайт, на котором официально публикуются данные;
  • event_code — короткий код, однозначно идентифицирующий событие.

03 — MqlCalendarValue

MqlCalendarEvent описывает само событие, а MqlCalendarValue хранит результаты конкретной публикации. Например, индекс потребительских цен (CPI) в США публикуется ежемесячно. Само событие остается прежним, но каждому месяцу соответствует свой набор значений.

Эта структура содержит следующие поля:

  • time — дата и время новостной публикации;
  • period — отчетный период, к которому относятся данные;
  • actual_value — фактически опубликованное значение;
  • forecast_value — значение, которое экономисты ожидали до публикации данных;
  • prev_value — значение, указанное в предыдущей публикации;
  • revised_prev_value — скорректированное предыдущее значение, если оно было пересмотрено позднее;
  • revision — номер пересмотра этой публикации;
  • impact_type — ожидаемое влияние новости на валюту.

Кроме того, эта структура предоставляет вспомогательные функции, например HasActualValue() и GetActualValue(), для проверки наличия значения перед его считыванием.

Проще говоря

  1. MqlCalendarCountry представляет страну, к которой относятся новостное событие и его значения.
  2. MqlCalendarEvent представляет объявляемое экономическое событие.
  3. MqlCalendarValue показывает результаты конкретной публикации экономических данных.

Унифицированная структура новостей

Как мы только что увидели, эти три структуры по сути описывают одно и то же — новости. Давайте определим унифицированную структуру, которая объединит все атрибуты новости в одном месте.

//+------------------------------------------------------------------+
//|            A custom news structure.                              |
//+------------------------------------------------------------------+
struct NewsStructure
  {
   //--- MqlCalendarCountry
   ulong                               country_id;                    // country ID (ISO 3166-1)
   string                              country_name;                  // country text name (in the current terminal encoding)
   string                              country_code;                  // country code name (ISO 3166-1 alpha-2)
   string                              country_currency;              // country currency code
   string                              country_currency_symbol;       // country currency symbol
   string                              url_name;                      // country name used in the mql5.com website URL

   //--- MqlCalendarEvent

   ulong                               event_id;                    // event ID
   ENUM_CALENDAR_EVENT_TYPE            event_type;                  // event type from the ENUM_CALENDAR_EVENT_TYPE enumeration
   ENUM_CALENDAR_EVENT_SECTOR          event_sector;                // sector an event is related to
   ENUM_CALENDAR_EVENT_FREQUENCY       event_frequency;             // event frequency
   ENUM_CALENDAR_EVENT_TIMEMODE        event_time_mode;             // event time mode
   ENUM_CALENDAR_EVENT_UNIT            event_unit;                  // economic indicator value's unit of measure
   ENUM_CALENDAR_EVENT_IMPORTANCE      event_importance;            // event importance
   ENUM_CALENDAR_EVENT_MULTIPLIER      event_multiplier;            // economic indicator value multiplier
   uint                                event_digits;                // number of decimal places
   string                              event_source_url;            // URL of a source where an event is published
   string                              event_code;                  // event code
   string                              event_name;                  // event text name in the terminal language (in the current terminal encoding)

   //--- MqlCalendarValue

   ulong                               value_id;              // value ID
   datetime                            value_time;            // event date and time
   datetime                            value_period;          // event reporting period
   int                                 value_revision;        // revision of the published indicator relative to the reporting period
   long                                actual_value;          // actual value multiplied by 10^6 or LONG_MIN if the value is not set
   long                                prev_value;            // previous value multiplied by 10^6 or LONG_MIN if the value is not set
   long                                revised_prev_value;    // revised previous value multiplied by 10^6 or LONG_MIN if the value is not set
   long                                forecast_value;        // forecast value multiplied by 10^6 or LONG_MIN if the value is not set
   ENUM_CALENDAR_EVENT_IMPACT          value_impact_type;     // potential impact on the currency rate

   //--- functions checking the values

   bool              HasActualValue(void) const   { return actual_value       != LONG_MIN; }
   bool              HasPreviousValue(void) const { return prev_value         != LONG_MIN; }
   bool              HasRevisedValue(void) const  { return revised_prev_value != LONG_MIN; }
   bool              HasForecastValue(void) const { return forecast_value     != LONG_MIN; }

   //--- functions receiving the values

   double            GetActualValue(void) const
     {
      return HasActualValue() ? actual_value / 1000000.0 : EMPTY_VALUE;
     }

   double            GetPreviousValue(void) const
     {
      return HasPreviousValue() ? prev_value / 1000000.0 : EMPTY_VALUE;
     }

   double            GetRevisedValue(void) const
     {
      return HasRevisedValue() ? revised_prev_value / 1000000.0 : EMPTY_VALUE;
     }

   double            GetForecastValue(void) const
     { return        HasForecastValue() ? forecast_value / 1000000.0 : EMPTY_VALUE; }
}

Небольшие различия в именах — например, префикс event_ для атрибутов события и value_ для атрибутов значения — упрощают обращение ко всем атрибутам новости по сравнению с использованием отдельных структур.


Получение новостей из встроенного календаря

В классе CNewsBuiltinProvider функция Get() получает новости из терминала, применяя заданные фильтры (даты начала и окончания, валюта и код страны).

//+------------------------------------------------------------------+
//| Retrieves economic news from the built-in MetaTrader 5 Economic  |
//| Calendar within the specified time range.                        |
//|                                                                  |
//| Parameters:                                                      |
//|   from         - Start of the search period.                     |
//|   to           - End of the search period.                       |
//|   results[]    - Output array receiving the retrieved news.      |
//|   currency     - Currency filter (e.g. "USD"). NULL retrieves    |
//|                  news for all currencies.                        |
//|   country_code - Country filter (ISO 3166-1 alpha-2, e.g. "US"). |
//|                  NULL retrieves news for all countries.          |
//|                                                                  |
//| Returns:                                                         |
//|   Number of news records retrieved, or -1 if the request fails.  |
//+------------------------------------------------------------------+
int CNewsBuiltinProvider::Get(datetime from, datetime to, NewsStructure &results[], string currency=NULL, string country_code=NULL)
  {

   MqlCalendarValue values[];
   MqlCalendarEvent event;
   MqlCalendarCountry country;

   int total = CalendarValueHistory(values, from, to, country_code, currency);
   if(total<0)
     {
      printf("Failed to get news from %s to %s. Error = %d", TimeToString(from), TimeToString(to), GetLastError());
      return -1;
     }

   ArrayResize(results, total);

//---

   for(int i=0; i<total; i++)
     {
      MqlCalendarEvent event;
      CalendarEventById(values[i].event_id, event); //Here among all the news we select one after the other by its id https://www.mql5.com/ru/docs/calendar/calendareventbyid

      MqlCalendarCountry country; //The couhtry where the currency pair originates
      CalendarCountryById(event.country_id, country); //https://www.mql5.com/ru/docs/calendar/calendarcountrybyid
      
      //---

      results[i].CountryAssign(country);
      results[i].EventAssign(event);
      results[i].ValueAssign(values[i]);
     }

   return total;
  }

Например, получение новостей за последние 24 часа для валюты USD.

#include <Bootstrap\News\provider_builtin.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   NewsStructure news[];

   datetime now = TimeCurrent();
   datetime start = now - 24 * 60 * 60; //One day prior
   string currency = "USD";

   int t_news = CNewsBuiltinProvider::Get(start, now, news, currency);

   printf("Available news %d on %s from %s to %s", t_news, currency, TimeToString(start), TimeToString(now));
   for(int i = 0; i < t_news; i++)
     {
      NewsStructure n = news[i];
      printf("%s | %s | %s | %s | %s", TimeToString(n.value_time, TIME_DATE | TIME_SECONDS), EnumToString(n.event_importance), n.country_currency, n.country_name, n.event_name);
     }
  }

Результаты:

JP      0       10:59:53.064    News Testing (EURUSD,M15)       Available news 32 on USD from 2026.07.16 23:59 to 2026.07.17 23:59
FH      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 02:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Governor Jefferson Speech
RS      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Housing Starts
DR      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Building Permits
CM      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Housing Starts m/m
FQ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Building Permits m/m
MP      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Import Price Index m/m
PK      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Import Price Index y/y
PE      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Export Price Index m/m
II      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Export Price Index y/y
EI      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Import Price Index excl. Petroleum m/m
NP      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Industrial Production m/m
PQ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Fed Capacity Utilization Rate
NN      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Fed Manufacturing Production m/m
DO      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Industrial Production y/y
NJ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Consumer Sentiment
HK      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Consumer Expectations
KJ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Michigan Current Conditions
JQ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Inflation Expectations
CM      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan 5-Year Inflation Expectations
ED      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 20:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Baker Hughes US Oil Rig Count
IG      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 20:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Baker Hughes US Total Rig Count
CI      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Copper Non-Commercial Net Positions
GN      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Silver Non-Commercial Net Positions
QN      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Gold Non-Commercial Net Positions
FJ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Crude Oil Non-Commercial Net Positions
GG      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC S&P 500 Non-Commercial Net Positions
QR      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Aluminium Non-Commercial Net Positions
FR      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Corn Non-Commercial Net Positions
NG      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Natural Gas Non-Commercial Net Positions
JJ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Soybeans Non-Commercial Net Positions
EQ      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Wheat Non-Commercial Net Positions
JN      0       10:59:53.064    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Nasdaq 100 Non-Commercial Net Positions


Проверка наличия новостей

Во многих торговых сценариях полезна простая функция, проверяющая наличие новостей в заданном диапазоне дат.

//+------------------------------------------------------------------+
//| Checks whether one or more economic news events exist within the |
//| specified time range. Optionally filters the results by event    |
//| importance, currency, and country.                               |
//|                                                                  |
//| Parameters:                                                      |
//|   from         - Start of the search period.                     |
//|   to           - End of the search period.                       |
//|   importance   - Event importance filter. Use -1 to match any    |
//|                  importance level.                               |
//|   currency     - Currency filter (e.g. "USD"). NULL searches     |
//|                  all currencies.                                 |
//|   country_code - Country filter (ISO 3166-1 alpha-2, e.g. "US"). |
//|                  NULL searches all countries.                    |
//|                                                                  |
//| Returns:                                                         |
//|   true if at least one matching event exists; otherwise false.   |
//+------------------------------------------------------------------+
bool CNewsBuiltinProvider::Exists(datetime from, datetime to, int importance=-1, string currency=NULL, string country_code=NULL)
  {
//---

   NewsStructure candidates[];
   int total = Get(from, to, candidates, currency, country_code);

   if(total <= 0)
      return false;

   if(importance==-1)
      return true;

// Check for the requested importance
   ENUM_CALENDAR_EVENT_IMPORTANCE imp = (ENUM_CALENDAR_EVENT_IMPORTANCE)importance;

   for(int i=0; i<total; i++)
     {
      if(candidates[i].event_importance == imp)
         return true;
     }

   return false;
  }

Для поиска определенных типов новостей метод принимает параметр importance, с помощью которого можно задать важность искомых новостей.

Пример использования:

   datetime now = TimeCurrent();
   datetime start = now - 24*60*60; //One day prior
   string currency = "USD";

   bool exists = CNewsBuiltinProvider::Exists(start, now, currency, CALENDAR_IMPORTANCE_HIGH);
   printf("High impact News exists on %s: %s", currency, exists ? "true" : "false");

   exists = CNewsBuiltinProvider::Exists(start, now, currency, CALENDAR_IMPORTANCE_MODERATE);
   printf("Moderate impact News exists on %s: %s", currency, exists ? "true" : "false");

Результаты:

DN      0       12:51:46.500    News Testing (EURUSD,M15)       High impact News exists on USD: false
NO      0       12:51:46.500    News Testing (EURUSD,M15)       Moderate impact News exists on USD: true


Получение следующей новостной публикации

Иногда нужно лишь узнать о следующей запланированной новостной публикации. Функция Next() упрощает эту задачу: она получает все новостные события в интервале от указанного времени (или текущего времени по умолчанию) до конца окна поиска вперед, а затем возвращает первое событие из результатов.

Поскольку новости получаются в хронологическом порядке, первый элемент всегда соответствует ближайшей предстоящей новостной публикации.

//+------------------------------------------------------------------+
//| Returns the next economic news event occurring after the current |
//| terminal time.                                                   |
//|                                                                  |
//| Parameters:                                                      |
//|   out               - Receives the next matching news event.     |
//|   currency          - Currency filter (e.g. "USD"). NULL         |
//|                       searches all currencies.                   |
//|   from              - The initial date for news lookup.          |
//|   lookahead_seconds - Maximum number of seconds to search ahead. |
//|                       Default is 900 seconds (15 minutes).       |
//|   country_code      - Country filter (ISO 3166-1 alpha-2,        |
//|                       e.g. "US"). NULL searches all countries.   |
//|                                                                  |
//| Returns:                                                         |
//|   true if a matching news event is found; otherwise false.       |
//+------------------------------------------------------------------+
bool CNewsBuiltinProvider::Next(NewsStructure &out,
                                string currency,
                                const datetime from,
                                const uint lookahead_seconds=900,
                                string country_code=NULL)
  {
   datetime now = from;
   datetime max_time = now + lookahead_seconds;

//---

   NewsStructure candidates[];

   int total = Get(candidates, now+1, max_time, currency, country_code);
   if(total > 0)
     {
      out = candidates[0]; // ascending order — first is nearest
      return true;
     }

   return false;
  }

Параметр lookahead_seconds определяет окно поиска вперед; я рекомендую не заглядывать слишком далеко в будущее, чтобы вызов функции выполнялся быстро.


Получение предыдущей новостной публикации

Аналогичным образом функция Previous() возвращает последнее новостное событие перед указанным временем (или текущим временем по умолчанию). Функция выполняет поиск в прошлом в пределах настраиваемого окна поиска назад и возвращает последнее найденное событие.

Поскольку найденные новости отсортированы в хронологическом порядке, последний элемент в результатах соответствует последней новостной публикации перед указанным временем.

//+------------------------------------------------------------------+
//| Returns the most recent economic news event before the specified |
//| time.                                                            |
//|                                                                  |
//| Parameters:                                                      |
//|   out              - Receives the most recent matching news      |
//|                      event.                                      |
//|   currency         - Currency filter (e.g. "USD"). NULL          |
//|                      searches all currencies.                    |
//|   from             - The reference time to search backwards      |
//|                      from.                                       |
//|   lookback_seconds - Maximum number of seconds to search         |
//|                      backwards. Default is 900 seconds           |
//|                      (15 minutes).                               |
//|   country_code     - Country filter (ISO 3166-1 alpha-2,         |
//|                      e.g. "US"). NULL searches all countries.    |
//|                                                                  |
//| Returns:                                                         |
//|   true if a matching news event is found; otherwise false.       |
//+------------------------------------------------------------------+
bool CNewsBuiltinProvider::Previous(NewsStructure &out,
                                    string currency,
                                    const datetime from,
                                    const uint lookback_seconds=900,
                                    string country_code=NULL)
  {
   datetime start = from - (datetime)lookback_seconds;

   NewsStructure candidates[];

   int total = Get(candidates, start, from - 1, currency, country_code);
   if(total <= 0)
      return false;

// Events are sorted in ascending order by time.
   out = candidates[total - 1];
   return true;
  }

Параметр lookback_seconds определяет окно поиска назад; я рекомендую не заглядывать слишком далеко в прошлое, чтобы вызов функции выполнялся быстро.


Экспорт новостей в CSV-файл

Создадим класс для работы с новостями из CSV-файла, начав с функции экспорта новостей в этот формат.

//+------------------------------------------------------------------+
//| Exports an array of news records to a CSV file.                  |
//|                                                                  |
//| Parameters:                                                      |
//|   results[]     - Array of news records to export.               |
//|   filename      - Name of the destination CSV file.              |
//|   common_folder - If true, the file is created in the terminal's |
//|                  common files folder; otherwise it is created in |
//|                  the current terminal's Files directory.         |
//|                                                                  |
//| Notes:                                                           |
//|   - The first row of the CSV contains the column headers.        |
//|   - Each NewsStructure is written as a single CSV record.        |
//|   - Records with an unexpected number of fields are skipped.     |
//+------------------------------------------------------------------+
void CNewsCSVProvider::Export(NewsStructure &results[], const string filename, bool common_folder=false)
  {
   CFile f = CFileIO::open(filename, "w", CP_UTF8, common_folder);

//---

   string csv_header = results[0].HeaderLine();

   string parsed_line[];
   ParseCSVLine(csv_header, parsed_line);

   uint header_size = parsed_line.Size();

   CSVWriter csv_writer(f);
   csv_writer.writeRow(parsed_line);

//---

   for(uint i=0; i<results.Size(); i++)
     {
      string row = results[i].ToCSVLine();
      ParseCSVLine(row, parsed_line);
      
      if(header_size != parsed_line.Size())
        {
         DebugBreak();
         continue;
        }
      csv_writer.writeRow(parsed_line);
     }

   f.close();
  }

При экспорте новостей в CSV значения нужно экранировать, чтобы запятые внутри полей не интерпретировались как разделители столбцов. Эту задачу решает функция ParseCSVLine().

Пример использования:

#include <Bootstrap\News\provider_csv.mqh>
//+------------------------------------------------------------------+
input datetime START_TIME=D'01.01.2026';
input datetime END_TIME=D'01.06.2026';

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+ 
void OnStart()
  {
//---
   
   NewsStructure historical_news[];
   int t_news = CNewsBuiltinProvider::Get(START_TIME, END_TIME, historical_news);
   
   if (t_news>0)
      CNewsCSVProvider::Export(historical_news, "news.csv");
  }

Результаты:


Использование новостей из CSV-файла

Читать и разбирать CSV-файл при каждом получении новостей неэффективно. CSV-файл следует прочитать только один раз, а затем сохранить результаты в памяти для дальнейшего использования.

Все функции класса, такие как Get(), Exists(), Next() и Previous(), работают с кэшем новостей (массивом), а не перечитывают CSV-файл заново. Это значительно повышает производительность за счет устранения ненужных операций файлового ввода-вывода и обеспечивает быстрый доступ к данным новостей.

Чтобы обеспечить такое поведение, CSV-файл загружается в конструкторе класса, что гарантирует заполнение внутреннего кэша до вызова любой функции получения новостей.

//+------------------------------------------------------------------+
//| Constructs a CSV news provider by loading economic news records  |
//| from a CSV file into an in-memory cache.                         |
//|                                                                  |
//| Parameters:                                                      |
//|   csv_filename - Name of the CSV file containing the exported    |
//|                  news records.                                   |
//|   common       - If true, the CSV file is loaded from the        |
//|                  terminal's common files folder; otherwise it is |
//|                  loaded from the current terminal's Files        |
//|                  directory.                                      |
//|   delimiter    - Field separator used in the CSV file. The       |
//|                  default delimiter is a comma (",").             |
//|                                                                  |
//+------------------------------------------------------------------+
CNewsCSVProvider::CNewsCSVProvider(const string csv_filename, bool common=false, const string delimiter=","):
   m_csv_filename(csv_filename),
   m_csv_source(true)
  {
   CFile f = CFileIO::open(csv_filename, "r", CP_UTF8, common);
   CSVReader csv_reader(f, delimiter);

   NewsStructure news_st;

   int buff = 1000, read=0;
   bool header_skipped = false;

   ArrayResize(this.m_news_cache, buff);

   string line = "";
   string csv_row[];

//---

   while(csv_reader.readRow(csv_row))
     {
      if(!header_skipped)
        {
         header_skipped = true;
         continue;
        }

      //---

      if(!FromCSVLine(csv_row, news_st))
         continue;

      this.m_news_cache[read] = news_st;
      read++;

      //--- optimized array resizing

      if(read >= (int)this.m_news_cache.Size())
         ArrayResize(this.m_news_cache, this.m_news_cache.Size()+buff);
     }
   
   printf("%d news loaded from %s",read, csv_filename);
   
   ArrayResize(this.m_news_cache, read);
  }

После считывания всех строк из CSV-файла значения сохраняются в массиве m_news_cache.

В объявлении класса указано наследование от родительского класса CNewsBaseCache.

class CNewsCSVProvider: public CNewsBaseCache

Этот вспомогательный базовый класс разработан с единственной целью — для работы с новостями, хранящимися в массиве m_news_cache[].

class CNewsBaseCache
  {
protected:
   NewsStructure     m_news_cache[];

public:
                     CNewsBaseCache();
                    ~CNewsBaseCache(void);

   void              AssignCache(NewsStructure &src[]);

   int               Get(datetime from, datetime to, NewsStructure &results[], string currency=NULL, string country_code=NULL);
   bool              Exists(datetime from, datetime to, int importance=-1, string currency=NULL, string country_code=NULL);

   bool              Next(NewsStructure &out, const uint lookahead_seconds=900, string currency=NULL, string country_code=NULL);
   bool              Next(datetime from, NewsStructure &out, const uint lookahead_seconds=900, string currency=NULL, string country_code=NULL);

   bool              Previous(NewsStructure &out, const uint lookback_seconds=900, string currency=NULL, string country_code=NULL);
   bool              Previous(datetime from, NewsStructure &out, const uint lookback_seconds=900, string currency=NULL, string country_code=NULL);
  };

Этот простой класс универсален: его можно использовать даже для новостей встроенного календаря. Это готовый к подключению модуль, который принимает большой массив новостей и использует его как источник для последующих вызовов функций.

Он лежит в основе класса CNewsCSVProvider — по аналогии с тем, как мы получали новости из встроенного календаря. Ниже показано, как использовать новости из CSV-файла.

#include <Bootstrap\News\provider_csv.mqh>

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+ 
void OnStart()
  {
//---
   datetime now = TimeCurrent();
   datetime start = now - 24*60*60; //One day prior
   string currency = "USD";
   
   NewsStructure news[];
   
   CNewsCSVProvider csv_news("news.csv");
   
   int t_news = csv_news.Get(start, now, news, currency);

   printf("Available news %d on %s from %s to %s", t_news, currency, TimeToString(start), TimeToString(now));
   for(int i=0; i<t_news; i++)
     {
      NewsStructure n = news[i];
      printf("%s | %s | %s | %s | %s", TimeToString(n.value_time, TIME_DATE|TIME_SECONDS), EnumToString(n.event_importance), n.country_currency, n.country_name, n.event_name);
     }
   
   bool exists = csv_news.Exists(start, now, CALENDAR_IMPORTANCE_HIGH, currency);
   printf("High impact News exists on %s: %s", currency, exists ? "true" : "false");
   
   exists = csv_news.Exists(start, now, CALENDAR_IMPORTANCE_MODERATE, currency);
   printf("Moderate impact News exists on %s: %s", currency, exists ? "true" : "false");

//--- 
  }

Результаты:

MF      0       17:00:56.065    News Testing (EURUSD,M15)       7484 news loaded from news.csv
CM      0       17:00:56.069    News Testing (EURUSD,M15)       Available news 32 on USD from 2026.07.16 23:59 to 2026.07.17 23:59
GM      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 02:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Governor Jefferson Speech
CL      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Housing Starts
IO      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Building Permits
RN      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Housing Starts m/m
GD      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Building Permits m/m
PG      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Import Price Index m/m
MH      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Import Price Index y/y
EK      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Export Price Index m/m
DD      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Export Price Index y/y
PK      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Import Price Index excl. Petroleum m/m
OS      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Industrial Production m/m
EO      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Fed Capacity Utilization Rate
OK      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Fed Manufacturing Production m/m
IJ      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Industrial Production y/y
GE      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Consumer Sentiment
MI      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Consumer Expectations
RO      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Michigan Current Conditions
CL      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Inflation Expectations
JN      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan 5-Year Inflation Expectations
PG      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 20:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Baker Hughes US Oil Rig Count
DJ      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 20:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Baker Hughes US Total Rig Count
JN      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Copper Non-Commercial Net Positions
FS      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Silver Non-Commercial Net Positions
LK      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Gold Non-Commercial Net Positions
GG      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Crude Oil Non-Commercial Net Positions
FR      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC S&P 500 Non-Commercial Net Positions
DL      0       17:00:56.069    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Aluminium Non-Commercial Net Positions
OG      0       17:00:56.070    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Corn Non-Commercial Net Positions
OE      0       17:00:56.070    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Natural Gas Non-Commercial Net Positions
KO      0       17:00:56.070    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Soybeans Non-Commercial Net Positions
HS      0       17:00:56.070    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Wheat Non-Commercial Net Positions
KM      0       17:00:56.070    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Nasdaq 100 Non-Commercial Net Positions
JK      0       17:00:56.074    News Testing (EURUSD,M15)       High impact News exists on USD: false
QP      0       17:00:56.077    News Testing (EURUSD,M15)       Moderate impact News exists on USD: true


Тестирование программ, использующих новости, на истории в Тестере стратегий

Одно из ограничений встроенного Экономического календаря заключается в том, что его функции не работают в Тестере стратегий, то есть выдают ошибки при тестировании на истории. CalendarValueHistory(), CalendarValueLast() и другие функции API календаря используют текущие данные календаря, предоставляемые терминалом. В результате при выполнении в Тестере стратегий или в режиме оптимизации эти функции либо завершаются с ошибкой, либо не возвращают никаких данных.

Это создает сложности при разработке советников, использующих экономические новости. Хотя торговую логику можно протестировать, функциональность, использующую Экономический календарь, проверить нельзя. Это не позволяет точно оценивать стратегии, основанные на новостях, при тестировании на истории.

Распространенное решение — экспортировать необходимые данные Экономического календаря в CSV-файл во время работы программы на реальном или демо-счете. Затем при тестировании на истории советник может загружать те же данные из CSV-файла вместо обращения к встроенному календарю. Это позволяет программе одинаково работать при реальной торговле и в Тестере стратегий, используя одни и те же данные новостей.

Пример советника:

#define NEWS_CSV "news.csv"
#property tester_file NEWS_CSV
//+------------------------------------------------------------------+
#include <Bootstrap\News\provider_csv.mqh>
CNewsCSVProvider csv_news(NEWS_CSV);
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ObjectsDeleteAll(0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ObjectsDeleteAll(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
    
    NewsStructure n;
    if (csv_news.Next(n))
      {
         string info = StringFormat("%s | %s | %s | %s | %s", 
                              TimeToString(n.value_time, TIME_DATE|TIME_SECONDS), 
                              EnumToString(n.event_importance), 
                              n.country_currency, 
                              n.country_name, 
                              n.event_name);
            
         DisplayLabel("time", TimeToString(n.value_time, TIME_DATE|TIME_SECONDS), 20);
         DisplayLabel("importance", EnumToString(n.event_importance),40);
         DisplayLabel("currency", n.country_currency, 70);
         DisplayLabel("c name", n.country_name, 100);
         DisplayLabel("event name", n.event_name, 130);
      }
    else
      {
       Comment("");
      }
  }
//+------------------------------------------------------------------+
//| Displays or updates a text label on the chart.                   |
//+------------------------------------------------------------------+
void DisplayLabel(const string name,
                  const string text,
                  const int y=20,
                  const int x=10,
                  const color clr=clrOrange,
                  const int font_size=15,
                  const string font="Arial")
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);

      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, font_size);
      ObjectSetString(0, name, OBJPROP_FONT, font);
   }

   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ChartRedraw();
}

Результаты:

Одного простого вызова функции оказалось достаточно, чтобы обеспечить работу с новостями в Тестере стратегий.


Работа с новостями из базы данных

База данных обеспечивает более структурированный и масштабируемый способ хранения данных Экономического календаря. Это отличная альтернатива использованию CSV-файла в качестве основного источника данных новостей.

Вместо поиска в обычном текстовом файле новости можно получать с помощью SQL-запросов, фильтрующих записи по временному диапазону, валюте, стране или любому другому доступному полю. Это позволяет эффективно работать с многолетним архивом новостей и держать потребление памяти под контролем.

Начнем с функции экспорта новостей в базу данных:

//+------------------------------------------------------------------+
//| Exports an array of news records to an SQLite database.          |
//|                                                                  |
//| Parameters:                                                      |
//|   results[]     - Array of news records to export.               |
//|   filename      - Name of the SQLite database file. If the file  |
//|                   does not exist, it is created automatically.   |
//|   common_folder - If true, the database is created in the        |
//|                   terminal's common files folder; otherwise it   |
//|                   is created in the current terminal's Files     |
//|                   directory.                                     |
//|                                                                  |
//+------------------------------------------------------------------+
void CNewsSQLiteProvider  ::Export(NewsStructure &results[], string filename, bool common_folder=false)
  {
   int total = ArraySize(results);
   if(total == 0)
     {
      printf("Export: nothing to export, results[] is empty");
      return;
     }

   CSqlite3 db(false);
   if(!db.connect(filename, common_folder))
      return;

//--- collect distinct currencies present in results[]

   string currencies[];
   for(int i=0; i<total; i++)
     {
      string cur = results[i].country_currency;
      bool found = false;
      for(int c=0; c<ArraySize(currencies); c++)
         if(currencies[c] == cur)
           {
            found = true;
            break;
           }

      if(!found)
        {
         int n = ArraySize(currencies);
         ArrayResize(currencies, n+1);
         currencies[n] = cur;
        }
     }

//---

   if(!db.begin())
     {
      printf("Failed to begin transaction. Error = %d", GetLastError());
      db.close();
      return;
     }

   string insert_cols =
      "country_id,"
      "country_name,"
      "country_code,"
      "country_currency,"
      "country_currency_symbol,"
      "url_name,"
      "event_id,"
      "event_type,"
      "event_sector,"
      "event_frequency,"
      "event_time_mode,"
      "event_unit,"
      "event_importance,"
      "event_multiplier,"
      "event_digits,"
      "event_source_url,"
      "event_code,"
      "event_name,"
      "value_id,"
      "value_time,"
      "value_period,"
      "value_revision,"
      "actual_value,"
      "prev_value,"
      "revised_prev_value,"
      "forecast_value,"
      "value_impact_type";

   string placeholders = "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?"; // 27 placeholders

   for(int c=0; c<ArraySize(currencies); c++)
     {
      string cur   = currencies[c];
      cur = CurrenyToTableName(cur);

      //--- Builtin economic calendar

      if(!createNewsTable(cur, db))  //--- Create a table for each currency
         continue;

      //---

      string insert_sql = StringFormat("INSERT OR IGNORE INTO %s (%s) VALUES (%s)",cur, insert_cols, placeholders);

      int stmt = DatabasePrepare(db.get_handle(), insert_sql);
      if(stmt == INVALID_HANDLE)
        {
         printf("Failed to prepare insert for %s. Error = %d", cur, GetLastError());
         db.rollback();
         db.close();
         return;
        }

      for(int i=0; i<total; i++)
        {
         if(results[i].country_currency != cur)
            continue;

         if(!bindNewsRow(stmt, results[i]))
           {
            printf("Bind failed for %s row, event_id=%I64u. Error = %d",
                   cur, results[i].event_id, GetLastError());
            continue;
           }

         DatabaseRead(stmt);   // executes the bound insert
         DatabaseReset(stmt);  // clears bindings for the next row's reuse of this statement
        }

      DatabaseFinalize(stmt);
     }

   if(!db.commit())
      DebugBreak();
   db.close();

   printf("Exported %d news records across %d currency tables to %s", total, ArraySize(currencies), filename);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNewsSQLiteProvider  ::Export(datetime from, datetime to, string currency=NULL, string country_code=NULL)
  {
   NewsStructure news_found[];
   int f = CNewsBuiltinProvider::Get(news_found, from, to, currency, country_code);
//---
   if(f>0)
      this.Export(news_found, m_db_filename, m_common_folder);
  }

Поскольку данные в базе организованы в таблицы, для удобства мы храним новости по каждой валюте в отдельной таблице.

Получив массив объектов NewsStructure, функция перебирает его элементы и определяет уникальные валюты, данные по которым будут занесены в отдельные таблицы базы данных.

   string currencies[];
   for(int i=0; i<total; i++)
     {
      string cur = results[i].country_currency;
      bool found = false;
      for(int c=0; c<ArraySize(currencies); c++)
         if(currencies[c] == cur)
           {
            found = true;
            break;
           }

      if(!found)
        {
         int n = ArraySize(currencies);
         ArrayResize(currencies, n+1);
         currencies[n] = cur;
        }
     }

Пример: экспорт новостей по всем валютам в базу данных.

#include <Bootstrap\News\provider_sqlite.mqh>
CNewsSQLiteProvider sql_provider("news.sqlite");
//+------------------------------------------------------------------+
input datetime START_TIME=D'01.01.2026';
input datetime END_TIME=D'01.06.2026';
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   sql_provider.Export(START_TIME, END_TIME);
}

Результаты:

2026.07.20 14:25:44.793 News Testing (EURUSD,M15)       Exported 5751 news records across 19 currency tables to news.sqlite

news database table overview

Подобно классам-провайдерам встроенного календаря и CSV, класс-провайдер SQLite предоставляет тот же высокоуровневый интерфейс через методы Get(), Exists(), Next() и Previous(). Это означает, что источник данных можно заменить с минимальными изменениями остальной части приложения или вовсе без них. Поэтому легко переключаться между текущими данными календаря, CSV-файлами и локальной базой данных в зависимости от сценария использования.

Основной метод класса CNewsSQLiteProvider — Get(); в отличие от предыдущих реализаций, здесь он запрашивает информацию из базы данных SQLite.

//+------------------------------------------------------------------+
//| Retrieves economic news records from the SQLite database within  |
//| the specified time range.                                        |
//|                                                                  |
//| Parameters:                                                      |
//|   results[]    - Output array receiving the matching news        |
//|                  records.                                        |
//|   from         - Start of the search period.                     |
//|   to           - End of the search period.                       |
//|   currency     - Currency whose table should be queried          |
//|                  (e.g. "USD").                                   |
//|   country_code - Optional country filter (ISO 3166-1 alpha-2,    |
//|                  e.g. "US"). NULL retrieves news for all         |
//|                  countries within the selected currency.         |
//|                                                                  |
//| Returns:                                                         |
//|   The number of news records retrieved. Returns 0 if no matching |
//|   records are found or if the query cannot be executed.          |
//|                                                                  |
//| Notes:                                                           |
//|   - News records are returned in ascending order of              |
//|     publication time.                                            |
//|   - The currency is internally mapped to its corresponding       |
//|     database table name before the query is executed.            |
//|   - The output array is automatically resized to fit the         |
//|     retrieved records.                                           |
//+------------------------------------------------------------------+
int CNewsSQLiteProvider  ::Get(NewsStructure &results[],
                               datetime from,
                               datetime to,
                               string currency,
                               string country_code=NULL)
  {
   ArrayResize(results,0);

   if(m_db.get_handle()==INVALID_HANDLE)
     {
      printf("Invalid database handle. Error=%d",GetLastError());
      return 0;
     }

//---

   currency = CurrenyToTableName(currency);

//---

   string sql;
   if(country_code==NULL || country_code=="")
     {
      sql = StringFormat(
               "SELECT * FROM %s "
               "WHERE value_time>=? AND value_time<=? "
               "ORDER BY value_time ASC",
               currency);
     }
   else
     {
      sql = StringFormat(
               "SELECT * FROM %s "
               "WHERE value_time>=? AND value_time<=? "
               "AND country_code=? "
               "ORDER BY value_time ASC",
               currency);
     }

   int stmt = DatabasePrepare(m_db.get_handle(),sql);

   if(stmt==INVALID_HANDLE)
     {
      PrintFormat("Failed to prepare query. Error=%d",GetLastError());
      return 0;
     }

//---

   int p=0;
   DatabaseBind(stmt,p++,(long)from);
   DatabaseBind(stmt,p++,(long)to);

   if(country_code!=NULL && country_code!="")
      DatabaseBind(stmt,p++,country_code);

//---

   uint ARRAY_BUFF = 1000;
   ArrayResize(results, ARRAY_BUFF);

//---

   int count=0;
   while(DatabaseRead(stmt))
     {
      NewsStructure n;
      int c=0;

      long long_val;
      DatabaseColumnLong(stmt,c++,long_val);
      n.country_id = long_val;

      DatabaseColumnText(stmt,c++,n.country_name);
      DatabaseColumnText(stmt,c++,n.country_code);
      DatabaseColumnText(stmt,c++,n.country_currency);
      DatabaseColumnText(stmt,c++,n.country_currency_symbol);
      DatabaseColumnText(stmt,c++,n.url_name);

      DatabaseColumnLong(stmt,c++,long_val);
      n.event_id = long_val;

      string text;

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventTypeNames, EventTypeValues, n.event_type);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventSectorNames, EventSectorValues, n.event_sector);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventFrequencyNames, EventFrequencyValues, n.event_frequency);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventTimeModeNames, EventTimeModeValues, n.event_time_mode);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventUnitNames, EventUnitValues, n.event_unit);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventImportanceNames, EventImportanceValues, n.event_importance);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventMultiplierNames, EventMultiplierValues, n.event_multiplier);

      int int_val = 0;
      DatabaseColumnInteger(stmt,c++, int_val);
      n.event_digits = int_val;

      DatabaseColumnText(stmt,c++,n.event_source_url);
      DatabaseColumnText(stmt,c++,n.event_code);
      DatabaseColumnText(stmt,c++,n.event_name);

      DatabaseColumnLong(stmt,c++,long_val);
      n.value_id = long_val;

      DatabaseColumnLong(stmt,c++, long_val);
      n.value_time = (datetime)long_val;

      DatabaseColumnLong(stmt,c++, long_val);
      n.value_period = (datetime)long_val;

      DatabaseColumnInteger(stmt,c++,n.value_revision);

      DatabaseColumnLong(stmt,c++,n.actual_value);
      DatabaseColumnLong(stmt,c++,n.prev_value);
      DatabaseColumnLong(stmt,c++,n.revised_prev_value);
      DatabaseColumnLong(stmt,c++,n.forecast_value);

      DatabaseColumnText(stmt,c++,text);
      StringToEnum(text, EventImpactNames, EventImpactValues, n.value_impact_type);

      uint curr_size = results.Size();
      if(count>=(int)curr_size)
         ArrayResize(results, curr_size+ARRAY_BUFF);

      results[count++]=n;
     }

   ArrayResize(results, count); //Final resize
   DatabaseFinalize(stmt);
   return count;
  }

Можно попробовать запросить ту же информацию, что и в предыдущих примерах, и посмотреть, получится ли аналогичный результат:

#include <Bootstrap\News\provider_sqlite.mqh>
CNewsSQLiteProvider sql_provider("news.sqlite");

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+ 
void OnStart()
  {
//---

   start = D'17.07.2026';
   now = start+24*60*60;
   
   int t_news = sql_provider.Get(news, start, now, currency);

   printf("Available news %d on %s from %s to %s", t_news, currency, TimeToString(start), TimeToString(now));
   for(int i=0; i<t_news; i++)
     {
      NewsStructure n = news[i];
      printf("%s | %s | %s | %s | %s", TimeToString(n.value_time, TIME_DATE|TIME_SECONDS), EnumToString(n.event_importance), n.country_currency, n.country_name, n.event_name);
     }
   
   bool exists = sql_provider.Exists(start, now, currency, CALENDAR_IMPORTANCE_HIGH);
   printf("High impact News exists on %s: %s", currency, exists ? "true" : "false");
   
   exists = sql_provider.Exists(start, now, currency, CALENDAR_IMPORTANCE_MODERATE);
   printf("Moderate impact News exists on %s: %s", currency, exists ? "true" : "false");
  }

Результаты:

JD      0       14:44:08.011    News Testing (EURUSD,M15)       Available news 32 on USD from 2026.07.17 00:00 to 2026.07.18 00:00
PD      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 02:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Governor Jefferson Speech
DG      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Housing Starts
FF      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Building Permits
QQ      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Housing Starts m/m
DM      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Building Permits m/m
KL      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Import Price Index m/m
FO      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Import Price Index y/y
FR      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Export Price Index m/m
GM      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Export Price Index y/y
KD      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 15:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Import Price Index excl. Petroleum m/m
LD      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Industrial Production m/m
JD      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Fed Capacity Utilization Rate
HS      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Fed Manufacturing Production m/m
RS      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 16:15:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Fed Industrial Production y/y
HN      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Consumer Sentiment
NF      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Consumer Expectations
MF      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | Michigan Current Conditions
LE      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan Inflation Expectations
QQ      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 17:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Michigan 5-Year Inflation Expectations
KP      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 20:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Baker Hughes US Oil Rig Count
GS      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 20:00:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | Baker Hughes US Total Rig Count
EE      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Copper Non-Commercial Net Positions
IJ      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Silver Non-Commercial Net Positions
OR      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Gold Non-Commercial Net Positions
PO      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Crude Oil Non-Commercial Net Positions
MK      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC S&P 500 Non-Commercial Net Positions
CE      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Aluminium Non-Commercial Net Positions
DN      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Corn Non-Commercial Net Positions
DR      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Natural Gas Non-Commercial Net Positions
LG      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Soybeans Non-Commercial Net Positions
OD      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_LOW | USD | United States | CFTC Wheat Non-Commercial Net Positions
HR      0       14:44:08.011    News Testing (EURUSD,M15)       2026.07.17 22:30:00 | CALENDAR_IMPORTANCE_MODERATE | USD | United States | CFTC Nasdaq 100 Non-Commercial Net Positions
ML      0       14:44:08.011    News Testing (EURUSD,M15)       High impact News exists on USD: false
JI      0       14:44:08.012    News Testing (EURUSD,M15)       Moderate impact News exists on USD: true

Мы получили ту же информацию, что и в предыдущих запусках. Отлично!

Преимущество получения информации из базы данных в том, что эта реализация работает даже в Тестере стратегий без изменения кода. Операции с базой данных выполняются относительно медленно. В реальной торговле несколько миллисекунд обычно несущественны, но при моделировании даже около 100 мс на вызов могут значительно увеличить общее время тестирования.

Чтобы ускорить процесс, давайте используем CNewsBaseCache по аналогии с классом для работы с новостями из CSV-файла. На этот раз мы прочитаем все новости из базы данных и сохраним их в массиве для дальнейшего использования.

class CNewsSQLiteProvider
  {
protected:

   string            m_db_filename;
   CSqlite3          m_db;
   bool              m_common_folder;

   CNewsBaseCache    cache_layer;
   bool              m_cache_mode;

   struct CurrencyCache
     {
      string         currency;
      NewsStructure  news[];
     };

   CurrencyCache     m_currencies_cache[];

   int               SQLiteGet(NewsStructure &results[], datetime from, datetime to, string currency, string country_code=NULL);
   int               SQLiteGet(NewsStructure &results[], const string sql);
   bool              SQLiteExists(datetime from, datetime to, string currency, int importance=-1, string country_code=NULL);

   bool              SQLiteNext(NewsStructure &out, string currency, uint lookahead_seconds=900, string country_code=NULL);
   bool              SQLiteNext(NewsStructure &out, string currency, datetime from, uint lookahead_seconds=900, string country_code=NULL);

   bool              SQLitePrevious(NewsStructure &out, string currency, uint lookback_seconds=900, string country_code=NULL);
   bool              SQLitePrevious(NewsStructure &out, string currency, datetime from, uint lookback_seconds=900, string country_code=NULL);

   int               FindCache(const string currency);
   int               EnsureCurrencyLoaded(const string currency);
   
public:
                     CNewsSQLiteProvider(string db_filename, bool cache_mode, bool common=false);
                    ~CNewsSQLiteProvider(void);

   static void       Export(NewsStructure &results[], string filename, bool common_folder=false);
   void              Export(datetime from, datetime to, string currency, string country_code=NULL);

   int               Get(NewsStructure &results[], datetime from, datetime to, string currency, string country_code=NULL);
   bool              Exists(datetime from, datetime to, string currency, int importance=-1, string country_code=NULL)
     {
      if(m_cache_mode)
         return this.cache_layer.Exists(from, to, currency, importance, country_code);

      return this.SQLiteExists(from, to, currency, importance, country_code);
     }

   bool              Next(NewsStructure &out, string currency, uint lookahead_seconds=900, string country_code=NULL)
     {
      if(m_cache_mode)
         return this.cache_layer.Next(out, currency, lookahead_seconds, country_code);

      return this.SQLiteNext(out, currency, lookahead_seconds, country_code);
     }

   bool              Next(NewsStructure &out, string currency, datetime from, uint lookahead_seconds=900, string country_code=NULL)
     {
      if(m_cache_mode)
         return this.cache_layer.Next(out, currency, from, lookahead_seconds, country_code);

      return this.SQLiteNext(out, currency, from, lookahead_seconds, country_code);
     }

   bool              Previous(NewsStructure &out, string currency, uint lookback_seconds=900, string country_code=NULL)
     {
      if(m_cache_mode)
         return this.cache_layer.Next(out, currency, lookback_seconds, country_code);

      return this.SQLitePrevious(out, currency, lookback_seconds, country_code);
     }

   bool              Previous(NewsStructure &out, string currency, datetime from, uint lookback_seconds=900, string country_code=NULL)
     {
      if(m_cache_mode)
         return this.cache_layer.Next(out, currency, from, lookback_seconds, country_code);

      return this.SQLitePrevious(out, currency, from, lookback_seconds, country_code);
     }

Если в конструкторе класса пользователь присвоит параметру cache_mode значение true, метод Get() выбирает, использовать ли данные из массива или, если нужных данных еще нет в памяти, загрузить их из базы данных при первом обращении к этой валюте. Такой подход сокращает число запросов к базе данных: если заданная валюта отсутствует в памяти, мы извлекаем все строки из базы, а затем используем информацию из m_currencies_cache[].

Ниже показано, как использовать новости из базы данных для тестирования стратегий.

#define NEWS_DB "news.sqlite"
#define is_tester bool(MQLInfoInteger(MQL_TESTER))

#property tester_file NEWS_DB
//+------------------------------------------------------------------+
#include <Bootstrap\News\provider_sqlite.mqh>
CNewsSQLiteProvider sql_news(NEWS_DB, is_tester);
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ObjectsDeleteAll(0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ObjectsDeleteAll(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
    
    NewsStructure n;
    
    bool n_found = sql_news.Next(n, "USD");
    
    if (n_found)
      {
         string info = StringFormat("%s | %s | %s | %s | %s", 
                              TimeToString(n.value_time, TIME_DATE|TIME_SECONDS), 
                              EnumToString(n.event_importance), 
                              n.country_currency, 
                              n.country_name, 
                              n.event_name);
            
         DisplayLabel("time", TimeToString(n.value_time, TIME_DATE|TIME_SECONDS), 20);
         DisplayLabel("importance", EnumToString(n.event_importance),40);
         DisplayLabel("currency", n.country_currency, 70);
         DisplayLabel("c name", n.country_name, 100);
         DisplayLabel("event name", n.event_name, 130);
      }
    else
      {
       Comment("");
      }
  }


Заключение

В этой статье мы создали гибкий фреймворк для работы с экономическими новостями в MQL5. Мы прошли путь от изучения функций встроенного Экономического календаря MetaTrader 5 до создания унифицированной структуры новостей, упрощающей работу со странами, событиями и опубликованными значениями через единый тип данных.

Чтобы фреймворк подходил и для реальной торговли, и для тестирования на истории, мы реализовали несколько провайдеров новостей с одинаковым интерфейсом, получающих данные из разных источников. Встроенный провайдер обеспечивает прямой доступ к Экономическому календарю во время реальной торговли. CSV-провайдер позволяет тестировать стратегии, использующие новости, на истории с ранее экспортированными данными. SQLite-провайдер предлагает эффективное и масштабируемое решение для хранения больших архивов новостей и выполнения запросов к ним.

Поскольку классы всех провайдеров реализуют один и тот же набор методов, переключение между источниками данных требует лишь небольших изменений в остальной части приложения или вовсе не требует их. Такая абстракция сохраняет чистоту кода торговых стратегий и позволяет менять источник новостей в зависимости от среды.

Теперь на этой основе вы можете создавать советники, которые реагируют на экономические события и фильтруют сделки вблизи новостных публикаций высокой важности, а также надежно тестировать их на истории с тем же программным интерфейсом.


Если вы хотите внести свой вклад, ознакомьтесь с этим репозиторием на GitHub:  https://github.com/MegaJoctan/MQL5-Bootstrap


Таблица вложений

Имя файлаОписание и использование
Experts\Bootstrap\News Testing EA.mq5Советник для тестирования работы с новостями в Тестере стратегий.
Scripts\Bootstrap\News Testing.mq5Скрипт, который выполняет все примеры, описанные выше.
Include\Bootstrap\csv.mqhБиблиотека с функциями, отвечающими за чтение информации из CSV-файлов и запись в них.
Include\Bootstrap\fileIO.mqh Напоминает операции файлового ввода-вывода в Python. Подробнее.
Include\Bootstrap\SQLite3.mqhБиблиотека в стиле Python для работы с базами данных SQLite. Подробнее.
Include\Bootstrap\News\provide_builtin.mqhБиблиотека, содержащая статический класс для работы с новостями, предоставляемыми терминалом MetaTrader 5.
Include\Bootstrap\News\provide_csv.mqhБиблиотека, содержащая класс для работы с новостями, полученными из CSV-файла.
Include\Bootstrap\News\provide_sqlite.mqhБиблиотека, содержащая класс для работы с новостями из базы данных SQLite.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/23546

Прикрепленные файлы |
MQL5.zip (29.5 KB)
Рыночная микроструктура в MQL5 (Часть 8): Оценка силы микротренда Рыночная микроструктура в MQL5 (Часть 8): Оценка силы микротренда
В восьмой части добавляется побарная оценка силы микротренда для NQ M1. Функция GetMicroTrendStrength() формирует непрерывную композитную оценку в диапазоне [-1, +1] из согласованности EMA, нормализованного по ATR положения цены, согласованности наклонов и объема. Штраф за противоречие подавляет конфликты между согласованностью EMA и положением цены. Сессионно-адаптивные пороги масштабируются в зависимости от показателя достоверности из седьмой части, регулируя частоту сигналов в разных режимах. Результаты включают метку с семью состояниями, бинарный сигнал и проверку устойчивости, откалиброванные по 514 нью-йоркским сессиям (май 2024 г. - май 2026 г.).
За пределами GARCH (Часть VIII): Библиотека MMAR и ее использование в советнике За пределами GARCH (Часть VIII): Библиотека MMAR и ее использование в советнике
Эта статья завершает проект MMAR фасадным классом CMMAR и демонстрационным советником для MetaTrader 5. Фасад предоставляет компактный API (настройка, Fit(), Forecast()), объединяющий анализ разбиений, подгонку спектра и моделирование методом Монте-Карло. Вы узнаете, как загружать данные, подгонять модель и получать прогноз волатильности, а также использовать диагностику и обработку статусов для надежного применения модели в советниках.
Декомпозиция частичной информации: Когда два индикатора вместе информативнее, чем каждый по отдельности Декомпозиция частичной информации: Когда два индикатора вместе информативнее, чем каждый по отдельности
Мы представляем библиотеку декомпозиции частичной информации для MQL5, которая разделяет информацию двух источников о целевой переменной на четыре атома: два уникальных (по одному для каждого источника), общий и синергетический. В реализации используются квантильное разбиение на бины, предвычисленные логарифмы и подгонка по принципу максимальной энтропии (для I_ccs). Результаты сопоставляются с нулевой моделью на основе блочных перестановок, поскольку на конечных выборках оценки атомов превышают ноль. Используйте ее для отбора пар индикаторов и проверки статистической значимости, включая поправку на множественные сравнения на уровне семейства тестов.
MQL5 Bootstrap (II): Основные валидаторы для надежных торговых систем MQL5 Bootstrap (II): Основные валидаторы для надежных торговых систем
В статье создается переиспользуемый слой валидации для советников MQL5. В ней реализованы правила размера лота и его нормализации, проверки SL/TP и уровня заморозки, нормализация цен по количеству знаков, проверка достаточности маржи, фильтрация неизменных уровней при модификации, контроль лимита ордеров на счете, обнаружение нового бара, проверка доступности символа для торговли, новостные окна экономического календаря и функции определения торговых сессий. В результате код становится чище, а число ошибок терминала в реальной торговле уменьшается.