English 中文 Español Deutsch 日本語 Português
preview
Упрощаем торговлю на новостях (Часть 3): Совершаем сделки

Упрощаем торговлю на новостях (Часть 3): Совершаем сделки

MetaTrader 5Трейдинг | 14 февраля 2025, 18:24
960 3
Kabelo Frans Mampa
Kabelo Frans Mampa

Введение

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



Чего можно ожидать?

Улучшенная графика — лаконичная, современная и адаптируемая к текущему графику. На изображении ниже представлен такой график в светлом (дневном) режиме.

    AUDUSD в светлом режиме

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

    Разделы десятый (11-15 сгруппированы) и шестнадцатый являются необязательными и будут обновляться каждую новую 1-минутную свечу (это необходимо для повышения производительности при тестировании на истории).

    Раздел

    • 10 - дата и время терминала. Время отображается красным цветом при новостном событии, когда график находится в светлом режиме.
    • 11 - дата и время текущей/следующей новости. Текст будет отображаться красным цветом, когда дата и время будут соответствовать времени терминала.
    • 12 - название новостного события. Цвет текста будет меняться в зависимости от важности события, например, высокая важность отображается красным цветом.
    • 13 - страна, к которой относится новость. Цвет текста будет меняться в зависимости от важности события и цветового режима графика (светлый/темный).
    • 14 - валюта, к которой относится новость. Цвет текста варьируется.
    • 15 - важность новости. Цвет текста варьируется.
    • 16 - спред текущего символа и ранг, который рассчитывается на основе данных спреда за 2 недели по 1-минутным свечам и классифицируется по группам excellent (отлично), good (хорошо), normal (нормально), bad (плохо) и terrible (ужасно) с разными цветами для каждой категории с вариациями для темного и светлого режимов.

    На изображении ниже показан темный (ночной) режим.

    Раздел

    • 17 - время всех событий, которые произойдут или произошли в текущий день. 

    AUDUSD в темном режиме



    Входные параметры DISPLAY

    • CHART COLOUR MODE - переключение между темным и светлым режимом.
    • DISPLAY NEWS INFO - показать/скрыть разделы 11-15 на графике.
    • DISPLAY EVENT OBJ - показать/скрыть раздел 17 на графике.
    • DISPLAY SPREAD RATING - показать/скрыть раздел 16 на графике.
    • DISPLAY DATE - показать/скрыть раздел 10 на графике.

    Входные параметры Display


    Входные параметры DST SCHEDULE

    • SELECT DST OPTION - позволить пользователю выбрать собственный график перехода на летнее время или разрешить советнику автоматически выбирать рекомендуемый график перехода на летнее время для правильной настройки времени событий при тестировании на истории в тестере стратегий.
    • SELECT CUSTOM DST - позволить пользователю вручную настроить летнее время.

    Входные параметры DST SCHEDULE


    Входные параметры RISK MANAGEMENT

    • SELECT RISK OPTION - выбрать профиль управления рисками: MINIMUM LOTSIZE (минимальный размер лота), MAXIMUM LOTSIZE (максимальный размер лота) и так далее.
    • RISK FLOOR - установить минимальный риск для всех профилей риска. Например, если средств недостаточно для размера позиции в 1 лот, но достаточно для минимального размера в 0,01 лота, для открытия сделки будет использовано 0,01 лота вместо того, чтобы не открывать ни одной сделки из-за недостатка средств. Это мера безопасности на случай, если профили риска не были настроены должным образом.
    • MAX-RISK - открыть сделку с процентом свободной маржи на счете, если на счете недостаточно средств для открытия обычной сделки. Эта опция работает только в том случае, если для параметра RISK FLOOR установлено значение MAX-RISK.
    • RISK CEILING - ограничить размер лота, когда средств на счете достаточно, чтобы открыть максимальный лот для определенного символа. Ограничение варьируется от MAX LOTSIZE, что означает, что максимально возможный размер лота — это тот, который установлен конкретным символом, тогда как MAX LOTSIZE(x2) откроет две сделки с максимальным размером лота, если это позволяет ограничение по объему.
    • PERCENTAGE OF [BALANCE | FREE-MARGIN] - рисковать определенным процентом от доступного баланса или свободной маржи.
    • AMOUNT PER [BALANCE | FREE-MARGIN] - рисковать определенной суммой баланса или свободной маржи, например, если [BALANCE | FREE-MARGIN] установлен на 1000 и EACH AMOUNT равно 10, это означает, что на каждые 1000 в валюте баланса или свободной маржи риск составляет 10 валютных единиц для каждой сделки. Таким образом, если ваш баланс/свободная маржа составляет USD 1000, то вы рискуете USD 10 в каждой сделке.
    • LOTSIZE PER [BALANCE | FREE-MARGIN] - рисковать определенным размером лота для значения баланса или свободной маржи, например, если [BALANCE | FREE-MARGIN] установлен на 1000 и EACH LOTS(VOLUME) равно 0.1, это означает, что на каждые 1000 единиц валюты баланса или свободной маржи риск составляет 0,1 лота для каждой сделки. Таким образом, если ваш баланс/свободная маржа составляет USD 1000, то риск составляет 0,1 для каждой сделки.
    • CUSTOM LOTSIZE - рисковать заранее определенным размером лота для каждой открытой сделки.
    • PERCENTAGE OF MAX-RISK - рисковать процентом от максимального объема риска для символа при доступной свободной марже на счете, например, если максимальный объем риска для AUDUSD при свободной марже на счете в USD 10 000 составляет 100 лотов, то если мы установим PERCENTAGE OF MAX-RISK на 25%, то используемый размер лота составит 25% от 100 лотов, что составляет 25 лотов.

    Входные параметры RISK MANAGEMENT


    Входные параметры NEWS SETTINGS


    Раздел содержит следующие настройки:

    1. CALENDAR IMPORTANCE
    2. EVENT FREQUENCY
    3. EVENT SECTOR
    4. EVENT TYPE
    5. EVENT CURRENCY
    Все они показаны на скриншоте ниже.

    Входные параметры NEWS SETTINGS

    • CALENDAR IMPORTANCE - сортировать новостные по указанной важности.

    Входной параметр CALENDAR IMPORTANCE

    • EVENT FREQUENCY - сортировать новости на основе частоты их появления.

    Входной параметр EVENT FREQUENCY

    • EVENT SECTOR - сортировать новости по сектору.

    Входной параметр EVENT SECTOR

    • EVENT TYPE - сортировать новости по типу: EVENT - это, как правило, высказывания и заседания, INDICATOR - это обычно процентные ставки, данные по занятости и так далее и HOLIDAY - разные праздники.

    Входной параметр EVENT TYPE

    • EVENT CURRENCY - сортировать новости по валюте. SYMBOL CURRENCIES - учитывать все валюты из SYMBOL MARGIN, SYMBOL BASE и SYMBOL PROFIT.

    Входной параметр EVENT CURRENCY


    Входные параметры TRADE SETTINGS

    • STOPLOSS[0=NONE] - установить фиксированный стоп-лосс для всех сделок. 0 - не используется.
    • TAKEPROFIT[0=NONE] - установить фиксированный тейк-профит для всех сделок. 0 - не используется.
    • PRE-ENTRY SEC - позволить пользователю настроить количество секунд для открытия сделки перед событием. Таким образом, если PRE-ENTRY SEC установлен на 5, это означает, что 5 секунд до наступления события — это тот промежуток времени, в течение которого будут открыты сделки. Например, если время наступления события - 15:00, то сделки будут разрешены за 5 секунд до начала события с 14:59:45 по 14:59:59.
    • TRADING DAY OF WEEK - сортировать по рабочим дням. 

    Входные параметры TRADE SETTINGS

    Давайте перейдем к коду.


    Класс свойств символа

    Добавлены изменения из Части 2:

    • Объявление перечисления для оценки спреда
    //Enumeration for Spread rating
    enum SpreadRating
      {
       SpreadRating_Terrible,//Terrible
       SpreadRating_Bad,//Bad
       SpreadRating_Normal,//Normal
       SpreadRating_Good,//Good
       SpreadRating_Excellent//Excellent
      };
    • Объявление булевой переменной для настройки цветового режима графика
    bool isLightMode;//Variable to configure Chart color mode
    • Декларация булевой функции для извлечения плавающего спреда
    bool              SpreadFloat(string SYMBOL=NULL);//Retrieve Spread Float
    //+------------------------------------------------------------------+
    //|Retrieve Spread Float                                             |
    //+------------------------------------------------------------------+
    bool CSymbolProperties::SpreadFloat(string SYMBOL=NULL)
      {
       if(SetSymbolName(SYMBOL))//Set Symbol
         {
          return CSymbol.SpreadFloat();
         }
       Print("Unable to retrieve Symbol's Spread Float");
       return false;//Retrieve false when failed.
      }
    • Объявление функции оценки спреда
    SpreadRating      SpreadValue(string SYMBOL=NULL);//Retrieve Spread Rating

    Функция должна возвращать значение перечисления из SpreadRating.

    //+------------------------------------------------------------------+
    //|Retrieve Spread Rating                                            |
    //+------------------------------------------------------------------+
    SpreadRating CSymbolProperties::SpreadValue(string SYMBOL=NULL)
      {
       if(SetSymbolName(SYMBOL))//Set Symbol
         {
          if(SpreadFloat(SYMBOL))//Check if Symbol has a floating Spread
            {
    
             //Declarations
             vector Spreads;
             int SpreadArray[],SpreadAvg=0,SpreadMax=0,SpreadMin=0,
                               SpreadUpper=0,SpreadLower=0,SpreadAvgUpper=0,
                               SpreadAvgLower=0,SpreadMidUpper=0,SpreadMidLower=0;
    
             //Get Spread data from CopySpread built-in function for 2 weeks using M1 timeframe.
             if(CopySpread(GetSymbolName(),PERIOD_M1,iTime(GetSymbolName(),PERIOD_W1,2),
                           iTime(GetSymbolName(),PERIOD_M1,0),SpreadArray)==-1)
               {
                Print("Error trying to retrieve spread values");
                return SpreadRating_Normal;//Retrieve default value when failed.
               }
             else
               {
                Spreads.Assign(SpreadArray);//Assign spread array into Spreads vector variable
    
                SpreadMax = int(Spreads.Max());//Assign max spread
                SpreadMin = int(Spreads.Min());//Assign min spread
                SpreadAvg = int(Spreads.Median());//Assign average spread
    
                //Divide Spread into sectors based of different averages.
                SpreadMidUpper = int((SpreadAvg+SpreadMax)/2);
                SpreadMidLower = int((SpreadAvg+SpreadMin)/2);
                SpreadAvgUpper = int((SpreadAvg+SpreadMidUpper)/2);
                SpreadAvgLower = int((SpreadAvg+SpreadMidLower)/2);
                SpreadUpper = int((SpreadMidUpper+SpreadMax)/2);
                SpreadLower = int((SpreadMidLower+SpreadMin)/2);
    
                int Spread = Spread(SYMBOL);//Assign Symbol's Spread
    
                if(Spread<SpreadLower||Spread==SpreadMin)//Excellent
                  {
                   return SpreadRating_Excellent;
                  }
                else
                   if(Spread>=SpreadLower&&Spread<SpreadAvgLower)//Good
                     {
                      return SpreadRating_Good;
                     }
                   else
                      if(Spread>=SpreadAvgLower&&Spread<=SpreadAvgUpper)//Normal
                        {
                         return SpreadRating_Normal;
                        }
                      else
                         if(Spread>SpreadAvgUpper&&Spread<=SpreadUpper)//Bad
                           {
                            return SpreadRating_Bad;
                           }
                         else//Terrible
                           {
                            return SpreadRating_Terrible;
                           }
               }
            }
          else
            {
             return SpreadRating_Normal;//Retrieve default value when spread is fixed.
            }
         }
       Print("Unable to retrieve Symbol's Spread Rating");
       return SpreadRating_Normal;//Retrieve default value when failed.
      }

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

       if(SetSymbolName(SYMBOL))//Set Symbol
         {
          if(SpreadFloat(SYMBOL))//Check if Symbol has a floating Spread
            {

    Если мы успешно задали символ и у символа плавающий спред, объявляем векторную переменную Spreads и переменные int для хранения значений спреда. После объявления наших переменных мы будем использовать функцию CopySpread для сохранения значений спреда в нашей переменной SpreadArray, начиная с 1-минутной свечи 2 недели назад до текущей. Если функция CopySpread по какой-то причине не сработает, вернем SpreadRating_Normal в качестве значения по умолчанию.

     //Declarations
             vector Spreads;
             int SpreadArray[],SpreadAvg=0,SpreadMax=0,SpreadMin=0,
                               SpreadUpper=0,SpreadLower=0,SpreadAvgUpper=0,
                               SpreadAvgLower=0,SpreadMidUpper=0,SpreadMidLower=0;
    
             //Get Spread data from CopySpread built-in function for 2 weeks using M1 timeframe.
             if(CopySpread(GetSymbolName(),PERIOD_M1,iTime(GetSymbolName(),PERIOD_W1,2),
                           iTime(GetSymbolName(),PERIOD_M1,0),SpreadArray)==-1)
               {
                Print("Error trying to retrieve spread values");
                return SpreadRating_Normal;//Retrieve default value when failed.
               }

    После успешного выполнения CopySpread мы присвоим вектору Spreads целочисленные значения из нашего SpreadArray. Затем нам необходимо получить основную информацию из этих значений массива, например, максимальный спред за двухнедельный период, а также минимальный спред и спреды. Мы сохраним эти значения в SpreadMax, SpreadMin и SpreadAvg соответственно. Теперь мы хотим получить разные средние значения из этих трех предыдущих значений.

    Для переменной SpreadMidUpper нам нужно среднее между SpreadAvg и SpreadMax, для переменной SpreadMidLower - среднее между SpreadAvg и SpreadMin, для переменной SpreadAvgUpper - среднее между SpreadAvg и SpreadMidUpper, для переменной SpreadAvgLower - среднее между SpreadAvg и SpreadMidLower, для переменной SpreadUpper - среднее между SpreadMidUpper и SpreadMax, для переменной SpreadLower - среднее между SpreadMidLower и SpreadMin. Нам также понадобится текущий спред символа для сравнения и классификации спреда.

    Spreads.Assign(SpreadArray);//Assign spread array into Spreads vector variable
    
                SpreadMax = int(Spreads.Max());//Assign max spread
                SpreadMin = int(Spreads.Min());//Assign min spread
                SpreadAvg = int(Spreads.Median());//Assign average spread
    
                //Divide Spread into sectors based of different averages.
                SpreadMidUpper = int((SpreadAvg+SpreadMax)/2);
                SpreadMidLower = int((SpreadAvg+SpreadMin)/2);
                SpreadAvgUpper = int((SpreadAvg+SpreadMidUpper)/2);
                SpreadAvgLower = int((SpreadAvg+SpreadMidLower)/2);
                SpreadUpper = int((SpreadMidUpper+SpreadMax)/2);
                SpreadLower = int((SpreadMidLower+SpreadMin)/2);
    
                int Spread = Spread(SYMBOL);//Assign Symbol's Spread

    Значения спреда

    У нас есть 5 классификаций спредов, а именно:

      • Excellent (отлично): Текущий спред меньше переменной SpreadLower или равен SpreadMin.
      • Good (хорошо): Текущий спред больше или равен переменной SpreadLower и меньше переменной SpreadAvgLower.
      • Normal (нормально): Когда текущий спред меньше или равен переменной SpreadAvgLower, а также меньше или равен переменной SpreadAvgUpper.
      • Bad (плохо): Текущий спред больше SpreadAvgUpper, а также меньше или равен SpreadUpper.
      • Terrible (ужасно): Текущий спред больше SpreadUpper

                if(Spread<SpreadLower||Spread==SpreadMin)//Excellent
                  {
                   return SpreadRating_Excellent;
                  }
                else
                   if(Spread>=SpreadLower&&Spread<SpreadAvgLower)//Good
                     {
                      return SpreadRating_Good;
                     }
                   else
                      if(Spread>=SpreadAvgLower&&Spread<=SpreadAvgUpper)//Normal
                        {
                         return SpreadRating_Normal;
                        }
                      else
                         if(Spread>SpreadAvgUpper&&Spread<=SpreadUpper)//Bad
                           {
                            return SpreadRating_Bad;
                           }
                         else//Terrible
                           {
                            return SpreadRating_Terrible;
                           }
    • Объявление функции для извлечения цвета спреда на основе его ранга
    color             SpreadColor(string SYMBOL=NULL);//Retrieve Spread Color

    Чтобы получить цвет спреда для каждого значения перечисления спреда, рассмотрим использование оператора switch, поскольку значения перечисления являются постоянными. Цвета для каждого ранга показаны ниже:

      • Excellent - clrBlue в светлом режиме

    Excellent в светлом режиме

     иначе - clrLightCyan

    Excellent в темном режиме

      • Good - clrCornflowerBlue в светлом режиме

    Good в светлом режиме
    иначе - clrLightGreen
    Good в темном режиме

      • Normal - clrBlack в светлом режиме
    Normal в светлом режиме
     иначе - clrWheat
    Normal в темном режиме
      • Bad - clrOrange
      • Terrible - clrRed
      • По умолчанию: clrBlack в светлом режиме, иначе - clrWheat

    //+------------------------------------------------------------------+
    //|Retrieve Spread Color                                             |
    //+------------------------------------------------------------------+
    color CSymbolProperties::SpreadColor(string SYMBOL=NULL)
      {
       switch(SpreadValue(SYMBOL))//Get Spread Rating value
         {
          case SpreadRating_Excellent://Excellent Spread
             return (isLightMode)?clrBlue:clrLightCyan;
             break;
          case SpreadRating_Good://Good Spread
             return (isLightMode)?clrCornflowerBlue:clrLightGreen;
             break;
          case SpreadRating_Normal://Normal Spread
             return (isLightMode)?clrBlack:clrWheat;
             break;
          case SpreadRating_Bad://Bad Spread
             return clrOrange;
             break;
          case SpreadRating_Terrible://Terrible Spread
             return clrRed;
             break;
          default://failed to be identified
             return (isLightMode)?clrBlack:clrWheat;//Retrieve default color when failed.
             break;
         }
      }
    • Объявление строковой функции для получения описания спреда
    string            SpreadDesc(string SYMBOL=NULL);//Retrieve Spread Description
    //+------------------------------------------------------------------+
    //|Retrieve Spread Description                                       |
    //+------------------------------------------------------------------+
    string CSymbolProperties::SpreadDesc(string SYMBOL=NULL)
      {
       switch(SpreadValue(SYMBOL))//Get Spread Rating value
         {
          case SpreadRating_Excellent://Excellent Spread
             return "Excellent";
             break;
          case SpreadRating_Good://Good Spread
             return "Good";
             break;
          case SpreadRating_Normal://Normal Spread
             return "Normal";
             break;
          case SpreadRating_Bad://Bad Spread
             return "Bad";
             break;
          case SpreadRating_Terrible://Terrible Spread
             return "Terrible";
             break;
          default://failed to be identified
             return "Unknown";//Retrieve default value when failed.
             break;
         }
      }
    • Объявление строковой функции для получения описания символа
    string            Description(string SYMBOL=NULL);//Retrieve Symbol's Description
    //+------------------------------------------------------------------+
    //|Retrieve Symbol's Description                                     |
    //+------------------------------------------------------------------+
    string CSymbolProperties::Description(string SYMBOL=NULL)
      {
       if(SetSymbolName(SYMBOL))//Set Symbol
         {
          return CSymbol.Description();
         }
       Print("Unable to retrieve Symbol's Description");
       return "";//Retrieve an empty string when failed.
      }

    Symbol Description


    Класс свойств графика

    Класс изменен по сравнению с Частью 2. Класс свойств графика теперь будет наследоваться от класса графика из включенных классов MQL5. Структура ChartProp будет хранить все свойства графика, в которые мы внесем изменения. Наша публичная функция ChartRefresh вызовет функцию ChartGet, которая инициализирует свойства графика, а затем мы вызовем функцию ChartSet, которая настроит график с использованием значений свойств графика из ChartGet.

    #include "SymbolProperties.mqh"
    #include <Charts/Chart.mqh>
    CSymbolProperties CSymbol;//Symbol Properties object
    //+------------------------------------------------------------------+
    //|ChartProperties class                                             |
    //+------------------------------------------------------------------+
    class CChartProperties : public CChart
      {
    private:
    //Structure for chart properties
       struct ChartProp
         {
          ENUM_CHART_MODE mode;//Chart Mode
          color          clrBackground;//Chart Background Color
          color          clrForeground;//Chart Foreground Color
          color          clrLineLast;//Chart Line Color
          color          clrCandleBear;//Chart Bear Candle Color
          color          clrBarDown;//Chart Down Candle Color
          color          clrCandleBull;//Chart Bull Candle Color
          color          clrBarUp;//Chart Up Candle Color
          color          clrLineAsk;//Chart Ask Color
          color          clrLineBid;//Chart Bid Color
          color          clrChartLine;//Chart Line Color
          color          clrStopLevels;//Chart Stop Level Color
          color          clrVolumes;//Chart Volumes Color
          bool           Foreground;//Chart Foreground Visibility
          bool           ShowLineAsk;//Chart Ask Line Visibility
          bool           ShowLineBid;//Chart Bid Line Visibility
          bool           ShowPeriodSep;//Chart Period Separator Visibility
          bool           ShowOHLC;//Chart Open-High-Low-Close Visibility
          bool           ShowGrid;//Chart Grid Visibility
          ENUM_CHART_VOLUME_MODE ShowVolumes;//Chart Volumes Visibility
          bool           AutoScroll;//Chart Auto Scroll Option
          bool           Shift;//Chart Shift Option
          double         ShiftSize;//Chart Shift Size
          bool           ShowObjectDescr;//Chart Object Descriptions
          ulong          CHART_SHOW_TRADE_LEVELS;//Chart Trade Levels Visibility
          ulong          CHART_SHOW_ONE_CLICK;//Chart One Click Trading Visibility
          ulong          CHART_SHOW_TICKER;//Chart Ticker Visibility
          ulong          CHART_DRAG_TRADE_LEVELS;//Chart Drag Trade levels
          ENUM_CHART_POSITION Navigate;//Chart Navigate
         };
       ChartProp         DefaultChart,MyChart;//Used to store chart properties
       void              ChartSet(ChartProp &Prop);//Apply Chart format
       void              ChartGet();//Assign Chart property values
    public:
                         CChartProperties();//Constructor
                        ~CChartProperties(void);//Destructor
                         //Configure the chart
       void              ChartRefresh() {ChartGet();ChartSet(MyChart);}
       string            GetChartPeriodName();//Retrieve Period name
      };

    В конструкторе мы присваиваем наследуемой переменной m_chart_id текущий идентификатор графика.

    //+------------------------------------------------------------------+
    //|Constructor                                                       |
    //+------------------------------------------------------------------+
    CChartProperties::CChartProperties()
      {
       m_chart_id=ChartID();//Set chart id
       ChartGet();//Get chart values
       ChartSet(MyChart);//customize chart
      }

    Для функции ChartGet мы присваиваем значения нашим переменным DefaultChart и MyChart, где DefaultChart будет хранить текущие свойства графика до того, как мы изменим график, а MyChart будет хранить наши пользовательские значения.

    //+------------------------------------------------------------------+
    //|Assign Chart property values                                      |
    //+------------------------------------------------------------------+
    void CChartProperties::ChartGet()
      {
       DefaultChart.mode = Mode();//assign chart mode
       MyChart.mode = CHART_CANDLES;//assign custom chart mode
       DefaultChart.clrBackground = ColorBackground();//assign Background color
       MyChart.clrBackground = (isLightMode)?clrWhite:clrBlack;//assign custom Background color
       DefaultChart.clrForeground = ColorForeground();//assign foreground color
       MyChart.clrForeground = (isLightMode)?clrBlack:clrWhite;//assign custom foreground color
       DefaultChart.clrLineLast = ColorLineLast();//assign Chart Line Color
       MyChart.clrLineLast = clrWhite;//assign custom Chart Line Color
       DefaultChart.clrCandleBear = ColorCandleBear();//assign Chart Bear Candle Color
       MyChart.clrCandleBear = clrBlack;//assign custom Chart Bear Candle Color
       DefaultChart.clrBarDown = ColorBarDown();//assign Chart Down Candle Color
       MyChart.clrBarDown = (isLightMode)?clrBlack:CSymbol.Background();//assign custom Chart Down Candle Color
       DefaultChart.clrCandleBull = ColorCandleBull();//assign Chart Bull Candle Color
       MyChart.clrCandleBull = CSymbol.Background();//assign custom Chart Bull Candle Color
       DefaultChart.clrBarUp = ColorBarUp();//assign Chart Up Candle Color
       MyChart.clrBarUp = (isLightMode)?clrBlack:CSymbol.Background();//assign custom Chart Up Candle Color
       DefaultChart.clrLineAsk = ColorLineAsk();//assign Chart Ask Color 
       MyChart.clrLineAsk = (isLightMode)?clrBlack:clrWhite;//assign custom Chart Ask Color
       DefaultChart.clrLineBid = ColorLineBid();//assign Chart Bid Color
       MyChart.clrLineBid = (isLightMode)?clrBlack:CSymbol.Background();//assign custom Chart Bid Color
       DefaultChart.clrChartLine = ColorChartLine();//assign Chart Line Color
       MyChart.clrChartLine = (isLightMode)?clrBlack:clrWhite;//assign custom Chart Line Color
       DefaultChart.clrStopLevels = ColorStopLevels();//assign Chart Stop Level Color
       MyChart.clrStopLevels = clrRed;//assign custom Chart Stop Level Color
       DefaultChart.clrVolumes = ColorVolumes();//assign Chart Volumes Color
       MyChart.clrVolumes = clrGreen;//assign custom Chart Volumes Color
       DefaultChart.Foreground = Foreground();//assign Chart Foreground Visibility
       MyChart.Foreground = false;//assign custom Chart Foreground Visibility
       DefaultChart.ShowLineAsk = ShowLineAsk();//assign Chart Ask Line Visibility
       MyChart.ShowLineAsk = true;//assign custom Chart Ask Line Visibility
       DefaultChart.ShowLineBid = ShowLineBid();//assign Chart Bid Line Visibility
       MyChart.ShowLineBid = true;//assign custom Chart Bid Line Visibility
       DefaultChart.ShowPeriodSep = ShowPeriodSep();//assign Chart Period Separator Visibility
       MyChart.ShowPeriodSep = true;//assign custom Chart Period Separator Visibility
       DefaultChart.ShowOHLC = ShowOHLC();//assign Chart Open-High-Low-Close Visibility
       MyChart.ShowOHLC = false;//assign custom Chart Open-High-Low-Close Visibility
       DefaultChart.ShowGrid = ShowGrid();//assign Chart Grid Visibility
       MyChart.ShowGrid = false;//assign custom Chart Grid Visibility
       DefaultChart.ShowVolumes = ShowVolumes();//assign Chart Volumes Visibility
       MyChart.ShowVolumes = CHART_VOLUME_HIDE;//assign custom Chart Volumes Visibility
       DefaultChart.AutoScroll = AutoScroll();//assign Chart Auto Scroll Option
       MyChart.AutoScroll = true;//assign custom Chart Auto Scroll Option
       DefaultChart.Shift = Shift();//assign Chart Shift Option
       MyChart.Shift = true;//assign custom Chart Shift Option
       DefaultChart.ShiftSize = ShiftSize();//assign Chart Shift Size
       MyChart.ShiftSize = 15;//assign custom Chart Shift Size
       DefaultChart.ShowObjectDescr = ShowObjectDescr();//assign Chart Object Descriptions
       MyChart.ShowObjectDescr = false;//assign custom Chart Object Descriptions
       DefaultChart.Navigate = CHART_END;//assign Chart Navigate
       MyChart.Navigate = CHART_END;//assign custom Chart Navigate
       //---assign Chart Trade Levels Visibility
       DefaultChart.CHART_SHOW_TRADE_LEVELS = ChartGetInteger(ChartId(),CHART_SHOW_TRADE_LEVELS);
       //---assign custom Chart Trade Levels Visibility
       MyChart.CHART_SHOW_TRADE_LEVELS = ulong(true);
       //---assign Chart One Click Trading Visibility
       DefaultChart.CHART_SHOW_ONE_CLICK = ChartGetInteger(ChartId(),CHART_SHOW_ONE_CLICK);
       //---assign custom Chart One Click Trading Visibility
       MyChart.CHART_SHOW_ONE_CLICK = ulong(false);
       //---assign Chart Ticker Visibility
       DefaultChart.CHART_SHOW_TICKER = ChartGetInteger(ChartId(),CHART_SHOW_TICKER);
       //---assign custom Chart Ticker Visibility
       MyChart.CHART_SHOW_TICKER = ulong(false);
       //---assign Chart Drag Trade levels
       DefaultChart.CHART_DRAG_TRADE_LEVELS = ChartGetInteger(ChartId(),CHART_DRAG_TRADE_LEVELS);
       //---assign custom Chart Drag Trade levels
       MyChart.CHART_DRAG_TRADE_LEVELS = ulong(false);
      }

    Наша функция ChartSet будет принимать нашу структуру ChartProp в качестве аргумента для настройки текущего графика.

    //+------------------------------------------------------------------+
    //|Apply Chart format                                                |
    //+------------------------------------------------------------------+
    void CChartProperties::ChartSet(ChartProp &Prop)
      {
       Mode(Prop.mode);//Set Chart Candle Mode
       ColorBackground(Prop.clrBackground);//Set Chart Background Color
       ColorForeground(Prop.clrForeground);//Set Chart Foreground Color
       ColorLineLast(Prop.clrLineLast);//Set Chart Line Color
       ColorCandleBear(Prop.clrCandleBear);//Set Chart Bear Candle Color
       ColorBarDown(Prop.clrBarDown);//Set Chart Down Candle Color
       ColorCandleBull(Prop.clrCandleBull);//Set Chart Bull Candle Color
       ColorBarUp(Prop.clrBarUp);//Set Chart Up Candle Color
       ColorLineAsk(Prop.clrLineAsk);//Set Chart Ask Color
       ColorLineBid(Prop.clrLineBid);//Set Chart Bid Color
       ColorChartLine(Prop.clrChartLine);//Set Chart Line Color
       ColorStopLevels(Prop.clrStopLevels);//Set Chart Stop Level Color
       ColorVolumes(Prop.clrVolumes);//Set Chart Volumes Color
       Foreground(Prop.Foreground);//Set if Chart is in Foreground Visibility
       ShowLineAsk(Prop.ShowLineAsk);//Set Chart Ask Line Visibility
       ShowLineBid(Prop.ShowLineBid);//Set Chart Bid Line Visibility
       ShowPeriodSep(Prop.ShowPeriodSep);//Set Chart Period Separator Visibility
       ShowOHLC(Prop.ShowOHLC);//Set Chart Open-High-Low-Close Visibility
       ShowGrid(Prop.ShowGrid);//Set Chart Grid Visibility
       ShowVolumes(Prop.ShowVolumes);//Set Chart Volumes Visibility
       AutoScroll(Prop.AutoScroll);//Set Chart Auto Scroll Option
       Shift(Prop.Shift);//Set Chart Shift Option
       ShiftSize(Prop.ShiftSize);//Set Chart Shift Size Value
       ShowObjectDescr(Prop.ShowObjectDescr);//Set Chart Show Object Descriptions
       ChartSetInteger(ChartId(),CHART_SHOW_TRADE_LEVELS,Prop.CHART_SHOW_TRADE_LEVELS);//Set Chart Trade Levels Visibility
       ChartSetInteger(ChartId(),CHART_SHOW_ONE_CLICK,Prop.CHART_SHOW_ONE_CLICK);//Set Chart One Click Trading Visibility
       ChartSetInteger(ChartId(),CHART_SHOW_TICKER,Prop.CHART_SHOW_TICKER);//Set Chart Ticker Visibility
       ChartSetInteger(ChartId(),CHART_DRAG_TRADE_LEVELS,Prop.CHART_DRAG_TRADE_LEVELS);//Set Chart Drag Trade levels
       Navigate(Prop.Navigate);//Set Chart Navigate
      }

    Что касается функции GetChartPeriodName, мы получим имя периода текущего графика с помощью оператора switch.

    //+------------------------------------------------------------------+
    //|Retrieve Period name                                              |
    //+------------------------------------------------------------------+
    string CChartProperties::GetChartPeriodName()
      {
       switch(ChartPeriod(ChartId()))//Get chart Period with chart id
         {
          case PERIOD_M1:
             return("M1");
          case PERIOD_M2:
             return("M2");
          case PERIOD_M3:
             return("M3");
          case PERIOD_M4:
             return("M4");
          case PERIOD_M5:
             return("M5");
          case PERIOD_M6:
             return("M6");
          case PERIOD_M10:
             return("M10");
          case PERIOD_M12:
             return("M12");
          case PERIOD_M15:
             return("M15");
          case PERIOD_M20:
             return("M20");
          case PERIOD_M30:
             return("M30");
          case PERIOD_H1:
             return("H1");
          case PERIOD_H2:
             return("H2");
          case PERIOD_H3:
             return("H3");
          case PERIOD_H4:
             return("H4");
          case PERIOD_H6:
             return("H6");
          case PERIOD_H8:
             return("H8");
          case PERIOD_H12:
             return("H12");
          case PERIOD_D1:
             return("Daily");
          case PERIOD_W1:
             return("Weekly");
          case PERIOD_MN1:
             return("Monthly");
         }
       return("unknown period");
      }

    Наш деструктор восстановит предыдущую конфигурацию графика до внесения в него каких-либо изменений.

    //+------------------------------------------------------------------+
    //|Destructor                                                        |
    //+------------------------------------------------------------------+
    CChartProperties::~CChartProperties()
      {
       ChartSet(DefaultChart);//restore chart default configuration
       m_chart_id=-1;//reset chart id
      }



    Класс свойств объекта

    В этом классе было внесено несколько изменений для всех пользовательских цветов текста объектов. В Части 2 мы могли использовать только один цвет текста объекта для всех текстовых объектов. Наше решение — объявить переменную цвета вне класса с именем TextObj_color.

    #include "ChartProperties.mqh"
    color TextObj_color;
    //+------------------------------------------------------------------+
    //|ObjectProperties class                                            |
    //+------------------------------------------------------------------+
    class CObjectProperties:public CChartProperties
      {
    private:
       //Simple  chart objects structure
       struct ObjStruct
         {
          long           ChartId;
          string         Name;
         } Objects[];//ObjStruct variable array
    
       //-- Add chart object to Objects array
       void              AddObj(long chart_id,string name)
         {
          ArrayResize(Objects,Objects.Size()+1,Objects.Size()+2);
          Objects[Objects.Size()-1].ChartId=chart_id;
          Objects[Objects.Size()-1].Name=name;
         }
    
    protected:
       void              DeleteObj()
         {
          for(uint i=0;i<Objects.Size();i++)
            {
             ObjectDelete(Objects[i].ChartId,Objects[i].Name);
            }
         }
    
    public:
                         CObjectProperties(void) {}//Class constructor
    
       //-- Create Rectangle chart object
       void              Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor);
    
       //-- Create text chart object
       void              TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                                 ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10);
    
       //-- Create Event object
       void               EventObj(long chartID,string name,string description,datetime eventdate);
    
       //-- Class destructor removes all chart objects created previously
                        ~CObjectProperties(void)
         {
          DeleteObj();
         }
      };

    Как мы увидим ниже, параметров у функции Textobj довольно много. Чтобы их не стало еще больше, мы просто используем Textobj_color для изменения цвета текстового объекта.

    //+------------------------------------------------------------------+
    //|Create text chart object                                          |
    //+------------------------------------------------------------------+
    void CObjectProperties::TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                                    ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10)
      {
       ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
       if(ObjectCreate(chartID,name,OBJ_LABEL,0,0,0))//Create object label
         {
          AddObj(chartID,name);//Add object to array
          ObjectSetInteger(chartID,name,OBJPROP_XDISTANCE,x_coord);//Set x Distance/coordinate
          ObjectSetInteger(chartID,name,OBJPROP_YDISTANCE,y_coord);//Set y Distance/coordinate
          ObjectSetInteger(chartID,name,OBJPROP_CORNER,Corner);//Set object's corner anchor
          ObjectSetString(chartID,name,OBJPROP_TEXT,text);//Set object's text
          ObjectSetInteger(chartID,name,OBJPROP_COLOR,TextObj_color);//Set object's color
          ObjectSetInteger(chartID,name,OBJPROP_FONTSIZE,fontsize);//Set object's font-size
         }
       else
         {
          Print("Failed to create object: ",name);
         }
      }

    В функцию Square было внесено небольшое изменение, позволяющее использовать разные цвета фона в зависимости от цветового режима графика.

    //+------------------------------------------------------------------+
    //|Create Rectangle chart object                                     |
    //+------------------------------------------------------------------+
    void CObjectProperties::Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor)
      {
       const int              sub_window=0;             // subwindow index
       const int              x=x_coord;                // X coordinate
       const int              y=y_coord;                // Y coordinate
       const color            back_clr=(isLightMode)?clrWhite:clrBlack;// background color
       const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // border type
       const color            clr=clrRed;               // flat border color (Flat)
       const ENUM_LINE_STYLE  style=STYLE_SOLID;        // flat border style
       const int              line_width=0;             // flat border width
       const bool             back=false;               // in the background
       const bool             selection=false;          // highlight to move
       const bool             hidden=true;              // hidden in the object list
    
       ObjectDelete(chart_ID,name);//Delete previous object with the same name and chart id
       if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))//create rectangle object label
         {
          AddObj(chart_ID,name);//Add object to array
          ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);//Set x Distance/coordinate
          ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);//Set y Distance/coordinate
          ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);//Set object's width/x-size
          ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);//Set object's height/y-size
          ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);//Set object's background color
          ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);//Set object's border type
          ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,Anchor);//Set objects anchor point
          ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);//Set object's color
          ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);//Set object's style
          ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);//Set object's flat border width
          ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);//Set if object is in foreground or not
          ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);//Set if object is selectable/dragable
          ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);//Set if object is Selected
          ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);//Set if object is hidden in object list
          ChartRedraw(chart_ID);
         }
       else
         {
          Print("Failed to create object: ",name);
         }
      }


    Файл заголовка CommonVariables

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

    #define NEWS_DATABASE_MEMORY           StringFormat("Calendar_%s_%d_%s.sqlite",broker,ChartID(),(MQLInfoInteger(MQL_TESTER)?"TESTER":"REAL"))

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

    enum Choice
      {
       Yes,//YES
       No//NO
      };
    
    enum DayOfTheWeek
      {
       Monday,//MONDAY
       Tuesday,//TUESDAY
       Wednesday,//WEDNESDAY
       Thursday,//THURSDAY
       Friday,//FRIDAY
       AllDays//ALL DAYS
      };
    
    //+------------------------------------------------------------------+
    //|Convert enumeration Choice into a boolean value                   |
    //+------------------------------------------------------------------+
    bool Answer(Choice choose)
      {
       return (choose==Yes)?true:false;
      }


    Класс Time Variables

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

    //+------------------------------------------------------------------+
    //|TimeVariables class                                               |
    //+------------------------------------------------------------------+
    class CTimeVariables
      {
    private:
       //---Array to store candlestick times
       datetime          CandleTime[2000];
    public:
                         CTimeVariables(void);
       //---Set Array index time
       void              SetTime(uint index,datetime time);
       //---Get Array index time
       datetime          GetTime(uint index);
      };

    В конструкторе мы установим время по умолчанию для всех индексов в массиве CandleTime.

    //+------------------------------------------------------------------+
    //|Constructor                                                       |
    //+------------------------------------------------------------------+
    CTimeVariables::CTimeVariables()
      {
       for(uint i=0; i<CandleTime.Size(); i++)
         {
          CandleTime[i]=D'1970.01.01';
         }
      }

    В функции SetTime у нас есть два параметра: один - для индекса массива, а другой - для даты и времени. Если аргумент индекса больше или равен нулю и меньше размера CandleTime, то мы присвоим индекс массива с аргументом времени. 

    //+------------------------------------------------------------------+
    //|Set Array index time                                              |
    //+------------------------------------------------------------------+
    void CTimeVariables::SetTime(uint index,datetime time)
      {
       if(index>=0&&index<CandleTime.Size())
         {
          CandleTime[index] = time;
         }
      }

    Функция GetTime принимает один положительный целочисленный аргумент для извлечения даты и времени из значения индекса массива CandleTime, если аргумент индекса допустим.

    //+------------------------------------------------------------------+
    //|Get Array index time                                              |
    //+------------------------------------------------------------------+
    datetime CTimeVariables::GetTime(uint index)
      {
       return (index>=0&&index<CandleTime.Size())?CandleTime[index]:datetime(0);
      }


    Класс Time Management

    Объявим перечисление DSTSchedule, чтобы пользователь/трейдер мог выбирать между автоматическим летним временем (Auto DST) и пользовательским летним временем (Custom DST) для входных параметров советника. Переменная MySchedule будет использоваться для хранения пользовательского летнего времени.

    //-- Enumeration for DST schedule
    enum DSTSchedule
      {
       AutoDst_Selection,//AUTO DST
       CustomDst_Selection//CUSTOM DST
      } MyDST;
    
    DST_type MySchedule;//Variable for custom DST schedule

    Функция ниже возвращает час для определенной даты в целочисленном типе данных. 

    int               ReturnHour(datetime time);//Returns the Hour for a specific date
    //+------------------------------------------------------------------+
    //|Returns the Hour for a specific date                              |
    //+------------------------------------------------------------------+
    int CTimeManagement::ReturnHour(datetime time)
      {
       return Time(time).hour;
      }

    Функция ниже возвращает минуты для определенной даты в целочисленном типе данных. 

    int               ReturnMinute(datetime time);//Returns the Minute for a specific date
    //+------------------------------------------------------------------+
    //|Returns the Minute for a specific date                            |
    //+------------------------------------------------------------------+
    int CTimeManagement::ReturnMinute(datetime time)
      {
       return Time(time).min;
      }

    Функция ниже возвращает секунду для определенной даты в целочисленном типе данных. 

    int               ReturnSecond(datetime time);//Returns the Second for s specific date
    //+------------------------------------------------------------------+
    //|Returns the Second for s specific date                            |
    //+------------------------------------------------------------------+
    int CTimeManagement::ReturnSecond(datetime time)
      {
       return Time(time).sec;
      }

    Функция ниже возвращает MqlDateTime для аргумента datetime.

    //-- Will convert datetime to MqlDateTime
       MqlDateTime       Time(datetime Timetoformat);
    //+------------------------------------------------------------------+
    //|Will convert datetime to MqlDateTime                              |
    //+------------------------------------------------------------------+
    MqlDateTime CTimeManagement::Time(datetime Timetoformat)
      {
       TimeToStruct(Timetoformat,timeFormat);
       return timeFormat;
      }

    Функция ниже возвращает дату и время для аргумента datetime с изменениями в часах, минутах и секундах.

    //-- Will return a datetime with changes to the hour,minute and second
       datetime          Time(datetime time,int Hour,int Minute,int Second);
    //+------------------------------------------------------------------+
    //|Will return a datetime with changes to the hour,minute and second |
    //+------------------------------------------------------------------+
    datetime CTimeManagement::Time(datetime time,int Hour,int Minute,int Second)
      {
       timeFormat=Time(time);
       timeFormat.hour=Hour;
       timeFormat.min=Minute;
       timeFormat.sec=Second;
       return StructToTime(timeFormat);
      }

    Функция ниже возвращает дату и время для аргумента datetime с изменением часов и минут.

    //-- Will return a datetime with changes to the hour and minute
       datetime          Time(datetime time,int Hour,int Minute);
    //+------------------------------------------------------------------+
    //|Will return a datetime with changes to the hour and minute        |
    //+------------------------------------------------------------------+
    datetime CTimeManagement::Time(datetime time,int Hour,int Minute)
      {
       timeFormat=Time(time);
       timeFormat.hour=Hour;
       timeFormat.min=Minute;
       return StructToTime(timeFormat);
      }

    Функция ниже возвращает true, если время TimeTradeServer находится в пределах аргументов BeginTime и EndTime.

    //-- Check current time is within a time range
       bool              TimeIsInRange(datetime BeginTime,datetime EndTime);
    //+------------------------------------------------------------------+
    //|Check current time is within a time range                         |
    //+------------------------------------------------------------------+
    bool CTimeManagement::TimeIsInRange(datetime BeginTime,datetime EndTime)
      {
       if(BeginTime<=TimeTradeServer()&&EndTime>=TimeTradeServer())
         {
          return true;
         }
       return false;
      }

    Функция ниже вернет true, если PreEvent datetime меньше или равно TimeTradeServer, а EventTime больше TimeTradeServer.

    //-- Check if current time is within preEvent time and Event time
       bool              TimePreEvent(datetime PreEvent,datetime Event);
    //+------------------------------------------------------------------+
    //|Check if current time is within preEvent time and Event time      |
    //+------------------------------------------------------------------+
    bool CTimeManagement::TimePreEvent(datetime PreEventTime,datetime EventTime)
      {
       if(PreEventTime<=TimeTradeServer()&&EventTime>TimeTradeServer())
         {
          return true;
         }
       return false;
      }

    Функция ниже вернет MqlDateTime для текущего времени с изменением часов и минут.

    //-- Return MqlDateTime for current date time with custom hour and minute
       MqlDateTime       Today(int Hour,int Minute);
    //+------------------------------------------------------------------+
    //|Return MqlDateTime for current date time with custom hour and     |
    //|minute                                                            |
    //+------------------------------------------------------------------+
    MqlDateTime CTimeManagement::Today(int Hour,int Minute)
      {
       TimeTradeServer(today);
       today.hour=Hour;
       today.min=Minute;
       return today;
      }

    Функция ниже вернет MqlDateTime для текущего времени с изменением часов, минут и секунд.

    //-- Return MqlDateTime for current date time with custom hour, minute and second
       MqlDateTime       Today(int Hour,int Minute,int Second);
    //+------------------------------------------------------------------+
    //|Return MqlDateTime for current date time with custom hour, minute |
    //|and second                                                        |
    //+------------------------------------------------------------------+
    MqlDateTime CTimeManagement::Today(int Hour,int Minute,int Second)
      {
       TimeTradeServer(today);
       today.hour=Hour;
       today.min=Minute;
       today.sec=Second;
       return today;
      }

    Функция ниже вернет значение true, если текущий день равен соответствующему дню недели или перечисление DayOfTheWeek равно AllDays.

    //-- Check current day of the week
       bool              isDayOfTheWeek(DayOfTheWeek Day);
    //+------------------------------------------------------------------+
    //|Check current day of the week                                     |
    //+------------------------------------------------------------------+
    bool CTimeManagement::isDayOfTheWeek(DayOfTheWeek Day)
      {
       switch(Day)
         {
          case  Monday://Monday
             if(DayOfWeek(TimeTradeServer())==MONDAY)
               {
                return true;
               }
             break;
          case Tuesday://Tuesday
             if(DayOfWeek(TimeTradeServer())==TUESDAY)
               {
                return true;
               }
             break;
          case Wednesday://Wednesday
             if(DayOfWeek(TimeTradeServer())==WEDNESDAY)
               {
                return true;
               }
             break;
          case Thursday://Thursday
             if(DayOfWeek(TimeTradeServer())==THURSDAY)
               {
                return true;
               }
             break;
          case Friday://Friday
             if(DayOfWeek(TimeTradeServer())==FRIDAY)
               {
                return true;
               }
             break;
          case AllDays://All days
             return true;
             break;
          default://Unknown
             break;
         }
       return false;
      }

    Функция ниже вернет день недели для определенной даты.

    //-- Return enumeration Day of week for a certain date
       ENUM_DAY_OF_WEEK  DayOfWeek(datetime time);
    //+------------------------------------------------------------------+
    //|Return enumeration Day of week for a certain date                 |
    //+------------------------------------------------------------------+
    ENUM_DAY_OF_WEEK CTimeManagement::DayOfWeek(datetime time)
      {
       return (ENUM_DAY_OF_WEEK)Time(time).day_of_week;
      }


    Класс Candle Properties

    В этот класс добавлена новая функция.

    //+------------------------------------------------------------------+
    //|CandleProperties class                                            |
    //+------------------------------------------------------------------+
    class CCandleProperties : public CChartProperties
      {
    private:
       CTimeManagement   Time;//TimeManagement object
       CTimeVariables    CTV;//Timevariables object
    public:
       double            Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Open-Price
       double            Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Close-Price
       double            High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle High-Price
       double            Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Low-Price
       bool              IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others
       bool              NewCandle(int index,ENUM_TIMEFRAMES period=PERIOD_CURRENT,string SYMBOL=NULL);//Check if a new candle is present
      };

    Функция NewCandle вернет true при формировании новой свечи, а затем сохранит время открытия текущей свечи в классе Timevariables с помощью функции SetTime. Ранее сохраненное время будет сравниваться с временем открытия текущей свечи, чтобы проверить, отличается ли время. Если отличается, то мы предполагаем, что сформировалась новая свеча.

    //+------------------------------------------------------------------+
    //|Check if a new candle is present                                  |
    //+------------------------------------------------------------------+
    bool CCandleProperties::NewCandle(int index,ENUM_TIMEFRAMES period=PERIOD_CURRENT,string SYMBOL=NULL)
      {
       if(CTV.GetTime(index) == iTime(((SYMBOL==NULL)?Symbol():SYMBOL),period,0))
         {
          return false;//Candle time are equal no new candles have formed
         }
       else
         {
         //--- Candle time has changed set the new time
          CTV.SetTime(index,iTime(((SYMBOL==NULL)?Symbol():SYMBOL),period,0));
          return true;
         }
      }


    Класс Sessions

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

    Время торговых сессий

    #include "Timemanagement.mqh"
    //+------------------------------------------------------------------+
    //|Sessions Class                                                    |
    //+------------------------------------------------------------------+
    class CSessions:CTimeManagement
      {
    public:
                         CSessions(void) {}
                        ~CSessions(void) {}
       //--- Check if trading Session has began
       bool              isSessionStart(int offsethour=0,int offsetmin=0);
       //--- Check if trading Session has ended
       bool              isSessionEnd(int offsethour=0,int offsetmin=45);
       //--- Get Session End datetime
       datetime          SessionEnd(int offsethour=0,int offsetmin=45);
      };

    Функция ниже проверит все действительные торговые сессии для текущего символа и дня недели. Как только будет найдена самая ранняя торговая сессия, мы добавим и выполним смещение к этому времени. Например, если самая ранняя торговая сессия начинается с 01:00-05:00, то наш час смещения равен 1, а минута смещения равна 0, наша торговая сессия начнется в 02:00-05:00. Цель определения времени начала сессии — избежать больших спредов, которые обычно возникают в начале торговой сессии. Если наша торговая сессия в данный момент активна, функция вернет true. 

    //+------------------------------------------------------------------+
    //|Check if trading Session has started                              |
    //+------------------------------------------------------------------+
    bool CSessions::isSessionStart(int offsethour=0,int offsetmin=0)
      {
    //--- Declarations
       datetime datefrom,dateto,DateFrom[],DateTo[];
    
    //--- Find all session times
       for(int i=0; i<10; i++)
         {
          //--- Get the session dates for the current symbol and Day of week
          if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
            {
             //--- Check if the end date's hour is at midnight
             if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
               {
                //--- Adjust the date to one minute before midnight
                dateto = Time(TimeTradeServer(),23,59);
               }
             //--- Re-adjust DateFrom Array size
             ArrayResize(DateFrom,int(ArraySize(DateFrom))+1,int(ArraySize(DateFrom))+2);
             //--- Assign the last array index datefrom value
             DateFrom[int(ArraySize(DateFrom))-1] = datefrom;
             //--- Re-adjust DateTo Array size
             ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
             //--- Assign the last array index dateto value
             DateTo[int(ArraySize(DateTo))-1] = dateto;
            }
         }
    
    //--- Check if there are session times
       if(DateFrom.Size()>0)
         {
          /* Adjust DateFrom index zero date as the first index date will be the earliest date
           from the whole array, we add the offset to this date only*/
          DateFrom[0] = TimePlusOffset(DateFrom[0],MinutesS(startoffsetmin));
          DateFrom[0] = TimePlusOffset(DateFrom[0],HoursS(startoffsethour));
          //--- Iterate through the whole array
          for(uint i=0; i<DateFrom.Size(); i++)
            {
             //--- Check if the current time is within the trading session
             if(TimeIsInRange(DateFrom[i],DateTo[i]))
               {
                return true;
               }
            }
         }
       else
         {
          //--- If there are no trading session times
          return true;
         }
       return false;
      }

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

    //+------------------------------------------------------------------+
    //|Check if trading Session has ended                                |
    //+------------------------------------------------------------------+
    bool CSessions::isSessionEnd(int offsethour=0,int offsetmin=45)
      {
    //--- Declarations
       datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;
    
    //--- Find all session times
       for(int i=0; i<10; i++)
         {
          //--- Get the session dates for the current symbol and Day of week
          if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
            {
             //--- Check if the end date's hour is at midnight
             if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
               {
                //--- Adjust the date to one minute before midnight
                dateto = Time(TimeTradeServer(),23,59);
               }
             //--- Re-adjust DateTo Array size
             ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
             //--- Assign the last array index dateto value
             DateTo[int(ArraySize(DateTo))-1] = dateto;
            }
         }
    
    //--- Check if there are session times
       if(DateTo.Size()>0)
         {
          //--- Assign lastdate a default value
          lastdate = DateTo[0];
          //--- Iterate through the whole array
          for(uint i=0; i<DateTo.Size(); i++)
            {
             //--- Check for the latest date in the array
             if(DateTo[i]>lastdate)
               {
                lastdate = DateTo[i];
               }
            }
         }
       else
         {
          //--- If there are no trading session times
          return false;
         }
       /* get the current time and modify the hour and minute time to the lastdate variable
       and assign the new datetime to sessionend variable*/
       sessionend = Today(ReturnHour(lastdate),ReturnMinute(lastdate));
    //--- Re-adjust the sessionend dates with the minute and hour offsets
       sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
       sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));
    
    //--- Check if sessionend date is more than the current time
       if(TimeTradeServer()<sessionend)
         {
          return false;
         }
       return true;
      }

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

    //+------------------------------------------------------------------+
    //|Get Session End datetime                                          |
    //+------------------------------------------------------------------+
    datetime CSessions::SessionEnd(int offsethour=0,int offsetmin=45)
      {
    //--- Declarations
       datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;
    
    //--- Find all session times
       for(int i=0; i<10; i++)
         {
          //--- Get the session dates for the current symbol and Day of week
          if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
            {
             //--- Check if the end date's hour is at midnight
             if(CTV.ReturnHour(dateto)==00||CTV.ReturnHour(dateto)==24)
               {
                //--- Adjust the date to one minute before midnight
                dateto = Time(TimeTradeServer(),23,59);
               }
             //--- Re-adjust DateTo Array size
             ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
             //--- Assign the last array index dateto value
             DateTo[int(ArraySize(DateTo))-1] = dateto;
            }
         }
    
    //--- Check if there are session times
       if(DateTo.Size()>0)
         {
          //--- Assign lastdate a default value
          lastdate = DateTo[0];
          //--- Iterate through the whole array
          for(uint i=0; i<DateTo.Size(); i++)
            {
             //--- Check for the latest date in the array
             if(DateTo[i]>lastdate)
               {
                lastdate = DateTo[i];
               }
            }
         }
       else
         {
          //--- If there are no trading session times
          return 0;
         }
       /* get the current time and modify the hour and minute time to the lastdate variable
       and assign the new datetime to sessionend variable*/
       sessionend = Today(ReturnHour(lastdate),ReturnMinute(lastdate));
    //--- Re-adjust the sessionend dates with the minute and hour offsets
       sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
       sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));
    //--- return sessionend date
       return sessionend;
      }


    Класс News

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

    Преимущества баз данных в памяти по сравнению с хранилищем

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

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

    Начнем с объявлений вне класса для использования во входных параметрах советника. Целочисленная переменная DBMemoryConnection будет хранить в памяти целочисленный хэндл соединения для нашей базы данных. Перечисление Calendar_Importance будет использоваться для выбора различной важности событий во входном параметре советника. Перечисление Event_Sector будет использоваться для выбора различных секторов событий во входном параметре советника. Перечисление Event_Frequency будет использоваться для выбора различной частоты событий для входного параметра советника. Перечисление Event_Type будет использоваться для выбора различных типов событий для входного параметра советника. Перечисление Event_Currency будет использоваться для выбора различных вариантов валюты события во входном параметре советника. Структурная переменная календаря UpcomingNews будет хранить сведения о следующем экономическом событии для легкого доступа в других классах/файлах. Мы объявили ее вне класса News.

    int       DBMemoryConnection;//In memory database handle
    
    //--- Enumeration for Calendar Importance
    enum Calendar_Importance
      {
       Calendar_Importance_None,//NONE
       Calendar_Importance_Low,//LOW
       Calendar_Importance_Moderate,//MODERATE
       Calendar_Importance_High,//HIGH
       Calendar_Importance_All//ALL
      } myImportance;
    
    //--- Enumeration for Calendar Sector
    enum Event_Sector
      {
       Event_Sector_None,//NONE
       Event_Sector_Market,//MARKET
       Event_Sector_Gdp,//GDP
       Event_Sector_Jobs,//JOBS
       Event_Sector_Prices,//PRICES
       Event_Sector_Money,//MONEY
       Event_Sector_Trade,//TRADE
       Event_Sector_Government,//GOVERNMENT
       Event_Sector_Business,//BUSINESS
       Event_Sector_Consumer,//CONSUMER
       Event_Sector_Housing,//HOUSING
       Event_Sector_Taxes,//TAXES
       Event_Sector_Holidays,//HOLIDAYS
       Event_Sector_ALL//ALL
      } mySector;
    
    //--- Enumeration for Calendar Event Frequency
    enum Event_Frequency
      {
       Event_Frequency_None,//NONE
       Event_Frequency_Week,//WEEK
       Event_Frequency_Month,//MONTH
       Event_Frequency_Quarter,//QUARTER
       Event_Frequency_Year,//YEAR
       Event_Frequency_Day,//DAY
       Event_Frequency_ALL//ALL
      } myFrequency;
    
    //--- Enumeration for Calendar Event type
    enum Event_Type
      {
       Event_Type_Event,//EVENT
       Event_Type_Indicator,//INDICATOR
       Event_Type_Holiday,//HOLIDAY
       Event_Type_All//ALL
      } myType;
    
    //--- Enumeration for Calendar Event Currency
    enum Event_Currency
      {
       Event_Currency_Symbol,//SYMBOL CURRENCIES
       Event_Currency_Margin,//SYMBOL MARGIN
       Event_Currency_Base,//SYMBOL BASE
       Event_Currency_Profit,//SYMBOL PROFIT
       Event_Currency_ALL,//ALL CURRENCIES
       Event_Currency_NZD_NZ,//NZD -> NZ
       Event_Currency_EUR_EU,//EUR -> EU
       Event_Currency_JPY_JP,//JPY -> JP
       Event_Currency_CAD_CA,//CAD -> CA
       Event_Currency_AUD_AU,//AUD -> AU
       Event_Currency_CNY_CN,//CNY -> CN
       Event_Currency_EUR_IT,//EUR -> IT
       Event_Currency_SGD_SG,//SGD -> SG
       Event_Currency_EUR_DE,//EUR -> DE
       Event_Currency_EUR_FR,//EUR -> FR
       Event_Currency_BRL_BR,//BRL -> BR
       Event_Currency_MXN_MX,//MXN -> MX
       Event_Currency_ZAR_ZA,//ZAR -> ZA
       Event_Currency_HKD_HK,//HKD -> HK
       Event_Currency_INR_IN,//INR -> IN
       Event_Currency_NOK_NO,//NOK -> NO
       Event_Currency_USD_US,//USD -> US
       Event_Currency_GBP_GB,//GBP -> GB
       Event_Currency_CHF_CH,//CHF -> CH
       Event_Currency_KRW_KR,//KRW -> KW
       Event_Currency_EUR_ES,//EUR -> ES
       Event_Currency_SEK_SE,//SEK -> SE
       Event_Currency_ALL_WW//ALL -> WW
      } myCurrency;
    
    //--- Structure variable to store Calendar next Event data
    Calendar UpcomingNews;

    Дополнительные функциональные возможности класса News:

    • Расширение перечисления CalendarComponentsEventInfo_View добавлен для отображения сведений о событиях в базе данных календаря, Currencies_View добавлен для отображения всех валют, доступных в экономическом календаре.
    • Структурный массив CalendarContents увеличился в размере с 10 до 12, чтобы вместить два новых представления EventInfo_View и Currencies_View.
    • Объявление переменной DBMemory типа структуры MQL5CalendarContents для хранения свойств базы данных.
    • Объявление структуры и переменных CalendarData для хранения всех данных из базы данных календаря в общей папке.
    • Объявление функции GetCalendar, которая запросит все отсортированные данные из базы данных календаря в общей папке и сохранит эти данные в массиве структуры CalendarData. Данные этого массива будут вставлены в нашу новую базу данных календаря в памяти после ее создания.
    • Объявление функции GetAutoDST для извлечения перечисления DST_type из таблицы AutoDST в базе данных Calendar в общей папке.
    • Объявление функции Request_Importance для извлечения строкового запроса на важность события на основе перечисления Calendar_Importance.
    • Объявление функции Request_Frequency для извлечения строкового запроса на частоту событий на основе перечисления Event_Frequency.
    • Объявление функции Request_Sector для извлечения строкового запроса сектора события на основе перечисления Event_Sector.
    • Объявление функции Request_Type для извлечения строкового запроса типа события на основе перечисления Event_Type.
    • Объявление функции Request_Currency для извлечения строкового запроса валюты события на основе перечисления Event_Currency.
    • Объявление функции EconomicDetailsMemory заполнит массив структуры календаря NewsTime событиями на определенную дату.
    • Объявление функции CreateEconomicDatabaseMemory создаст базу данных календаря в памяти.
    • Объявление функции EconomicNextEvent обновит структурную переменную UpcomingNews данными о следующем событии.
    • Объявление функции GetImpact извлекает данные о влиянии предстоящих событий.
    • Объявление функции IMPORTANCE преобразует строку Importance в перечисление важности событий календаря.
    • Объявление функции IMPORTANCE преобразует перечисление Calendar_Importance Enumeration в перечисление важности событий календаря.
    • Объявление функции GetImportance преобразует перечисление важности событий календаря в строковый ранг важности (Importance Rating).
    • Объявление функции GetImportance_color извлекает цвет для каждого перечисления важности событий календаря.
    • Объявление функции SECTOR преобразует перечисление Event_Sector в перечисление секторов календарных событий.
    • Объявление функции FREQUENCY преобразует перечисление Event_Frequency в перечисление частоты событий календаря.
    • Объявление функции TYPE преобразует перечисление Event_Type в перечисление типов событий календаря.

    //+------------------------------------------------------------------+
    //|News class                                                        |
    //+------------------------------------------------------------------+
    class CNews : private CCandleProperties
      {
       //Private Declarations Only accessable by this class/header file
    private:
    
       //-- To keep track of what is in our database
       enum CalendarComponents
         {
          AutoDST_Table,//AutoDST Table
          CalendarAU_View,//View for DST_AU
          CalendarNONE_View,//View for DST_NONE
          CalendarUK_View,//View for DST_UK
          CalendarUS_View,//View for DST_US
          EventInfo_View,//View for Event Information
          Currencies_View,//View for Currencies
          Record_Table,// Record Table
          TimeSchedule_Table,//TimeSchedule Table
          MQL5Calendar_Table,//MQL5Calendar Table
          AutoDST_Trigger,//Table Trigger for AutoDST
          Record_Trigger//Table Trigger for Record
         };
    
       //-- structure to retrieve all the objects in the database
       struct SQLiteMaster
         {
          string         type;//will store object's type
          string         name;//will store object's name
          string         tbl_name;//will store table name
          int            rootpage;//will store rootpage
          string         sql;//Will store the sql create statement
         } DBContents[];//Array of type SQLiteMaster
    
       //--  MQL5CalendarContents inherits from SQLiteMaster structure
       struct MQL5CalendarContents:SQLiteMaster
         {
          CalendarComponents  Content;
          string         insert;//Will store the sql insert statement
         } CalendarContents[12],DBMemory;//Array to Store objects in our database
    
       CTimeManagement   CTime;//TimeManagement Object declaration
       CDaylightSavings_UK  Savings_UK;//DaylightSavings Object for the UK and EU
       CDaylightSavings_US  Savings_US;//DaylightSavings Object for the US
       CDaylightSavings_AU  Savings_AU;//DaylightSavings Object for the AU
    
       bool              AutoDetectDST(DST_type &dstType);//Function will determine Broker DST
       DST_type          DSTType;//variable of DST_type enumeration declared in the CommonVariables class/header file
       bool              InsertIntoTables(int db,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table
       void              CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table
       bool              CreateCalendarTable(int db,bool &tableExists);//Function for creating a table in a database
       bool              CreateTimeTable(int db,bool &tableExists);//Function for creating a table in a database
       void              CreateCalendarViews(int db);//Function for creating views in a database
       void              CreateRecordTable(int db);//Creates a table to store the record of when last the Calendar database was updated/created
       string            DropRequest;//Variable for dropping tables in the database
    
       //-- Function for retrieving the MQL5CalendarContents structure for the enumartion type CalendarComponents
       MQL5CalendarContents CalendarStruct(CalendarComponents Content)
         {
          MQL5CalendarContents Calendar;
          for(uint i=0;i<CalendarContents.Size();i++)
            {
             if(CalendarContents[i].Content==Content)
               {
                return CalendarContents[i];
               }
            }
          return Calendar;
         }
    
       //--- To Store Calendar DB Data
       struct CalendarData
         {
          int            EventId;//Event Id
          string         Country;//Event Country
          string         EventName;//Event Name
          string         EventType;//Event Type
          string         EventImportance;//Event Importance
          string         EventCurrency;//Event Currency
          string         EventCode;//Event Code
          string         EventSector;//Event Sector
          string         EventForecast;//Event Forecast Value
          string         EventPreval;//Event Previous Value
          string         EventImpact;//Event Impact
          string         EventFrequency;//Event Frequency
          string         DST_UK;//DST UK
          string         DST_US;//DST US
          string         DST_AU;//DST AU
          string         DST_NONE;//DST NONE
         } DB_Data[],DB_Cal;//Structure variables
    
       //--- Will Retrieve all relevant Calendar data for DB in Memory from DB in Storage
       void              GetCalendar(CalendarData &Data[])
         {
          //--- Open calendar DB in Storage
          int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
          if(db==INVALID_HANDLE)//Checks if the database was able to be opened
            {
             //if opening the database failed
             if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder
               {
                return;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
               }
            }
          //--- Get filtered calendar DB data
          string SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,"
                                           "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,"
                                           "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ "
                                           "Inner Join %s TS on TS.ID=MQ.ID "
                                           "Where %s and %s and %s and %s and %s;",
                                           CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name,
                                           Request_Importance(myImportance),Request_Frequency(myFrequency),
                                           Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency));
          //--- Process Sql request
          int Request = DatabasePrepare(db,SqlRequest);
          if(Request==INVALID_HANDLE)
            {
             //--- Print details if request failed.
             Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
             Print("SQL");
             Print(SqlRequest);
            }
          else
            {
             //--- Clear data from whole array
             ArrayRemove(Data,0,WHOLE_ARRAY);
             //--- create structure variable to get data from request
             CalendarData data;
             //Assigning values from the sql query into Data structure array
             for(int i=0; DatabaseReadBind(Request,data); i++)
               {
                //--- Resize Data Array
                ArrayResize(Data,i+1,i+2);
                Data[i]  = data;
               }
            }
          DatabaseFinalize(Request);//Finalize request
          //--- Close Calendar database
          DatabaseClose(db);
         }
    
       //--- Retrieve the AutoDST enumeration data from calendar DB in storage
       DST_type          GetAutoDST()
         {
          string Sch_Dst;
          //--- open the database 'Calendar' in the common folder
          int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON);
    
          if(db==INVALID_HANDLE)//Checks if 'Calendar' failed to be opened
            {
             if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if 'Calendar' database exists
               {
                Print("Could not find Database!");
                return DST_NONE;//return default value when failed.
               }
            }
    
          //--- Sql query to get AutoDST value
          string request_text="SELECT DST FROM 'AutoDST'";
          //--- Process sql request
          int request=DatabasePrepare(db,request_text);
          if(request==INVALID_HANDLE)
            {
             Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
             DatabaseClose(db);//Close Database
             return DST_NONE;//return default value when failed.
            }
    
          //--- Read Sql request output data
          if(DatabaseRead(request))
            {
             //-- Store the first column data into string variable Sch_Dst
             if(!DatabaseColumnText(request,0,Sch_Dst))
               {
                Print("DatabaseRead() failed with code ", GetLastError());
                DatabaseFinalize(request);//Finalize request
                DatabaseClose(db);//Closes the database 'Calendar'
                return DST_NONE;//return default value when failed.
               }
            }
          DatabaseFinalize(request);//Finalize request
          DatabaseClose(db);//Closes the database 'Calendar'
          return (Sch_Dst=="DST_UK")?DST_UK:(Sch_Dst=="DST_US")?DST_US:
                 (Sch_Dst=="DST_AU")?DST_AU:DST_NONE;//Returns the enumeration value for each corresponding string
         }
    
       //--- Retrieve Sql request string for calendar event Importance
       string            Request_Importance(Calendar_Importance Importance)
         {
          //--- Constant request prefix string
          const string constant="MQ.EventImportance";
          //--- switch statement for Calendar_Importance enumeration
          switch(Importance)
            {
             case Calendar_Importance_All://String Request for all event Importance
                return constant+"<>'"+EnumToString(myImportance)+"'";
                break;
             default://String Request for any event Importance
                return constant+"='"+EnumToString(IMPORTANCE(myImportance))+"'";
                break;
            }
         }
    
       //--- Retrieve Sql request string for calendar event Frequency
       string            Request_Frequency(Event_Frequency Frequency)
         {
          //--- Constant request prefix string
          const string constant="MQ.EventFrequency";
          //--- switch statement for Event_Frequency enumeration
          switch(Frequency)
            {
             case Event_Frequency_ALL://String Request for all event frequencies
                return constant+"<>'"+EnumToString(myFrequency)+"'";
                break;
             default://String Request for any event frequency
                return constant+"='"+EnumToString(FREQUENCY(myFrequency))+"'";
                break;
            }
         }
    
       //--- Retrieve Sql request string for calendar event Sector
       string            Request_Sector(Event_Sector Sector)
         {
          //--- Constant request prefix string
          const string constant="MQ.EventSector";
          //--- switch statement for Event_Sector enumeration
          switch(Sector)
            {
             case Event_Sector_ALL://String Request for all event sectors
                return constant+"<>'"+EnumToString(mySector)+"'";
                break;
             default://String Request for any event sector
                return constant+"='"+EnumToString(SECTOR(mySector))+"'";
                break;
            }
         }
    
       //--- Retrieve Sql request string for calendar event type
       string            Request_Type(Event_Type Type)
         {
          //--- Constant request prefix string
          const string constant="MQ.EventType";
          //--- switch statement for Event_Type enumeration
          switch(Type)
            {
             case Event_Type_All://String Request for all event types
                return constant+"<>'"+EnumToString(myType)+"'";
                break;
             default://String request for any event type
                return constant+"='"+EnumToString(TYPE(myType))+"'";
                break;
            }
         }
    
       //--- Retrieve Sql request string for calendar event Currency
       string            Request_Currency(Event_Currency Currency)
         {
          //--- Constant request prefix string and request suffix
          const string constant_prefix="(MQ.EventCurrency",constant_suffix="')";
          //--- switch statement for Event_Currency enumeration
          switch(Currency)
            {
             case Event_Currency_ALL://String Request for all currencies
                return constant_prefix+"<>'"+EnumToString(myCurrency)+constant_suffix;
                break;
             case Event_Currency_Symbol://String Request for all symbol currencies
                return constant_prefix+"='"+CSymbol.CurrencyBase()+"' or MQ.EventCurrency='"+
                       CSymbol.CurrencyMargin()+"' or MQ.EventCurrency='"+CSymbol.CurrencyProfit()+constant_suffix;
                break;
             case Event_Currency_Margin://String Request for Margin currency
                return constant_prefix+"='"+CSymbol.CurrencyMargin()+constant_suffix;
                break;
             case Event_Currency_Base://String Request for Base currency
                return constant_prefix+"='"+CSymbol.CurrencyBase()+constant_suffix;
                break;
             case Event_Currency_Profit://String Request for Profit currency
                return constant_prefix+"='"+CSymbol.CurrencyProfit()+constant_suffix;
                break;
             case Event_Currency_NZD_NZ://String Request for NZD currency
                return constant_prefix+"='NZD' and MQ.EventCode='NZ"+constant_suffix;
                break;
             case Event_Currency_EUR_EU://String Request for EUR currency and EU code
                return constant_prefix+"='EUR' and MQ.EventCode='EU"+constant_suffix;
                break;
             case Event_Currency_JPY_JP://String Request for JPY currency
                return constant_prefix+"='JPY' and MQ.EventCode='JP"+constant_suffix;
                break;
             case Event_Currency_CAD_CA://String Request for CAD currency
                return constant_prefix+"='CAD' and MQ.EventCode='CA"+constant_suffix;
                break;
             case Event_Currency_AUD_AU://String Request for AUD currency
                return constant_prefix+"='AUD' and MQ.EventCode='AU"+constant_suffix;
                break;
             case Event_Currency_CNY_CN://String Request for CNY currency
                return constant_prefix+"='CNY' and MQ.EventCode='CN"+constant_suffix;
                break;
             case Event_Currency_EUR_IT://String Request for EUR currency and IT code
                return constant_prefix+"='EUR' and MQ.EventCode='IT"+constant_suffix;
                break;
             case Event_Currency_SGD_SG://String Request for SGD currency
                return constant_prefix+"='SGD' and MQ.EventCode='SG"+constant_suffix;
                break;
             case Event_Currency_EUR_DE://String Request for EUR currency and DE code
                return constant_prefix+"='EUR' and MQ.EventCode='DE"+constant_suffix;
                break;
             case Event_Currency_EUR_FR://String Request for EUR currency and FR code
                return constant_prefix+"='EUR' and MQ.EventCode='FR"+constant_suffix;
                break;
             case Event_Currency_BRL_BR://String Request for BRL currency
                return constant_prefix+"='BRL' and MQ.EventCode='BR"+constant_suffix;
                break;
             case Event_Currency_MXN_MX://String Request for MXN currency
                return constant_prefix+"='MXN' and MQ.EventCode='MX"+constant_suffix;
                break;
             case Event_Currency_ZAR_ZA://String Request for ZAR currency
                return constant_prefix+"='ZAR' and MQ.EventCode='ZA"+constant_suffix;
                break;
             case Event_Currency_HKD_HK://String Request for HKD currency
                return constant_prefix+"='HKD' and MQ.EventCode='HK"+constant_suffix;
                break;
             case Event_Currency_INR_IN://String Request for INR currency
                return constant_prefix+"='INR' and MQ.EventCode='IN"+constant_suffix;
                break;
             case Event_Currency_NOK_NO://String Request for NOK currency
                return constant_prefix+"='NOK' and MQ.EventCode='NO"+constant_suffix;
                break;
             case Event_Currency_USD_US://String Request for USD currency
                return constant_prefix+"='USD' and MQ.EventCode='US"+constant_suffix;
                break;
             case Event_Currency_GBP_GB://String Request for GBP currency
                return constant_prefix+"='GBP' and MQ.EventCode='GB"+constant_suffix;
                break;
             case Event_Currency_CHF_CH://String Request for CHF currency
                return constant_prefix+"='CHF' and MQ.EventCode='CH"+constant_suffix;
                break;
             case Event_Currency_KRW_KR://String Request for KRW currency
                return constant_prefix+"='KRW' and MQ.EventCode='KR"+constant_suffix;
                break;
             case Event_Currency_EUR_ES://String Request for EUR currency and ES code
                return constant_prefix+"='EUR' and MQ.EventCode='ES"+constant_suffix;
                break;
             case Event_Currency_SEK_SE://String Request for SEK currency
                return constant_prefix+"='SEK' and MQ.EventCode='SE"+constant_suffix;
                break;
             case Event_Currency_ALL_WW://String Request for ALL currency
                return constant_prefix+"='ALL' and MQ.EventCode='WW"+constant_suffix;
                break;
             default://String Request for no currencies
                return constant_prefix+"='"+constant_suffix;
                break;
            }
         }
    
       //Public declarations accessable via a class's Object
    public:
                         CNews(void);//Constructor
                        ~CNews(void);//Destructor
       void              CreateEconomicDatabase();//Creates the Calendar database for a specific Broker
       datetime          GetLatestNewsDate();//Gets the latest/newest date in the Calendar database
       void              EconomicDetails(Calendar &NewsTime[],datetime date_from=0,datetime date_to=0);//Gets values from the MQL5 economic Calendar
       void              EconomicDetailsMemory(Calendar &NewsTime[],datetime date);//Gets values from the MQL5 DB Calendar in Memory
       void              CreateEconomicDatabaseMemory();//Create calendar database in memory
       void              EconomicNextEvent(datetime date=0);//Will update UpcomingNews structure variable with the next event data
       bool              UpdateRecords();//Checks if the main Calendar database needs an update or not
       ENUM_CALENDAR_EVENT_IMPACT GetImpact();//Will retrieve Upcoming Event Impact data
    
       //--- Convert Importance string into Calendar Event Importance Enumeration
       ENUM_CALENDAR_EVENT_IMPORTANCE IMPORTANCE(string Importance)
         {
          //--- Calendar Importance is High
          if(Importance==EnumToString(CALENDAR_IMPORTANCE_HIGH))
            {
             return CALENDAR_IMPORTANCE_HIGH;
            }
          else
             //--- Calendar Importance is Moderate
             if(Importance==EnumToString(CALENDAR_IMPORTANCE_MODERATE))
               {
                return CALENDAR_IMPORTANCE_MODERATE;
               }
             else
                //--- Calendar Importance is Low
                if(Importance==EnumToString(CALENDAR_IMPORTANCE_LOW))
                  {
                   return CALENDAR_IMPORTANCE_LOW;
                  }
                else
                   //--- Calendar Importance is None
                  {
                   return CALENDAR_IMPORTANCE_NONE;
                  }
         }
    
       //--- Convert Calendar_Importance Enumeration into Calendar Event Importance Enumeration
       ENUM_CALENDAR_EVENT_IMPORTANCE IMPORTANCE(Calendar_Importance Importance)
         {
          //--- switch statement for Calendar_Importance enumeration
          switch(Importance)
            {
             case Calendar_Importance_None://None
                return CALENDAR_IMPORTANCE_NONE;
                break;
             case Calendar_Importance_Low://Low
                return CALENDAR_IMPORTANCE_LOW;
                break;
             case Calendar_Importance_Moderate://Moderate
                return CALENDAR_IMPORTANCE_MODERATE;
                break;
             case Calendar_Importance_High://High
                return CALENDAR_IMPORTANCE_HIGH;
                break;
             default://None
                return CALENDAR_IMPORTANCE_NONE;
                break;
            }
         }
    
       //--- Convert Calendar Event Importance Enumeration into string Importance Rating
       string            GetImportance(ENUM_CALENDAR_EVENT_IMPORTANCE Importance)
         {
          //--- switch statement for ENUM_CALENDAR_EVENT_IMPORTANCE enumeration
          switch(Importance)
            {
             case  CALENDAR_IMPORTANCE_HIGH://High
                return "HIGH";
                break;
             case CALENDAR_IMPORTANCE_MODERATE://Moderate
                return "MODERATE";
                break;
             case CALENDAR_IMPORTANCE_LOW://Low
                return "LOW";
                break;
             default://None
                return "NONE";
                break;
            }
         }
    
       //--- Retrieve color for each Calendar Event Importance Enumeration
       color             GetImportance_color(ENUM_CALENDAR_EVENT_IMPORTANCE Importance)
         {
          //--- switch statement for ENUM_CALENDAR_EVENT_IMPORTANCE enumeration
          switch(Importance)
            {
             case CALENDAR_IMPORTANCE_HIGH://High
                return clrRed;
                break;
             case CALENDAR_IMPORTANCE_MODERATE://Moderate
                return clrOrange;
                break;
             case CALENDAR_IMPORTANCE_LOW://Low
                return (isLightMode)?clrBlue:clrLightBlue;
                break;
             default://None
                return (isLightMode)?clrBlack:clrWheat;
                break;
            }
         }
    
       //--- Convert Event_Sector Enumeration into Calendar Event Sector Enumeration
       ENUM_CALENDAR_EVENT_SECTOR SECTOR(Event_Sector Sector)
         {
          //--- switch statement for Event_Sector enumeration
          switch(Sector)
            {
             case Event_Sector_None://NONE
                return CALENDAR_SECTOR_NONE;
                break;
             case Event_Sector_Market://MARKET
                return CALENDAR_SECTOR_MARKET;
                break;
             case Event_Sector_Gdp://GDP
                return CALENDAR_SECTOR_GDP;
                break;
             case Event_Sector_Jobs://JOBS
                return CALENDAR_SECTOR_JOBS;
                break;
             case Event_Sector_Prices://PRICES
                return CALENDAR_SECTOR_PRICES;
                break;
             case Event_Sector_Money://MONEY
                return CALENDAR_SECTOR_MONEY;
                break;
             case Event_Sector_Trade://TRADE
                return CALENDAR_SECTOR_TRADE;
                break;
             case Event_Sector_Government://GOVERNMENT
                return CALENDAR_SECTOR_GOVERNMENT;
                break;
             case Event_Sector_Business://BUSINESS
                return CALENDAR_SECTOR_BUSINESS;
                break;
             case Event_Sector_Consumer://CONSUMER
                return CALENDAR_SECTOR_CONSUMER;
                break;
             case Event_Sector_Housing://HOUSING
                return CALENDAR_SECTOR_HOUSING;
                break;
             case Event_Sector_Taxes://TAXES
                return CALENDAR_SECTOR_TAXES;
                break;
             case Event_Sector_Holidays://HOLIDAYS
                return CALENDAR_SECTOR_HOLIDAYS;
                break;
             default://Unknown
                return CALENDAR_SECTOR_NONE;
                break;
            }
         }
    
       //--- Convert Event_Frequency Enumeration into Calendar Event Frequency Enumeration
       ENUM_CALENDAR_EVENT_FREQUENCY FREQUENCY(Event_Frequency Frequency)
         {
          //--- switch statement for Event_Frequency enumeration
          switch(Frequency)
            {
             case  Event_Frequency_None://NONE
                return CALENDAR_FREQUENCY_NONE;
                break;
             case Event_Frequency_Day://DAY
                return CALENDAR_FREQUENCY_DAY;
                break;
             case Event_Frequency_Week://WEEK
                return CALENDAR_FREQUENCY_WEEK;
                break;
             case Event_Frequency_Month://MONTH
                return CALENDAR_FREQUENCY_MONTH;
                break;
             case Event_Frequency_Quarter://QUARTER
                return CALENDAR_FREQUENCY_QUARTER;
                break;
             case Event_Frequency_Year://YEAR
                return CALENDAR_FREQUENCY_YEAR;
                break;
             default://Unknown
                return CALENDAR_FREQUENCY_NONE;
                break;
            }
         }
    
       //--- Convert Event_Type Enumeration into Calendar Event Type Enumeration
       ENUM_CALENDAR_EVENT_TYPE TYPE(Event_Type Type)
         {
          //--- switch statement for Event_Type enumeration
          switch(Type)
            {
             case Event_Type_Event://EVENT
                return CALENDAR_TYPE_EVENT;
                break;
             case Event_Type_Indicator://INDICATOR
                return CALENDAR_TYPE_INDICATOR;
                break;
             case Event_Type_Holiday://HOLIDAY
                return CALENDAR_TYPE_HOLIDAY;
                break;
             default://Unknown
                return CALENDAR_TYPE_EVENT;
                break;
            }
         }
      };

    Сначала мы рассмотрим оператор SQL в функции GetCalendar, который запросит все данные из базы данных календаря в общей папке для нашей базы данных календаря в памяти. В этом запросе мы выбираем все столбцы в таблицах MQL5Calendar и TimeSchedule и объединяем таблицы, у которых идентификаторы совпадают. Затем мы сортируем данные на основе перечислений, выбранных трейдером во входных параметрах советника для настроек новостей.

    //--- Get filtered calendar DB data
          string SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,"
                                           "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,"
                                           "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ "
                                           "Inner Join %s TS on TS.ID=MQ.ID "
                                           "Where %s and %s and %s and %s and %s;",
                                           CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name,
                                           Request_Importance(myImportance),Request_Frequency(myFrequency),
                                           Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency));

    Мы рассмотрим SQL-запрос для следующей конфигурации настроек новостей по символу EURUSD.

    Конфигурация настроек новостей 1

    Как показано ниже, когда мы выбираем всё для важности календаря, мы намеренно не преобразуем нашу переменную myImportance перечисления Calendar_Importance, поскольку нет ни одного уровня важности событий со значением Calendar_Importance_All. Таким образом мы можем легко выбрать все события, у которых важность события не равна Calendar_Importance_All. То же самое можно сказать и обо всех входных параметрах настроек новостей, для которых выбрано значение ALL (все).

    case Calendar_Importance_All://String Request for all event Importance
                return constant+"<>'"+EnumToString(myImportance)+"'";
    Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,MQ.EventCode,MQ.EventSector,
    MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from MQL5Calendar MQ
    Inner Join TimeSchedule TS on TS.ID=MQ.ID Where MQ.EventImportance<>'Calendar_Importance_All' and MQ.EventFrequency<>'Event_Frequency_ALL'
    and MQ.EventSector<>'Event_Sector_ALL' and MQ.EventType<>'Event_Type_All' and (MQ.EventCurrency='EUR' or MQ.EventCurrency='EUR' or 
    MQ.EventCurrency='USD');

    Рассмотрим еще раз один SQL-запрос из функции GetCalendar для следующей конфигурации настроек новостей по символу EURUSD.

    Конфигурация настроек новостей 2

    Как показано ниже, когда мы выбираем любой другой параметр, отличный от "все" для важности календаря, мы преобразуем нашу переменную myImportance перечисления Calendar_Importance в тип перечисления ENUM_CALENDAR_EVENT_IMPORTANCE, чтобы строка могла совпадать со строкой, хранящейся в таблице MQL5Calendar, для корректного получения важности события определенного типа.

    default://String Request for any event Importance
                return constant+"='"+EnumToString(IMPORTANCE(myImportance))+"'";

    Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,MQ.EventCode,MQ.EventSector,MQ.EventForecast,
    MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from MQL5Calendar MQ Inner Join TimeSchedule TS 
    on TS.ID=MQ.ID Where MQ.EventImportance='CALENDAR_IMPORTANCE_HIGH' and MQ.EventFrequency='CALENDAR_FREQUENCY_MONTH' and 
    MQ.EventSector='CALENDAR_SECTOR_JOBS' and MQ.EventType='CALENDAR_TYPE_INDICATOR' and (MQ.EventCurrency<>'Event_Currency_ALL');

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

    //--- initializing properties for the EventInfo view
       CalendarContents[5].Content = EventInfo_View;
       CalendarContents[5].name = "Event Info";
       CalendarContents[5].sql = "CREATE VIEW IF NOT EXISTS 'Event Info' "
                                 "AS SELECT EVENTID as 'ID',COUNTRY as 'Country',EVENTNAME as 'Name',"
                                 "REPLACE(EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector',"
                                 "REPLACE(EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',EVENTCURRENCY as 'Currency' "
                                 "FROM MQL5Calendar GROUP BY \"Name\" ORDER BY \"Country\" Asc,"
                                 "CASE \"Importance\" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,\"Sector\" Desc;";
       CalendarContents[5].tbl_name = "Event Info";
       CalendarContents[5].type = "view";

    Давайте рассмотрим оператор SQL для создания представления информации о событии (Event Info). Во-первых, мы создаем представление только в том случае, если оно еще не существует, затем выбираем столбец EVENTID и переименовываем его в ID, COUNTRY переименовываем в Country, EVENTNAME переименовываем в Name, в EVENTTYPE заменяем текст CALENDAR_TYPE_ пустой строкой и переименовываем столбец в Type, в EVENTSECTOR заменяем текст CALENDAR_SECTOR_ пустой строкой и переименовываем столбец в Sector, в EVENTIMPORTANCE заменяем текст CALENDAR_IMPORTANCE_ пустой строкой и переименовываем столбец в Importance, EVENTCURRENCY переименовываем в Currency из таблицы MQL5Calendar. Затем мы группируем запрос по EVENTNAME, который теперь называется Name, чтобы события с одинаковым именем не отображались в представлении несколько раз. Затем мы задаем запросу последовательность: сначала мы сортируем результат по значению Country (страна) в порядке возрастания, так что страна с начальной буквой А, например, Австралия, будет показана первой, затем мы сортируем результат по значению EVENTIMPORTANCE, которое теперь называется Importance для отображения событий с самой высокой важностью. Для этого нам нужно задать строковым значениям важности ранжирование. В этом случае, значение HIGH (высокая) имеет наивысший приоритет, значение MODERATE (средний) - средний приоритет, значение LOW (низкий) - низкий приоритет, и, наконец, любое другое значение имеет последний приоритет. Далее мы сортируем результаты запроса по EVENTSECTOR, который теперь называется Sector, в порядке убывания. Пример представления будет показан ниже.

    CREATE VIEW IF NOT EXISTS 'Event Info' AS SELECT MQ.EVENTID as 'ID',MQ.COUNTRY as 'Country',MQ.EVENTNAME as 'Name',REPLACE(MQ.EVENTTYPE,
    'CALENDAR_TYPE_','') as 'Type',REPLACE(MQ.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector',REPLACE(MQ.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') 
    as 'Importance',MQ.EVENTCURRENCY as 'Currency' FROM MQL5Calendar MQ INNER JOIN TimeSchedule TS on TS.ID=MQ.ID GROUP BY "Name" ORDER BY 
    "Country" Asc,CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,"Sector" Desc;

    Просмотр информации о событии

    ID       Country  Name                                          Type            Sector        Importance   Currency
    36030006 Australia  RBA Governor Lowe Speech                    EVENT           MONEY         HIGH         AUD 
    36030008 Australia  RBA Interest Rate Decision                  INDICATOR       MONEY         HIGH         AUD 
    36010029 Australia  PPI q/q                                     INDICATOR       PRICES        MODERATE     AUD 
    36030014 Australia  RBA Trimmed Mean CPI q/q                    INDICATOR       PRICES        MODERATE     AUD 
    36030009 Australia  RBA Weighted Median CPI q/q                 INDICATOR       PRICES        MODERATE     AUD 
    36010031 Australia  Wage Price Index q/q                        INDICATOR       PRICES        MODERATE     AUD 
    36030026 Australia  RBA Assistant Governor Boulton Speech       EVENT           MONEY         MODERATE     AUD 
    36030024 Australia  RBA Assistant Governor Bullock Speech       EVENT           MONEY         MODERATE     AUD 
    36030025 Australia  RBA Assistant Governor Ellis Speech         EVENT           MONEY         MODERATE     AUD 
    
    62 lines later...
    
    76020002 Brazil  BCB Interest Rate Decision                     INDICATOR       MONEY         HIGH         BRL 
    76020004 Brazil  BCB Inflation Report                           EVENT           PRICES        MODERATE     BRL 
    76050001 Brazil  FIPE CPI m/m                                   INDICATOR       PRICES        MODERATE     BRL 
    76010005 Brazil  Mid-Month CPI m/m                              INDICATOR       PRICES        MODERATE     BRL 
    76020001 Brazil  BCB Focus Market Report                        EVENT           MONEY         MODERATE     BRL 
    76020003 Brazil  BCB MPC (Copom) Minutes                        EVENT           MONEY         MODERATE     BRL 
    76020005 Brazil  BCB National Monetary Council Meeting          EVENT           MONEY         MODERATE     BRL 
    76010009 Brazil  Unemployment Rate 3-months                     INDICATOR       JOBS          MODERATE     BRL 
    76020010 Brazil  Nominal Budget Balance                         INDICATOR       GOVERNMENT    MODERATE     BRL 
    76020011 Brazil  Primary Budget balance                         INDICATOR       GOVERNMENT    MODERATE     BRL 
    76010014 Brazil  Services Volume m/m                            INDICATOR       BUSINESS      MODERATE     BRL 
    
    98 lines later...
    
    124040017 Canada  BoC Governor Macklem Speech                   EVENT           MONEY          HIGH         CAD 
    124040003 Canada  BoC Governor Poloz Speech                     EVENT           MONEY          HIGH         CAD 
    124040006 Canada  BoC Interest Rate Decision                    INDICATOR       MONEY          HIGH         CAD 
    124040009 Canada  BoC Monetary Policy Report Press Conference   EVENT           MONEY          HIGH         CAD 
    124010011 Canada  Employment Change                             INDICATOR       JOBS           HIGH         CAD 
    124010021 Canada  GDP m/m                                       INDICATOR       GDP            HIGH         CAD 
    124010008 Canada  Core Retail Sales m/m                         INDICATOR       CONSUMER       HIGH         CAD 
    124020001 Canada  Ivey PMI                                      INDICATOR       BUSINESS       HIGH         CAD 
    124010024 Canada  IPPI m/m                                      INDICATOR       PRICES         MODERATE     CAD 
    124010026 Canada  RMPI m/m                                      INDICATOR       PRICES         MODERATE     CAD 
    124040001 Canada  BoC Business Outlook Survey                   EVENT           MONEY          MODERATE     CAD 
    

    Для нашего представления валют мы выбираем уникальные значения EventCurrency и EventCode из таблицы MQL5Calendar.

    //--- initializing properties for the Currencies view
       CalendarContents[6].Content = Currencies_View;
       CalendarContents[6].name = "Currencies";
       CalendarContents[6].sql = "CREATE VIEW IF NOT EXISTS Currencies AS "
                                 "SELECT Distinct EventCurrency as 'Currency',EventCode as 'Code' FROM 'MQL5Calendar';";
       CalendarContents[6].tbl_name = "Currencies";
       CalendarContents[6].type = "view";
    SELECT * FROM 'Currencies';
    Currency  	Code
    NZD  		NZ
    EUR 		EU
    JPY  		JP
    CAD  		CA
    AUD  		AU
    CNY  		CN
    EUR  		IT
    SGD  		SG
    EUR  		DE
    EUR  		FR
    BRL  		BR
    MXN  		MX
    ZAR  		ZA
    HKD  		HK
    INR  		IN
    NOK  		NO
    USD  		US
    GBP  		GB
    CHF  		CH
    KRW  		KR
    EUR  		ES
    SEK  		SE
    ALL  		WW

    Теперь инициализируем свойства нашей таблицы MQL5Calendar, которая будет находиться в нашей базе данных в памяти. Эта таблица будет представлять собой одну большую таблицу, которая по сути является комбинацией таблиц в нашей базе данных в хранилище. Это таблицы MQL5Calendar и TimeSchedule.

    //-- initializing properties for the MQL5Calendar table for DB in System Memory
       DBMemory.Content = MQL5Calendar_Table;
       DBMemory.name = "MQL5Calendar";
       DBMemory.sql = "CREATE TABLE IF NOT EXISTS MQL5Calendar(EVENTID  INT   NOT NULL,COUNTRY  TEXT   NOT NULL,"
                      "EVENTNAME   TEXT   NOT NULL,EVENTTYPE   TEXT   NOT NULL,EVENTIMPORTANCE   TEXT   NOT NULL,"
                      "EVENTCURRENCY  TEXT   NOT NULL,EVENTCODE   TEXT   NOT NULL,EVENTSECTOR TEXT   NOT NULL,"
                      "EVENTFORECAST  TEXT   NOT NULL,EVENTPREVALUE  TEXT   NOT NULL,EVENTIMPACT TEXT   NOT NULL,"
                      "EVENTFREQUENCY TEXT   NOT NULL,DST_UK   TEXT   NOT NULL,DST_US   TEXT   NOT NULL,"
                      "DST_AU   TEXT   NOT NULL,DST_NONE   TEXT   NOT NULL)STRICT;";
       DBMemory.tbl_name="MQL5Calendar";
       DBMemory.type = "table";
       DBMemory.insert = "INSERT INTO 'MQL5Calendar'(EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTCURRENCY,EVENTCODE,"
                         "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY,DST_UK,DST_US,DST_AU,DST_NONE) "
                         "VALUES (%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s', '%s', '%s');";

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

    //+------------------------------------------------------------------+
    //|Destructor                                                        |
    //+------------------------------------------------------------------+
    CNews::~CNews(void)
      {
       if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the news database open text file exists
         {
          FileDelete(NEWS_TEXT_FILE,FILE_COMMON);
         }
       DatabaseClose(DBMemoryConnection);//Close DB in memory
      }

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

    1. Проверяем, существует ли база данных календаря в общей папке. Если базы данных не существует, мы выполняем обновление.
    2. Проверяем, существуют ли все объекты в базе данных и соответствуют ли их SQL-операторы нашим ожиданиям. Если нет, выполняем обновление.
    3. Проверяем, совпадает ли дата в таблице Records с текущей датой. Если нет, обновляем.

    В этой статье мы добавим еще один шаг в последовательность. Цель этого дополнительного шага — проверить точность новостных данных в представлении Calendar_NONE. Я заметил, что иногда новостные данные из календаря MQL5 могут со временем меняться, и если мы сохранили новости, которые не изменились, нам нужно иметь возможность проверить наличие несоответствий между тем, что мы сохранили, и тем, что было обновлено из календаря MQL5, если таковые имеются.

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

          Calendar TodaysNews[];
          datetime Today = CTime.Time(TimeTradeServer(),0,0,0);
          EconomicDetails(TodaysNews,Today,Today+CTime.DaysS());
    
          for(uint i=0;i<TodaysNews.Size();i++)
            {
             request_text=StringFormat("SELECT ID FROM %s where Replace(Date,'.','-')=Replace('%s','.','-') and ID=%d;",
                                       CalendarStruct(CalendarNONE_View).name,TodaysNews[i].EventDate,TodaysNews[i].EventId);
             request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
             if(request==INVALID_HANDLE)//Checks if the request failed to be completed
               {
                Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
                DatabaseFinalize(request);
                DatabaseClose(db);
                return perform_update;
               }
             //PrintFormat(request_text);
             if(!DatabaseRead(request))//Will be true if there are results from the sql query/request
               {
                DatabaseFinalize(request);
                DatabaseClose(db);
                return perform_update;
               }
             DatabaseFinalize(request);
            }
          DatabaseClose(db);//Closes the database
          perform_update=false;
          return perform_update;

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

    //+------------------------------------------------------------------+
    //|Create calendar database in memory                                |
    //+------------------------------------------------------------------+
    void CNews::CreateEconomicDatabaseMemory()
      {
    //--- Open/create the database in memory
       DBMemoryConnection=DatabaseOpen(NEWS_DATABASE_MEMORY,DATABASE_OPEN_MEMORY);
       if(DBMemoryConnection==INVALID_HANDLE)//Checks if the database failed to open/create
         {
          Print("DB: ",NEWS_DATABASE_MEMORY, " open failed with code ", GetLastError());
          return;//will terminate execution of the rest of the code below
         }
    //--- Drop the table if it already exists
       DatabaseExecute(DBMemoryConnection,StringFormat("Drop table IF EXISTS %s",DBMemory.name));
    //--- Attempt to create the table
       if(!DatabaseExecute(DBMemoryConnection,DBMemory.sql))
         {
          Print("DB: create the Calendar table failed with code ", GetLastError());
          return;
         }
    //--- Check if the table exists
       if(DatabaseTableExists(DBMemoryConnection,DBMemory.tbl_name))
         {
          //--- Get all news data and time from the database in storage
          GetCalendar(DB_Data);
          //--- Insert all the news data and times into the table
          for(uint i=0;i<DB_Data.Size();i++)
            {
             string request_text=StringFormat(DBMemory.insert,DB_Data[i].EventId,DB_Data[i].Country,
                                              DB_Data[i].EventName,DB_Data[i].EventType,DB_Data[i].EventImportance,
                                              DB_Data[i].EventCurrency,DB_Data[i].EventCode,DB_Data[i].EventSector,
                                              DB_Data[i].EventForecast,DB_Data[i].EventPreval,DB_Data[i].EventImpact,
                                              DB_Data[i].EventFrequency,DB_Data[i].DST_UK,DB_Data[i].DST_US,
                                              DB_Data[i].DST_AU,DB_Data[i].DST_NONE);
    
             if(!DatabaseExecute(DBMemoryConnection, request_text))//Will attempt to run this sql request/query
               {
                //--- failed to run sql request/query
                Print(GetLastError());
                PrintFormat(request_text);
                return;
               }
            }
         }
    //--- Remove all data from the array
       ArrayRemove(DB_Data,0,WHOLE_ARRAY);
    //--- Assign the DST schedule
       MySchedule = (MQLInfoInteger(MQL_TESTER))?(MyDST==AutoDst_Selection)?GetAutoDST():MySchedule:DST_NONE;
      }

    Функция EconomicDetailsMemory извлекает все новостные события, которые происходят в определенную дату, а затем сохраняет новостные данные в массиве NewsTime, который передается по ссылке.

    //+------------------------------------------------------------------+
    //|Gets values from the MQL5 DB Calendar in Memory                   |
    //+------------------------------------------------------------------+
    void CNews::EconomicDetailsMemory(Calendar &NewsTime[],datetime date)
      {
    //--- SQL query to retrieve news data for a certain date
       string request_text=StringFormat("WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',EventType as 'Type'"
                                        ",EventImportance as 'Importance',%s as 'CTime',EventCurrency as 'Currency',EventCode as 'Code',"
                                        "EventSector as 'Sector',EventForecast as 'Forecast',EventPrevalue as 'Prevalue',EventImpact as'Impact',"
                                        "EventFrequency as 'Freq',RANK() OVER (PARTITION BY %s Order BY CASE EventPrevalue WHEN 'None' "
                                        "THEN 2 ELSE 1 END,CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END,CASE EventImportance WHEN "
                                        "'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 WHEN 'CALENDAR_IMPORTANCE_LOW'"
                                        " THEN 3 ELSE 4 END) Ranking FROM %s) SELECT Id,Country,Name,Type,Importance,CTime,Currency,Code,Sector,"
                                        "Forecast,Prevalue,Impact,Freq FROM MySubQuery where Date(Replace(CTime,'.','-'))=Date(Replace('%s','.','-')) and "
                                        "Ranking<2 Group by CTime;",EnumToString(MySchedule),EnumToString(MySchedule),DBMemory.name,
                                        TimeToString(date));
       int request=DatabasePrepare(DBMemoryConnection,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
       if(request==INVALID_HANDLE)//Checks if the request failed to be completed
         {
          Print("DB: ",NEWS_DATABASE_MEMORY, " request failed with code ", GetLastError());
          PrintFormat(request_text);
         }
    //--- Calendar structure variable
       Calendar ReadDB_Data;
    //--- Remove any data in the array
       ArrayRemove(NewsTime,0,WHOLE_ARRAY);
       for(int i=0; DatabaseReadBind(request,ReadDB_Data); i++)//Will read all the results from the sql query/request
         {
          //--- Resize array NewsTime
          ArrayResize(NewsTime,i+1,i+2);
          //--- Assign calendar structure values into NewsTime array index
          NewsTime[i]  = ReadDB_Data;
         }
    //--- Removes a request created in DatabasePrepare()
       DatabaseFinalize(request);
      }

    Давайте разберем SQL-запрос, так как он сложнее любого из наших предыдущих запросов. В этом запросе мы используем выражение WITH и функцию RANK().

    Что такое выражение WITH и как оно работает?

    Выражение WITH, также известное как Common Table Expression (CTE, общее табличное выражение), используется для определения временных наборов результатов, на которые можно ссылаться в операторах SELECT, INSERT, UPDATE или DELETE. Выражение WITH упрощает понимание и поддержку сложных запросов, разбивая их на более простые части. Оно также может помочь повысить производительность за счет повторного использования результатов ресурсоемких подзапросов. CTE являются временными и существуют только на протяжении запроса. Они не создают никаких постоянных объектов в базе данных.

    Что такое функция RANK() и как она работает?

    Функция RANK() используется для присвоения уникального ранга каждой строке в наборе результатов на основе значений в одном или нескольких столбцах. Строки упорядочиваются на основе указанных критериев, и ранг присваивается соответствующим образом. Функция RANK() является частью оконных функций в SQL, что означает, что она работает с окном (или подмножеством) строк и может возвращать несколько строк для каждой строки во входном наборе. Функция RANK() назначает ранг каждой строке на основе порядка, указанного в выражении ORDER BY в определении оконной функции. Если несколько строк имеют одинаковые значения в столбцах, указанных для упорядочивания, им присваивается одинаковый ранг, а следующий ранг пропускается на количество идентичных рангов. PARTITION BY (опционально) разбивает набор результатов на разделы, а функция RANK() применяется к каждому разделу по отдельности.

    В выражении WITH, выделенном ниже, мы выбираем (SELECT) EventId, Country, EventName, EventType, EventImportance, DST_NONE (расписание перехода на летнее время, установленное пользователем во входных данных советника), EventCurrency, EventCode, EventSector, EventForecast, EventPrevalue, EventImpact, EventFrequency, затем мы используем функцию ранжирования. Цель этой функции — присвоить ранг каждому результату SELECT. Этот ранг всегда будет равен 1 для уникального времени события (DST_NONE). Если у нас есть несколько событий с одинаковым временем, то мы применяем ранжирование. Таким образом, если EventPrevalue события равно None, оно получает ранг 2, в противном случае оно получает рейтинг 1 и так далее для остальной части выражения ORDER BY.

    WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',EventType as 'Type',EventImportance as 'Importance',
    DST_NONE as 'CTime',EventCurrency as 'Currency',EventCode as 'Code',EventSector as 'Sector',EventForecast as 'Forecast',
    EventPrevalue as 'Prevalue',EventImpact as'Impact',EventFrequency as 'Freq',RANK() OVER (PARTITION BY DST_NONE 
    Order BY CASE EventPrevalue WHEN 'None' THEN 2 ELSE 1 END,CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END,
    CASE EventImportance WHEN 'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 
    WHEN 'CALENDAR_IMPORTANCE_LOW' THEN 3 ELSE 4 END) Ranking FROM MQL5Calendar) SELECT Id,Country,Name,Type,
    Importance,CTime,Currency,Code,Sector,Forecast,Prevalue,Impact,Freq FROM MySubQuery where 
    Date(Replace(CTime,'.','-'))=Date(Replace('2024.07.30 00:00','.','-')) and Ranking<2 Group by CTime;

    Ниже приведен пример результатов CTE(MySubQuery). Я выделил события, которые будут исключены из результатов, поскольку их ранг превышает 1. Мы рассматриваем только события ранга ниже 2, а затем группируем результаты по времени, поскольку при нанесении объектов событий на график мы не хотим создавать несколько объектов событий с одинаковым временем. Мы хотим, чтобы отображалось только событие с наибольшей важностью.

    Id 		Country 	Name 					Type 			Importance 			CTime 		Currency Code Sector 			Forecast  Prevalue   Impact 		Freq 			  Ranking
    392030007 	Japan 		Unemployment Rate 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 01:30 JPY 	 JP   CALENDAR_SECTOR_JOBS 	2500000   2600000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    392050002 	Japan 		Jobs to Applicants Ratio 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 01:30 JPY 	 JP   CALENDAR_SECTOR_JOBS 	1240000   1240000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    36010001 	Australia 	Building Approvals m/m 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 03:30 AUD 	 AU   CALENDAR_SECTOR_BUSINESS 	-900000   5500000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    36010002 	Australia 	Private House Approvals m/m 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 03:30 AUD 	 AU   CALENDAR_SECTOR_BUSINESS 	None 	  2100000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    250010005 	France 		GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 07:30 EUR 	 FR   CALENDAR_SECTOR_GDP 	200000 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    250010006 	France 		GDP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 07:30 EUR 	 FR   CALENDAR_SECTOR_GDP 	1000000	  1100000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2
    250010008 	France 		Consumer Spending m/m 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 07:30 EUR 	 FR   CALENDAR_SECTOR_CONSUMER 	800000 	  1500000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    756050001 	Switzerland 	KOF Economic Barometer 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 09:00 CHF 	 CH   CALENDAR_SECTOR_BUSINESS 	101500000 102700000  CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    724010001 	Spain 		CPI m/m 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 09:00 EUR 	 ES   CALENDAR_SECTOR_PRICES 	200000 	  400000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    724010003 	Spain 		HICP m/m 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 09:00 EUR 	 ES   CALENDAR_SECTOR_PRICES 	300000 	  400000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    724010005 	Spain 		GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 09:00 EUR 	 ES   CALENDAR_SECTOR_GDP 	300000 	  800000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    724010002 	Spain 		CPI y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 EUR  	 ES   CALENDAR_SECTOR_PRICES 	3000000   3400000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    724010004 	Spain 		HICP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 EUR 	 ES   CALENDAR_SECTOR_PRICES 	3500000   3600000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    724010006 	Spain 		GDP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 EUR 	 ES   CALENDAR_SECTOR_GDP 	1900000   2500000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 5
    752020001 	Sweden 		Business Confidence 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 SEK 	 SE   CALENDAR_SECTOR_BUSINESS 	95500000  97300000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    752020002 	Sweden 		Manufacturing Confidence 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 SEK 	 SE   CALENDAR_SECTOR_BUSINESS 	99000000  99200000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    752020003 	Sweden 		Consumer Confidence 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 SEK 	 SE   CALENDAR_SECTOR_CONSUMER 	95300000  93300000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    752020004 	Sweden 		Inflation Expectations 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 SEK 	 SE   CALENDAR_SECTOR_CONSUMER 	6300000   6200000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    752020005 	Sweden 		Economic Tendency Indicator 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 09:00 SEK 	 SE   CALENDAR_SECTOR_BUSINESS 	94800000  96300000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   5
    276010008 	Germany 	GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 10:00 EUR 	 DE   CALENDAR_SECTOR_GDP 	100000 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    380010020 	Italy 		GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 10:00 EUR 	 IT   CALENDAR_SECTOR_GDP 	200000 	  300000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2
    276010009 	Germany 	GDP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 10:00 EUR 	 DE   CALENDAR_SECTOR_GDP 	-400000   -900000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2
    380010021 	Italy 		GDP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 10:00 EUR 	 IT   CALENDAR_SECTOR_GDP 	400000 	  700000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 4
    999030016 	European Union 	GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_GDP 	200000 	  300000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    999030017 	European Union 	GDP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_GDP 	400000 	  400000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2
    999040003 	European Union 	Industrial Confidence Indicator 	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_BUSINESS 	-9700000  -10100000  CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   3
    999040004 	European Union 	Services Sentiment Indicator 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_BUSINESS 	8300000   6500000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   3
    999040005 	European Union 	Economic Sentiment Indicator 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_BUSINESS 	95300000  95900000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   3
    999040006 	European Union 	Consumer Confidence Index 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_CONSUMER 	-13000000 -13000000  CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   3
    999040007 	European Union 	Consumer Price Expectations 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:00 EUR  	 EU   CALENDAR_SECTOR_CONSUMER 	14400000  13100000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   3
    999040008 	European Union 	Industry Selling Price Expectations 	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_BUSINESS 	6300000   6100000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   3
    380020007 	Italy 		10-Year BTP Auction 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 11:30 EUR 	 IT   CALENDAR_SECTOR_MARKET 	None 	  4010000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE    1
    380020005 	Italy 		5-Year BTP Auction 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:30 EUR 	 IT   CALENDAR_SECTOR_MARKET 	None 	  3550000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE    2
    826130002 	United Kingdom 	10-Year Treasury Gilt Auction 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 11:30 GBP 	 GB   CALENDAR_SECTOR_MARKET 	None 	  4371000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE    2
    724080001 	Spain 		Business Confidence 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 12:00 EUR 	 ES   CALENDAR_SECTOR_BUSINESS 	-4100000  -5700000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    76030002 	Brazil 		FGV IGP-M Inflation Index m/m 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 13:00 BRL 	 BR   CALENDAR_SECTOR_PRICES 	1000000   810000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    484020016 	Mexico 		GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 14:00 MXN 	 MX   CALENDAR_SECTOR_GDP 	0 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    276010020 	Germany 	CPI m/m 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 14:00 EUR 	 DE   CALENDAR_SECTOR_PRICES 	0 	  100000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    276010022 	Germany 	HICP m/m 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 14:00 EUR 	 DE   CALENDAR_SECTOR_PRICES 	-100000   200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    76010012 	Brazil 		PPI m/m 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 14:00 BRL 	 BR   CALENDAR_SECTOR_PRICES 	700000 	  450000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    484020017 	Mexico 		GDP n.s.a. y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 14:00 MXN 	 MX   CALENDAR_SECTOR_GDP 	2000000   1600000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2
    276010021 	Germany 	CPI y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 14:00 EUR 	 DE   CALENDAR_SECTOR_PRICES 	2200000   2200000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   6
    276010023 	Germany 	HICP y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 14:00 EUR 	 DE   CALENDAR_SECTOR_PRICES 	2500000   2500000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   6
    76010013 	Brazil 		PPI y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 14:00 BRL 	 BR   CALENDAR_SECTOR_PRICES 	2200000   170000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   6
    840170001 	United States 	S&P/CS HPI Composite-20 y/y 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	6800000   7200000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    840110001 	United States 	HPI m/m 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	300000 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    840110002 	United States 	HPI y/y 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	5700000   6300000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    840110003 	United States 	HPI 					CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	427700000 424300000  CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    840170002 	United States 	S&P/CS HPI Composite-20 n.s.a. m/m 	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	700000 	  1400000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    840170003 	United States 	S&P/CS HPI Composite-20 s.a. m/m 	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	200000 	  400000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   2
    840030021 	United States 	JOLTS Job Openings 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 16:00 USD 	 US   CALENDAR_SECTOR_JOBS 	7979000   8140000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    840180002 	United States 	CB Consumer Confidence Index 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 16:00 USD 	 US   CALENDAR_SECTOR_CONSUMER 	108000000 100400000  CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    840060003 	United States 	Dallas Fed Services Revenues 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 16:30 USD 	 US   CALENDAR_SECTOR_BUSINESS 	3900000   1900000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    840060004 	United States 	Dallas Fed Services Business Activity 	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 16:30 USD 	 US   CALENDAR_SECTOR_BUSINESS 	-6700000  -4100000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    484010001 	Mexico Fiscal 	Balance 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 22:30 MXN 	 MX   CALENDAR_SECTOR_TRADE 	-900000   -174071000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    

    Окончательные результаты показаны ниже.

    Id 		Country 	Name 					Type 			Importance 			CTime 		Currency Code Sector 			Forecast  Prevalue   Impact 		Freq 			  Ranking
    392030007 	Japan 		Unemployment Rate 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 01:30 JPY 	 JP   CALENDAR_SECTOR_JOBS 	2500000   2600000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    36010001 	Australia 	Building Approvals m/m 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 03:30 AUD 	 AU   CALENDAR_SECTOR_BUSINESS 	-900000   5500000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    250010005 	France 		GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 07:30 EUR 	 FR   CALENDAR_SECTOR_GDP 	200000 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    756050001 	Switzerland 	KOF Economic Barometer 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 09:00 CHF 	 CH   CALENDAR_SECTOR_BUSINESS 	101500000 102700000  CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    276010008 	Germany 	GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 10:00 EUR 	 DE   CALENDAR_SECTOR_GDP 	100000 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    999030016 	European Union 	GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 11:00 EUR 	 EU   CALENDAR_SECTOR_GDP 	200000 	  300000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    380020007 	Italy 		10-Year BTP Auction 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 11:30 EUR 	 IT   CALENDAR_SECTOR_MARKET 	None 	  4010000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE    1
    724080001 	Spain 		Business Confidence 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 12:00 EUR 	 ES   CALENDAR_SECTOR_BUSINESS 	-4100000  -5700000   CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    76030002 	Brazil 		FGV IGP-M Inflation Index m/m 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 13:00 BRL 	 BR   CALENDAR_SECTOR_PRICES 	1000000   810000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    484020016 	Mexico 		GDP q/q 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 14:00 MXN 	 MX   CALENDAR_SECTOR_GDP 	0 	  200000     CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1
    840170001 	United States 	S&P/CS HPI Composite-20 y/y 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 15:00 USD 	 US   CALENDAR_SECTOR_HOUSING 	6800000   7200000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    840030021 	United States 	JOLTS Job Openings 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 	2024.07.30 16:00 USD 	 US   CALENDAR_SECTOR_JOBS 	7979000   8140000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    840060003 	United States 	Dallas Fed Services Revenues 		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 16:30 USD 	 US   CALENDAR_SECTOR_BUSINESS 	3900000   1900000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    484010001 	Mexico Fiscal 	Balance 				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	2024.07.30 22:30 MXN 	 MX   CALENDAR_SECTOR_TRADE 	-900000   -174071000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1

    Эти результаты будут сохранены в переменной ниже:

    Calendar &NewsTime[]

    Функция ниже EconomicNextEvent похожа на предыдущую упомянутую функцию EconomicDetailsMemory. Главное отличие состоит в том, что цель функции ниже — получить следующее событие, а не извлечь все события на определенную дату.

    //+------------------------------------------------------------------+
    //|Will update UpcomingNews structure variable with the next event   |
    //|data                                                              |
    //+------------------------------------------------------------------+
    void CNews::EconomicNextEvent(datetime date=0)
      {
    //--- Declare unassigned Calendar structure variable Empty
       Calendar Empty;
    //--- assign empty values to Calendar structure variable UpcomingNews
       UpcomingNews = Empty;
    //--- If date variable is zero then assign current date
       date = (date==0)?TimeTradeServer():date;
    //--- Query to retrieve next upcoming event.
       string request_text=StringFormat("WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',"
                                        "EventType as 'Type',EventImportance as 'Importance',%s as 'CTime',EventCurrency as 'Currency',"
                                        "EventCode as 'Code',EventSector as 'Sector',EventForecast as 'Forecast',"
                                        "EventPrevalue as 'Prevalue',EventImpact as 'Impact',EventFrequency as 'Freq',"
                                        "RANK() OVER (PARTITION BY %s ORDER BY CASE EventPrevalue WHEN 'None' THEN 2 ELSE 1 END"
                                        ", CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END,CASE EventImportance "
                                        "WHEN 'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 WHEN"
                                        " 'CALENDAR_IMPORTANCE_LOW' THEN 3 ELSE 4 END) Ranking FROM %s) SELECT Id,Country,Name,"
                                        "Type,Importance,CTime,Currency,Code,Sector,Forecast,Prevalue,Impact,Freq FROM MySubQuery "
                                        "where Replace(CTime,'.','-')>=Replace('%s','.','-') AND Ranking<2 Group by CTime LIMIT 1;",
                                        EnumToString(MySchedule),EnumToString(MySchedule),DBMemory.name,TimeToString(date));
    
       int request=DatabasePrepare(DBMemoryConnection,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
       if(request==INVALID_HANDLE)//Checks if the request failed to be completed
         {
          Print("DB: ",NEWS_DATABASE_MEMORY, " request failed with code ", GetLastError());
          PrintFormat(request_text);
         }
    //--- Assign query results to Calendar structure variable UpcomingNews
       DatabaseReadBind(request,UpcomingNews);
    //--- Removes a request created in DatabasePrepare()
       DatabaseFinalize(request);
      }

    Приведенный ниже запрос для функции EconomicNextEvent аналогичен запросу в функции EconomicDetailsMemory, который мы ранее рассматривали. Единственное отличие состоит в том, что приведенный ниже запрос приводит только один результат.

    WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',EventType as 'Type',EventImportance as 'Importance',
    DST_NONE as 'CTime',EventCurrency as 'Currency',EventCode as 'Code',EventSector as 'Sector',EventForecast as 'Forecast',
    EventPrevalue as 'Prevalue',EventImpact as'Impact',EventFrequency as 'Freq',RANK() OVER (PARTITION BY DST_NONE 
    Order BY CASE EventPrevalue WHEN 'None' THEN 2 ELSE 1 END,CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END,
    CASE EventImportance WHEN 'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 
    WHEN 'CALENDAR_IMPORTANCE_LOW' THEN 3 ELSE 4 END) Ranking FROM MQL5Calendar) SELECT Id,Country,Name,Type,
    Importance,CTime,Currency,Code,Sector,Forecast,Prevalue,Impact,Freq FROM MySubQuery where 
    Date(Replace(CTime,'.','-'))=Date(Replace('2024.07.30 00:00','.','-')) and Ranking<2 Group by CTime LIMIT 1;

    Пример результатов для указанного выше запроса показан ниже.

    Id 		Country 	Name 					Type 			Importance 			CTime 		Currency Code Sector 			Forecast  Prevalue   Impact 		Freq 			  Ranking
    392030007 	Japan 		Unemployment Rate 			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 	2024.07.30 01:30 JPY 	 JP   CALENDAR_SECTOR_JOBS 	2500000   2600000    CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH   1
    

    Приведенный выше пример результата будет сохранен в переменной, которую мы объявили вне класса News под названием UpcomingNews.

    //--- Assign query results to Calendar structure variable UpcomingNews
       DatabaseReadBind(request,UpcomingNews);

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

    //+------------------------------------------------------------------+
    //|Will retrieve Upcoming Event Impact data                          |
    //+------------------------------------------------------------------+
    ENUM_CALENDAR_EVENT_IMPACT CNews::GetImpact()
      {
    //--- Declaration of string variable
       string impact=NULL;
    //--- Query to get impact data from previous event with the same event id and matching EventPrevalue and EventForecast scenarios.
       string request_text=StringFormat("SELECT EventImpact FROM %s where Replace(%s,'.','-')<Replace('%s','.','-') AND EventId=%d"
                                        " %s ORDER BY %s DESC LIMIT 1;",DBMemory.name,EnumToString(MySchedule),UpcomingNews.EventDate,
                                        UpcomingNews.EventId,((UpcomingNews.EventPreval=="None"||UpcomingNews.EventForecast=="None")?
                                              "AND EventImpact='CALENDAR_IMPACT_NA'":(int(UpcomingNews.EventPreval)<int(UpcomingNews.EventForecast))?
                                              "AND EventPrevalue<EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'":
                                              (int(UpcomingNews.EventPreval)>int(UpcomingNews.EventForecast))?
                                              "AND EventPrevalue>EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'":
                                              "AND EventPrevalue=EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'"),
                                        EnumToString(MySchedule));
    
       int request=DatabasePrepare(DBMemoryConnection,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
    
       if(request==INVALID_HANDLE)//Checks if the request failed to be completed
         {
          Print("DB: ",NEWS_DATABASE_MEMORY, " request failed with code ", GetLastError());
          PrintFormat(request_text);
         }
       if(DatabaseRead(request))//Will read the one record in the 'Record' table
         {
          //--- assign first column result into impact variable
          DatabaseColumnText(request,0,impact);
         }
    //--- Removes a request created in DatabasePrepare()
       DatabaseFinalize(request);
    //--- Return equivalent Event impact in the enumeration ENUM_CALENDAR_EVENT_IMPACT
       if(impact=="CALENDAR_IMPACT_POSITIVE")
         {
          return CALENDAR_IMPACT_POSITIVE;
         }
       else
          if(impact=="CALENDAR_IMPACT_NEGATIVE")
            {
             return CALENDAR_IMPACT_NEGATIVE;
            }
          else
            {
             return CALENDAR_IMPACT_NA;
            }
      }

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


    Зачем нам нужна величина влияния предыдущего события и как ее использовать?

    Во-первых, нам требуется информация о влиянии предыдущего события, чтобы предсказать, каким может быть результат воздействия следующего события. Таким образом, если предыдущее значение предстоящего события не равно None, а также прогнозируемому значению, мы проверим, равны ли эти значения или какое из них больше другого. Как только мы выяснили, какое значение больше другого, или если значения одинаковы, мы ищем предыдущие события с такой же конфигурацией и извлекаем влияние этого предыдущего события, чтобы предсказать, каким может быть влияние предстоящего события. Например, если предыдущее значение предстоящего события равно 3000, а прогноз — 10 000, мы ищем последнее событие с тем же идентификатором события, предыдущее значение которого было меньше прогнозируемого значения, и используем влияние этого события для предстоящего события.

    Каково влияние события и на чем оно основано?

    Влияние события — это измерение воспринимаемого эффекта, который определенное событие оказало на валюту. Влияние основано на валюте события. Таким образом, если уровень безработицы является предстоящим событием, а предыдущее событие с аналогичным предыдущим значением и конфигурацией прогнозируемого значения имеет влияние события CALENDAR_IMPACT_NEGATIVE, а валютой события является USD, это по сути означает, что на USD негативно повлиял предыдущий уровень безработицы. Поэтому при наблюдении за символом EURUSD сразу после того, как произошел предыдущий уровень безработицы, мы в идеале увидели бы укрепление EURUSD, то есть рост EUR относительно USD. 

    В запросе ниже мы выбираем (SELECT) влияние события из таблицы MQL5Calendar в базе данных в памяти. В выражении WHERE мы ищем даты событий до даты предстоящего события и сортируем по тому же идентификатору события. Затем мы сортируем тот же сценарий взаимосвязи между предыдущим значением события (EventPreval) и прогнозируемым значение события (EventForecast), если UpcomingNews.EventPreval равен None. Если же UpcomingNews.EventForecast равен None, мы уже знаем, что влияние события равно None, если UpcomingNews.EventPreval меньше UpcomingNews. Затем мы ищем предыдущие события, где EventPrevalue < EventForecast и применяется влияние события. Если UpcomingNews.EventPreval больше UpcomingNews.EventForecast, мы ищем предыдущие события, где EventPreval > EventForecast и применяется влияние события. В противном случае мы ищем, где EventPrevalue = EventForecast и применяется влияние события. Все результаты фильтруются по самой последней дате, поскольку выражение ORDER BY упорядочено по убыванию даты события, а выражение LIMIT позволяет вернуть/вывести только одну запись.

    //--- Query to get impact data from previous event with the same event id and matching EventPrevalue and EventForecast scenarios.
       string request_text=StringFormat("SELECT EventImpact FROM %s where Replace(%s,'.','-')<Replace('%s','.','-') AND EventId=%d"
                                        " %s ORDER BY %s DESC LIMIT 1;",DBMemory.name,EnumToString(MySchedule),UpcomingNews.EventDate,
                                        UpcomingNews.EventId,((UpcomingNews.EventPreval=="None"||UpcomingNews.EventForecast=="None")?
                                              "AND EventImpact='CALENDAR_IMPACT_NA'":(int(UpcomingNews.EventPreval)<int(UpcomingNews.EventForecast))?
                                              "AND EventPrevalue<EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'":
                                              (int(UpcomingNews.EventPreval)>int(UpcomingNews.EventForecast))?
                                              "AND EventPrevalue>EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'":
                                              "AND EventPrevalue=EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'"),
                                        EnumToString(MySchedule));

    Пример того, как выглядит запрос выше, когда все переменные заполнены данными. Итак, как вы можете видеть ниже, мы выбираем (SELECT) EventImpact из (FROM) MQL5Calendar, где (WHERE) DST_NONE не менее '2024.08.01 16:30', EventId равен 124500001, EventPrevalue меньше EventForecast, а EventImpact не равен CALENDAR_IMPACT_NA. Затем мы устанавливаем результаты в порядке убывания относительно DST_NONE и, наконец, ограничиваем результаты одним результатом.

    SELECT EventImpact FROM MQL5Calendar where Replace(DST_NONE,'.','-')<Replace('2024.08.01 16:30','.','-') AND EventId=124500001 
    AND EventPrevalue<EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA' ORDER BY DST_NONE DESC LIMIT 1;


    Класс Common Graphics

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

    #include "ObjectProperties.mqh"
    #include "RiskManagement.mqh"
    #include "CommonVariables.mqh"
    #include "News.mqh"
    //+------------------------------------------------------------------+
    //|CommonGraphics class                                              |
    //+------------------------------------------------------------------+
    class CCommonGraphics:CObjectProperties
      {
    private:
       //--- GraphicText structure this structure is responsible for managing the graphical text
       struct GraphicText
         {
          //--- private declaration for struct GraphicText
       private:
          //--- this structure will store properties for the subtext
          struct subtextformat
            {
             string      Label;//Store text label
             string      Text;//Store text value
            };
          //--- this structure inherits from subtextformat and is responsible for finding text
          struct found:subtextformat
            {
             bool        isFound;//Check if text is found
             int         index;//Get index for the text
            };
          //--- structure array for subtexts
          subtextformat  sub_text[];
          //--- function to find text properties from text's label
          found          FoundText(string label)
            {
             found find;
             find.Label="";
             find.Text="";
             find.isFound=false;
             find.index=-1;
             for(uint i=0;i<sub_text.Size();i++)
               {
                //--- If text label is found in array
                if(label==sub_text[i].Label)
                  {
                   //--- Assign text properties
                   find.Label=sub_text[i].Label;
                   find.Text=sub_text[i].Text;
                   find.isFound=true;
                   find.index=int(i);
                   return find;//return found text properties
                  }
               }
             return find;//return text properties
            }
          //--- public declaration for struct GraphicText
       public:
          //--- string variable
          string         text;
          //--- function to set/add text properties
          void           subtext(string label,string value)
            {
             //--- Get text properties from label
             found result = FoundText(label);
             //--- Check if text label was found/exists in array sub_text
             if(!result.isFound)
               {
                //--- Resize array sub_text
                ArrayResize(sub_text,sub_text.Size()+1,sub_text.Size()+2);
                //--- Add text properties for new array index
                sub_text[sub_text.Size()-1].Label = label;
                sub_text[sub_text.Size()-1].Text = value;
               }
             else
               {
                /* Set new text/override text from text label that exists
                in the array sub_text array */
                sub_text[result.index].Text = value;
               }
            }
          //--- function to retrieve text from text label
          string         subtext(string label)
            {
             return FoundText(label).Text;
            }
         };// End of struct GraphicText
    
       //--- AccountInfo object declaration
       CAccountInfo      CAccount;
       //--- News object declaration
       CNews             NewsObj;
       //--- TimeManagement object declaration
       CTimeManagement   CTime;
       //--- Calendar structure array declaration
       Calendar          CalendarArray[];
       //--- color variable declaration
       color             EventColor;
       //--- unit variable declarations
       uint              Fontsize,X_start,Y_start;
       //--- void function declarations for Graphical blocks
       void              Block_1();
       void              Block_2(uint SecondsPreEvent=5);
       CRiskManagement   CRisk;//Risk management class object
       //--- GraphicText structure array declarations
       GraphicText       Texts_Block1[9],Texts_Block2[7];
       //--- structure to store text height and width
       struct Text_Prop_Size
         {
          uint           Height;//store text height
          uint           Width;//store text width
         };
    
       //--- void function to retrieve sum of the texts height and the maximum width of texts from GraphicText array Texts
       void              GetTextMaxWidthAndHeight(GraphicText &Texts[],uint &Max_Height,uint &Max_Width,uint FontSize)
         {
          //--- set fontsize properties to get accurate text height and width sizes
          TextSetFont("Arial",(-1*FontSize)-100);
          //--- set variables to default value of zero
          Max_Height=0;
          Max_Width=0;
          //--- loop through all texts in the GraphicText array Texts
          for(uint i=0;i<Texts.Size();i++)
            {
             //--- temporary declarations for height and width
             uint Height=0,Width=0;
             //--- retrieve text height and width from index in Texts array
             TextGetSize(Texts[i].text,Width,Height);
             //--- sum texts height to variable Max_Height
             Max_Height+=Height;
             //--- assign width if text width is more than variable Max_Width value
             Max_Width=(Width>Max_Width)?Width:Max_Width;
            }
         }
       //--- function to retrieve text height and width properties in the structure Text_Prop_Size format
       Text_Prop_Size    GetText(string Text,uint FontSize)
         {
          //--- structure Text_Prop_Size variable
          Text_Prop_Size Size;
          //--- set fontsize properties to get accurate text height and width sizes
          TextSetFont("Arial",(-1*FontSize)-100);
          //--- retrieve text height and width from Text string variable
          TextGetSize(Text,Size.Width,Size.Height);
          //--- return structure Text_Prop_Size variable
          return Size;
         }
       //--- Function to get texts height sum and max width in the structure Text_Prop_Size format
       Text_Prop_Size    GetTextMax(GraphicText &Texts[],uint FontSize)
         {
          //--- structure Text_Prop_Size variable
          Text_Prop_Size Size;
          //--- uint variable declarations for text properties
          uint Max_Height;
          uint Max_Width;
          //--- Retrieve sum of texts height and maximum texts width into Max_Height,Max_Width
          GetTextMaxWidthAndHeight(Texts,Max_Height,Max_Width,FontSize);
          //--- assign values into structure Text_Prop_Size variable
          Size.Height = Max_Height;
          Size.Width = Max_Width;
          //--- return structure Text_Prop_Size variable
          return Size;
         }
       //--- Boolean declarations
       bool              is_date,is_spread,is_news,is_events;
    
    public:
       //--- class constructor
                         CCommonGraphics(bool display_date,bool display_spread,bool display_news,bool display_events);
                        ~CCommonGraphics(void) {}//class destructor
       void              GraphicsRefresh(uint SecondsPreEvent=5);//will create/refresh the chart objects
       //--- will update certain graphics at an interval
       void              Block_2_Realtime(uint SecondsPreEvent=5);
       //--- will create chart event objects
       void              NewsEvent();
      };

    Приведенная ниже структура GraphicText не является обычной структурой в MQL5. Эта структура содержит частные и/или публичные объявления структур, массивов структур, функций и строковой переменной. В этом случае структура GraphicText ведет себя как целый отдельный класс. Целью этой структуры является управление и хранение графических текстов. Структурный массив sub_text сохранит все свойства текста, а функция FoundText будет искать любые совпадения текстовой метки в пределах массива sub_text. Если совпадение есть, мы вернем подробности этого совпадения в структуре found, которая наследуется от структуры subtextformat. В публичных объявлениях строковая переменная с именем text будет хранить полную длину текста вструктурном массиве sub_text. Функция void subtext добавит текстовую метку и текст/подтекст в массив sub_text или перезапишет текст/подтекст, хранящийся в массиве, если внутри массива находится метка строковой переменной. Строковая функция subtext извлечет текст для метки, связанной с метками в структурном массиве sub_text.

       //--- GraphicText structure this structure is responsible for managing the graphical text
       struct GraphicText
         {
          //--- private declaration for struct GraphicText
       private:
          //--- this structure will store properties for the subtext
          struct subtextformat
            {
             string      Label;//Store text label
             string      Text;//Store text value
            };
          //--- this structure inherits from subtextformat and is responsible for finding text
          struct found:subtextformat
            {
             bool        isFound;//Check if text is found
             int         index;//Get index for the text
            };
          //--- structure array for subtexts
          subtextformat  sub_text[];
          //--- function to find text properties from text's label
          found          FoundText(string label)
            {
             found find;
             find.Label="";
             find.Text="";
             find.isFound=false;
             find.index=-1;
             for(uint i=0;i<sub_text.Size();i++)
               {
                //--- If text label is found in array
                if(label==sub_text[i].Label)
                  {
                   //--- Assign text properties
                   find.Label=sub_text[i].Label;
                   find.Text=sub_text[i].Text;
                   find.isFound=true;
                   find.index=int(i);
                   return find;//return found text properties
                  }
               }
             return find;//return text properties
            }
          //--- public declaration for struct GraphicText
       public:
          //--- string variable
          string         text;
          //--- function to set/add text properties
          void           subtext(string label,string value)
            {
             //--- Get text properties from label
             found result = FoundText(label);
             //--- Check if text label was found/exists in array sub_text
             if(!result.isFound)
               {
                //--- Resize array sub_text
                ArrayResize(sub_text,sub_text.Size()+1,sub_text.Size()+2);
                //--- Add text properties for new array index
                sub_text[sub_text.Size()-1].Label = label;
                sub_text[sub_text.Size()-1].Text = value;
               }
             else
               {
                /* Set new text/override text from text label that exists
                in the array sub_text array */
                sub_text[result.index].Text = value;
               }
            }
          //--- function to retrieve text from text label
          string         subtext(string label)
            {
             return FoundText(label).Text;
            }
         };// End of struct GraphicText

    Функция ниже создана для извлечения суммы высот текста в структурном массиве Texts, а также максимальной ширины из текстов в массиве.

     //--- void function to retrieve sum of the texts height and the maximum width of texts from GraphicText array Texts
       void              GetTextMaxWidthAndHeight(GraphicText &Texts[],uint &Max_Height,uint &Max_Width,uint FontSize)
         {
          //--- set fontsize properties to get accurate text height and width sizes
          TextSetFont("Arial",(-1*FontSize)-100);
          //--- set variables to default value of zero
          Max_Height=0;
          Max_Width=0;
          //--- loop through all texts in the GraphicText array Texts
          for(uint i=0;i<Texts.Size();i++)
            {
             //--- temporary declarations for height and width
             uint Height=0,Width=0;
             //--- retrieve text height and width from index in Texts array
             TextGetSize(Texts[i].text,Width,Height);
             //--- sum texts height to variable Max_Height
             Max_Height+=Height;
             //--- assign width if text width is more than variable Max_Width value
             Max_Width=(Width>Max_Width)?Width:Max_Width;
            }
         }

    Функция ниже создана для извлечения свойств высоты и ширины в формате структуры Text_Prop_Size из строковой переменной Text.

    //--- function to retrieve text height and width properties in the structure Text_Prop_Size format
       Text_Prop_Size    GetText(string Text,uint FontSize)
         {
          //--- structure Text_Prop_Size variable
          Text_Prop_Size Size;
          //--- set fontsize properties to get accurate text height and width sizes
          TextSetFont("Arial",(-1*FontSize)-100);
          //--- retrieve text height and width from Text string variable
          TextGetSize(Text,Size.Width,Size.Height);
          //--- return structure Text_Prop_Size variable
          return Size;
         }

    Функция ниже создана для извлечения суммы свойств высоты и максимальной ширины в формате структуры Text_Prop_Size из структурного массива Texts.

    //--- Function to get texts height sum and max width in the structure Text_Prop_Size format
       Text_Prop_Size    GetTextMax(GraphicText &Texts[],uint FontSize)
         {
          //--- structure Text_Prop_Size variable
          Text_Prop_Size Size;
          //--- uint variable declarations for text properties
          uint Max_Height;
          uint Max_Width;
          //--- Retrieve sum of texts height and maximum texts width into Max_Height,Max_Width
          GetTextMaxWidthAndHeight(Texts,Max_Height,Max_Width,FontSize);
          //--- assign values into structure Text_Prop_Size variable
          Size.Height = Max_Height;
          Size.Width = Max_Width;
          //--- return structure Text_Prop_Size variable
          return Size;
         }

    В конструкторе класса инициализируем наши ранее объявленные переменные: is_date (отображать ли информацию о дате на графике), is_spread (отображать ли информацию о спреде на графике), is_news (отображать ли информацию о новостях на графике) и is_events (отображать ли информацию об объекте события на графике). NewsObject. Функция EconomicNextEvent обновит переменную UpcomingNews сведениями о следующих новостях.

    //+------------------------------------------------------------------+
    //|Constructor                                                       |
    //+------------------------------------------------------------------+
    CCommonGraphics::CCommonGraphics(bool display_date,bool display_spread,bool display_news,bool display_events):
    //--- Assign variables
       is_date(display_date),is_spread(display_spread),is_news(display_news),is_events(display_events)
      {
    //--- get next news event
       NewsObject.EconomicNextEvent();
      }

    GraphicsRefresh будет отвечать за очистку графика от ранее созданных объектов (если таковые имеются), а затем будет вызывать другие функции, которые будут создавать объекты графика для отображения различной информации на текущем графике.

    //+------------------------------------------------------------------+
    //|will create/refresh the chart objects                             |
    //+------------------------------------------------------------------+
    void CCommonGraphics::GraphicsRefresh(uint SecondsPreEvent=5)
      {
    //--- create graphics if outside the strategy tester or in the strategy tester and visual mode is enabled
       if((!MQLInfoInteger(MQL_TESTER))||(MQLInfoInteger(MQL_TESTER)&&MQLInfoInteger(MQL_VISUAL_MODE)))
         {
          //--- Delete chart objects
          DeleteObj();//function from Object properties class
          Block_1();//Create graphics for block 1
          //--- Check whether to create graphics for block 2
          if(is_date||is_news||is_spread)
            {
             Block_2(SecondsPreEvent);//Create graphics for block 2
            }
          //--- creates event objects
          NewsEvent();
         }
      }

    Функция Block_1 ниже отвечает за создание графических элементов для графического блока 1, состоящего из:

    • Имени символа
    • Периода символа
    • Описания символа
    • Размер контракта по символу
    • Минимального размера лота по символу
    • Максимального размера лота по символу
    • Шага объема символа
    • Ограничения объема символа
    • Вариант управления рисками
    • Нижнего уровня риска
    • Верхнего уровня риска

    Графический блок 1

    //+------------------------------------------------------------------+
    //|Graphical Block 1                                                 |
    //+------------------------------------------------------------------+
    void CCommonGraphics::Block_1()
      {
    //--- Set text object color depending if the chart color mode is LightMode or not
       TextObj_color = (isLightMode)?clrBlack:clrWheat;
    //--- Set text properties for Symbol name,Symbol period and Symbol description # section 1
       Texts_Block1[0].text = Symbol()+", "+GetChartPeriodName()+": "+CSymbol.Description();//set main text
       Texts_Block1[0].subtext("Symbol Name",Symbol()+",");//set subtext - label,value
       Texts_Block1[0].subtext("Symbol Period",GetChartPeriodName());//set subtext - label,value
       Texts_Block1[0].subtext("Symbol Desc",": "+CSymbol.Description());//set subtext - label,value
    //--- Set text properties for Contract size # section 2
       Texts_Block1[1].text = "Contract Size: "+string(CSymbol.ContractSize());//set main text
       Texts_Block1[1].subtext("Contract Size Text","Contract Size:");//set subtext - label,value
       Texts_Block1[1].subtext("Contract Size",string(CSymbol.ContractSize()));//set subtext - label,value
    //--- Set text properties for Minimum lot # section 3
       Texts_Block1[2].text = "Minimum Lot: "+string(CSymbol.LotsMin());//set main text
       Texts_Block1[2].subtext("Minimum Lot Text","Minimum Lot:");//set subtext - label,value
       Texts_Block1[2].subtext("Minimum Lot",string(CSymbol.LotsMin()));//set subtext - label,value
    //--- Set text properties for Max lot # section 4
       Texts_Block1[3].text = "Max Lot: "+string(CSymbol.LotsMax());//set main text
       Texts_Block1[3].subtext("Max Lot Text","Max Lot:");//set subtext - label,value
       Texts_Block1[3].subtext("Max Lot",string(CSymbol.LotsMax()));//set subtext - label,value
    //--- Set text properties for Volume step # section 5
       Texts_Block1[4].text = "Volume Step: "+string(CSymbol.LotsStep());//set main text
       Texts_Block1[4].subtext("Volume Step Text","Volume Step:");//set subtext - label,value
       Texts_Block1[4].subtext("Volume Step",string(CSymbol.LotsStep()));//set subtext - label,value
    //--- Set text properties for Volume limit # section 6
       Texts_Block1[5].text = "Volume Limit: "+string(CSymbol.LotsLimit());//set main text
       Texts_Block1[5].subtext("Volume Limit Text","Volume Limit:");//set subtext - label,value
       Texts_Block1[5].subtext("Volume Limit",string(CSymbol.LotsLimit()));//set subtext - label,value
    //--- Set text properties for Risk option # section 7
       Texts_Block1[6].text = "Risk Option: "+CRisk.GetRiskOption();//set main text
       Texts_Block1[6].subtext("Risk Option Text","Risk Option:");//set subtext - label,value
       Texts_Block1[6].subtext("Risk Option",CRisk.GetRiskOption());//set subtext - label,value
    //--- Set text properties for Risk floor # section 8
       Texts_Block1[7].text = "Risk Floor: "+CRisk.GetRiskFloor();//set main text
       Texts_Block1[7].subtext("Risk Floor Text","Risk Floor:");//set subtext - label,value
       Texts_Block1[7].subtext("Risk Floor",CRisk.GetRiskFloor());//set subtext - label,value
    //--- Set text properties for Risk ceiling # section 9
       Texts_Block1[8].text = "Risk Ceiling: "+CRisk.GetRiskCeil();//set main text
       Texts_Block1[8].subtext("Risk Ceiling Text","Risk Ceiling:");//set subtext - label,value
       Texts_Block1[8].subtext("Risk Ceiling",CRisk.GetRiskCeil());//set subtext - label,value
    
    //--- Set basic properties
       Fontsize=10;//Set Fontsize
       X_start=2;//Set X distance
       Y_start=2;//Set Y distance
    
    /* Create objects # section 1*/
    //-- Create the background object for width and height+3 of section 1 text
       Square(0,"Symbol Name background",X_start,Y_start,GetText(Texts_Block1[0].text,Fontsize).Width,
              GetText(Texts_Block1[0].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 1
       TextObj(0,"Symbol Name",Texts_Block1[0].subtext("Symbol Name"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[0].subtext("Symbol Name"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol Period",Texts_Block1[0].subtext("Symbol Period"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[0].subtext("Symbol Period"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol Desc",Texts_Block1[0].subtext("Symbol Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 2*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[0].text,Fontsize).Height;//Re-adjust Y distance, add height from section 1
       //-- Create the background object for width and height+3 of section 2 text
       Square(0,"Symbol Contract Size background",X_start,Y_start,GetText(Texts_Block1[1].text,Fontsize).Width,
              GetText(Texts_Block1[1].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 2
       TextObj(0,"Symbol Contract Size Text",Texts_Block1[1].subtext("Contract Size Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[1].subtext("Contract Size Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol Contract Size",Texts_Block1[1].subtext("Contract Size"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 3*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[1].text,Fontsize).Height;//Re-adjust Y distance, add height from section 2
       //-- Create the background object for width and height+3 of section 3 text
       Square(0,"Symbol MinLot background",X_start,Y_start,GetText(Texts_Block1[2].text,Fontsize).Width,
              GetText(Texts_Block1[2].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 3
       TextObj(0,"Symbol MinLot Text",Texts_Block1[2].subtext("Minimum Lot Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[2].subtext("Minimum Lot Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol MinLot",Texts_Block1[2].subtext("Minimum Lot"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 4*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[2].text,Fontsize).Height;//Re-adjust Y distance, add height from section 3
       //-- Create the background object for width and height+3 of section 4 text
       Square(0,"Symbol MaxLot background",X_start,Y_start,GetText(Texts_Block1[3].text,Fontsize).Width,
              GetText(Texts_Block1[3].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 4
       TextObj(0,"Symbol MaxLot Text",Texts_Block1[3].subtext("Max Lot Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[3].subtext("Max Lot Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol MaxLot",Texts_Block1[3].subtext("Max Lot"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 5*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[3].text,Fontsize).Height;//Re-adjust Y distance, add height from section 4
       //-- Create the background object for width and height+3 of section 5 text
       Square(0,"Symbol Volume Step background",X_start,Y_start,GetText(Texts_Block1[4].text,Fontsize).Width,
              GetText(Texts_Block1[4].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 5
       TextObj(0,"Symbol Volume Step Text",Texts_Block1[4].subtext("Volume Step Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[4].subtext("Volume Step Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol Volume Step",Texts_Block1[4].subtext("Volume Step"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 6*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[4].text,Fontsize).Height;//Re-adjust Y distance, add height from section 5
       //-- Create the background object for width and height+3 of section 6 text
       Square(0,"Symbol Volume Limit background",X_start,Y_start,GetText(Texts_Block1[5].text,Fontsize).Width,
              GetText(Texts_Block1[5].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 6
       TextObj(0,"Symbol Volume Limit Text",Texts_Block1[5].subtext("Volume Limit Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[5].subtext("Volume Limit Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Symbol Volume Limit",Texts_Block1[5].subtext("Volume Limit"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 7*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[5].text,Fontsize).Height;//Re-adjust Y distance, add height from section 6
       //-- Create the background object for width and height+3 of section 7 text
       Square(0,"Risk Option background",X_start,Y_start,GetText(Texts_Block1[6].text,Fontsize).Width,
              GetText(Texts_Block1[6].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 7
       TextObj(0,"Risk Option Text",Texts_Block1[6].subtext("Risk Option Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[6].subtext("Risk Option Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Risk Option",Texts_Block1[6].subtext("Risk Option"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 8*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[6].text,Fontsize).Height;//Re-adjust Y distance, add height from section 7
       //-- Create the background object for width and height+3 of section 8 text
       Square(0,"Risk Floor background",X_start,Y_start,GetText(Texts_Block1[7].text,Fontsize).Width,
              GetText(Texts_Block1[7].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 8
       TextObj(0,"Risk Floor Text",Texts_Block1[7].subtext("Risk Floor Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[7].subtext("Risk Floor Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Risk Floor",Texts_Block1[7].subtext("Risk Floor"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /* Create objects # section 9*/
       X_start=2;//Reset X distance
       Y_start+=GetText(Texts_Block1[7].text,Fontsize).Height;//Re-adjust Y distance, add height from section 8
       //-- Create the background object for width and height+3 of section 9 text
       Square(0,"Risk Ceil background",X_start,Y_start,GetText(Texts_Block1[8].text,Fontsize).Width,
              GetText(Texts_Block1[8].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
       Y_start+=3;//Re-adjust Y distance
       X_start+=2;//Re-adjust X distance
    //-- Will create the text objects for section 9
       TextObj(0,"Risk Ceil Text",Texts_Block1[8].subtext("Risk Ceiling Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
       X_start+=GetText(Texts_Block1[8].subtext("Risk Ceiling Text"),Fontsize).Width;//Re-adjust X distance
       TextObj(0,"Risk Ceil",Texts_Block1[8].subtext("Risk Ceiling"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
      }

    Функция Block_2 ниже отвечает за создание графических элементов для графического блока 2, состоящего из:

    • Текущих даты и времени
    • Даты события
    • Названия события
    • Страны события
    • Валюты события
    • Важности события
    • Ранга спреда

    Графический блок 2

    //+------------------------------------------------------------------+
    //|Graphical Block 2                                                 |
    //+------------------------------------------------------------------+
    void CCommonGraphics::Block_2(uint SecondsPreEvent=5)
      {
    //--- Set text object color depending if the chart color mode is LightMode or not
       TextObj_color=(isLightMode)?clrBlack:clrWheat;
       if(is_date)//Check whether to display date information
         {
          //--- Set text properties for Date and Time # section 10
          Texts_Block2[0].text = "Date:"+TimeToString(TimeTradeServer(),TIME_DATE)+"|| Time:"+TimeToString(TimeTradeServer(),TIME_MINUTES)
                                 +"   ";//set main text
          Texts_Block2[0].subtext("Date Text","Date:");//set subtext - label,value
          Texts_Block2[0].subtext("Date",TimeToString(TimeTradeServer(),TIME_DATE));//set subtext - label,value
          Texts_Block2[0].subtext("Time Text","|| Time:");//set subtext - label,value
          Texts_Block2[0].subtext("Time",TimeToString(TimeTradeServer(),TIME_MINUTES));//set subtext - label,value
         }
       if(is_news)//Check whether to display news information
         {
          //--- Set text object color depending on upcoming news event's Importance
          EventColor = NewsObj.GetImportance_color(NewsObj.IMPORTANCE(UpcomingNews.EventImportance));
          //--- Set text properties for Event Date # section 11
          Texts_Block2[1].text = "Event: @"+UpcomingNews.EventDate+" ";//set main text
          Texts_Block2[1].subtext("Event Date Text","Event: @");//set subtext - label,value
          Texts_Block2[1].subtext("Event Date",UpcomingNews.EventDate);//set subtext - label,value
          //--- Set text properties for Event Name # section 12
          Texts_Block2[2].text = "Name: "+UpcomingNews.EventName+" ";//set main text
          Texts_Block2[2].subtext("Event Name Text","Name: ");//set subtext - label,value
          Texts_Block2[2].subtext("Event Name",UpcomingNews.EventName);//set subtext - label,value
          //--- Set text properties for Event Country # section 13
          Texts_Block2[3].text = "Country: "+UpcomingNews.CountryName+" ";//set main text
          Texts_Block2[3].subtext("Event Country Text","Country: ");//set subtext - label,value
          Texts_Block2[3].subtext("Event Country",UpcomingNews.CountryName);//set subtext - label,value
          //--- Set text properties for Event Currency # section 14
          Texts_Block2[4].text = "Currency: "+UpcomingNews.EventCurrency+" ";//set main text
          Texts_Block2[4].subtext("Event Currency Text","Currency: ");//set subtext - label,value
          Texts_Block2[4].subtext("Event Currency",UpcomingNews.EventCurrency);//set subtext - label,value
          //--- Set text properties for Event Importance # section 15
          Texts_Block2[5].text = "Importance: "+NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance))+" ";//set main text
          Texts_Block2[5].subtext("Importance Text","Importance: ");//set subtext - label,value
          Texts_Block2[5].subtext("Importance",NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance)));//set subtext - label,value
         }
       if(is_spread)//Check whether to display spread information
         {
          //--- Set text properties for Spread # section 16
          Texts_Block2[6].text = "Spread: "+string(CSymbol.Spread())+" Rating: "+CSymbol.SpreadDesc()+" ";//set main text
          Texts_Block2[6].subtext("Spread Text","Spread:");//set subtext - label,value
          Texts_Block2[6].subtext("Spread",string(CSymbol.Spread()));//set subtext - label,value
          Texts_Block2[6].subtext("Rating Text"," Rating:");//set subtext - label,value
          Texts_Block2[6].subtext("Rating Desc",CSymbol.SpreadDesc());//set subtext - label,value
         }
    
    //--- Set basic properties
       Fontsize=10;
       X_start=2;//Reset X distance
       Y_start=GetTextMax(Texts_Block1,Fontsize).Height+29;//Re-adjust Y distance from graphical block 1 Height
    
       /* Create objects # section 10*/
       if(is_date)//Check whether to display date information
         {
          //-- Create the background object for width and height+3 of section 10 text
          Square(0,"Datetime background",X_start,Y_start,GetText(Texts_Block2[0].text,Fontsize).Width,
                 GetText(Texts_Block2[0].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //-- Will create the text objects for section 10
          TextObj(0,"Date Text",Texts_Block2[0].subtext("Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[0].subtext("Date Text"),Fontsize).Width;//Re-adjust X distance
          TextObj(0,"Date",Texts_Block2[0].subtext("Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[0].subtext("Date"),Fontsize).Width;//Re-adjust X distance
          TextObj(0,"Time Text",Texts_Block2[0].subtext("Time Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[0].subtext("Time Text"),Fontsize).Width;//Re-adjust X distance
          //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring
          TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent),
                                              CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat;
          TextObj(0,"Time",Texts_Block2[0].subtext("Time"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
         }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       if(UpcomingNews.CountryName!=NULL&&is_news)//Check whether to display news information and if upcoming news is available
         {
          /* Create objects # section 11*/
          Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;//Re-adjust Y distance depending if section 10 is shown
          X_start=2;//Reset X distance
          //-- Create the background object for width and height+3 of section 11 text
          Square(0,"Event Date background",X_start,Y_start,GetText(Texts_Block2[1].text,Fontsize).Width,
                 GetText(Texts_Block2[1].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will create the text objects for section 11
          TextObj(0,"Event Date Text",Texts_Block2[1].subtext("Event Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[1].subtext("Event Date Text"),Fontsize).Width;//Re-adjust X distance
          //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring
          TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent),
                                              CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat;
          TextObj(0,"Event Date",Texts_Block2[1].subtext("Event Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 12*/
          Y_start+=GetText(Texts_Block2[1].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Create the background object for width and height+3 of section 12 text
          Square(0,"Event Name background",X_start,Y_start,GetText(Texts_Block2[2].text,Fontsize).Width,
                 GetText(Texts_Block2[2].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will create the text objects for section 12
          TextObj(0,"Event Name Text",Texts_Block2[2].subtext("Event Name Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[2].subtext("Event Name Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Event Name",Texts_Block2[2].subtext("Event Name"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 13*/
          Y_start+=GetText(Texts_Block2[2].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Create the background object for width and height+3 of section 13 text
          Square(0,"Event Country background",X_start,Y_start,GetText(Texts_Block2[3].text,Fontsize).Width,
                 GetText(Texts_Block2[3].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will create the text objects for section 13
          TextObj(0,"Event Country Text",Texts_Block2[3].subtext("Event Country Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[3].subtext("Event Country Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Event Country",Texts_Block2[3].subtext("Event Country"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 14*/
          Y_start+=GetText(Texts_Block2[3].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Create the background object for width and height+3 of section 14 text
          Square(0,"Event Currency background",X_start,Y_start,GetText(Texts_Block2[4].text,Fontsize).Width,
                 GetText(Texts_Block2[4].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will create the text objects for section 14
          TextObj(0,"Event Currency Text",Texts_Block2[4].subtext("Event Currency Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[4].subtext("Event Currency Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Event Currency",Texts_Block2[4].subtext("Event Currency"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 15*/
          Y_start+=GetText(Texts_Block2[4].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Create the background object for width and height+3 of section 15 text
          Square(0,"Event Importance background",X_start,Y_start,GetText(Texts_Block2[5].text,Fontsize).Width,
                 GetText(Texts_Block2[5].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will create the text objects for section 15
          TextObj(0,"Importance Text",Texts_Block2[5].subtext("Importance Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[5].subtext("Importance Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Importance",Texts_Block2[5].subtext("Importance"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 16*/
          if(is_spread)//Check whether to display spread information
            {
             Y_start+=GetText(Texts_Block2[5].text,Fontsize).Height;//Re-adjust Y distance
             X_start=2;//Reset X distance
             //-- Create the background object for width and height+3 of section 16 text
             Square(0,"Spread background",X_start,Y_start,GetText(Texts_Block2[6].text,Fontsize).Width,
                    GetText(Texts_Block2[6].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
             Y_start+=3;//Re-adjust Y distance
             X_start+=2;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             color Spread_clr=CSymbol.SpreadColor();
             //--- Set text object color depending if the chart color mode is LightMode or not
             TextObj_color=(isLightMode)?clrBlack:clrWheat;
             //-- Will create the text objects for section 16
             TextObj(0,"Symbol Spread Text",Texts_Block2[6].subtext("Spread Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending if the chart color mode is LightMode or not
             TextObj_color=(isLightMode)?clrBlack:clrWheat;
             TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
            }
         }
       else
          /* Create objects # section 16*/
          if(is_spread)//Check whether to display spread information
            {
             Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;//Re-adjust Y distance depending if section 10 is shown
             X_start=2;//Reset X distance
             //-- Create the background object for width and height+3 of section 16 text
             Square(0,"Spread background",X_start,Y_start,GetText(Texts_Block2[6].text,Fontsize).Width,
                    GetText(Texts_Block2[6].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER);
             Y_start+=3;//Re-adjust Y distance
             X_start+=2;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             color Spread_clr=CSymbol.SpreadColor();
             //--- Set text object color depending if the chart color mode is LightMode or not
             TextObj_color=(isLightMode)?clrBlack:clrWheat;
             //-- Will create the text objects for section 16
             TextObj(0,"Symbol Spread Text",Texts_Block2[6].subtext("Spread Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending if the chart color mode is LightMode or not
             TextObj_color=(isLightMode)?clrBlack:clrWheat;
             TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
            }
      }

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

    //+------------------------------------------------------------------+
    //|will update certain graphics at an interval                       |
    //+------------------------------------------------------------------+
    void CCommonGraphics::Block_2_Realtime(uint SecondsPreEvent=5)
      {
       if(MQLInfoInteger(MQL_TESTER)&&!MQLInfoInteger(MQL_VISUAL_MODE))
         {
          return;//exit if in strategy tester and not in visual mode
         }
    
       if(is_date)//Check whether to display date information
         {
          //--- Set text properties for Date and Time # section 10
          Texts_Block2[0].text = "Date:"+TimeToString(TimeTradeServer(),TIME_DATE)+"|| Time:"+TimeToString(TimeTradeServer(),TIME_SECONDS)
                                 +"   ";//set main text
          Texts_Block2[0].subtext("Date Text","Date:");//set subtext - label,value
          Texts_Block2[0].subtext("Date",TimeToString(TimeTradeServer(),TIME_DATE));//set subtext - label,value
          Texts_Block2[0].subtext("Time Text","|| Time:");//set subtext - label,value
          Texts_Block2[0].subtext("Time",TimeToString(TimeTradeServer(),TIME_SECONDS));//set subtext - label,value
         }
       if(is_news)//Check whether to display news information
         {
          //--- Set text object color depending on upcoming news event's Importance
          EventColor = NewsObj.GetImportance_color(NewsObj.IMPORTANCE(UpcomingNews.EventImportance));
          //--- Set text properties for Event Date # section 11
          Texts_Block2[1].text = "Event: @"+UpcomingNews.EventDate+" ";//set main text
          Texts_Block2[1].subtext("Event Date Text","Event: @");//set subtext - label,value
          Texts_Block2[1].subtext("Event Date",UpcomingNews.EventDate);//set subtext - label,value
          //--- Set text properties for Event Name # section 12
          Texts_Block2[2].text = "Name: "+UpcomingNews.EventName+" ";//set main text
          Texts_Block2[2].subtext("Event Name Text","Name: ");//set subtext - label,value
          Texts_Block2[2].subtext("Event Name",UpcomingNews.EventName);//set subtext - label,value
          //--- Set text properties for Event Country # section 13
          Texts_Block2[3].text = "Country: "+UpcomingNews.CountryName+" ";//set main text
          Texts_Block2[3].subtext("Event Country Text","Country: ");//set subtext - label,value
          Texts_Block2[3].subtext("Event Country",UpcomingNews.CountryName);//set subtext - label,value
          //--- Set text properties for Event Currency # section 14
          Texts_Block2[4].text = "Currency: "+UpcomingNews.EventCurrency+" ";//set main text
          Texts_Block2[4].subtext("Event Currency Text","Currency: ");//set subtext - label,value
          Texts_Block2[4].subtext("Event Currency",UpcomingNews.EventCurrency);//set subtext - label,value
          //--- Set text properties for Event Importance # section 15
          Texts_Block2[5].text = "Importance: "+NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance))+" ";//set main text
          Texts_Block2[5].subtext("Importance Text","Importance: ");//set subtext - label,value
          Texts_Block2[5].subtext("Importance",NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance)));//set subtext - label,value
         }
       if(is_spread)//Check whether to display spread information
         {
          //--- Set text properties for Spread # section 16
          Texts_Block2[6].text = "Spread: "+string(CSymbol.Spread())+" Rating: "+CSymbol.SpreadDesc()+" ";//set main text
          Texts_Block2[6].subtext("Spread Text","Spread:");//set subtext - label,value
          Texts_Block2[6].subtext("Spread",string(CSymbol.Spread()));//set subtext - label,value
          Texts_Block2[6].subtext("Rating Text"," Rating:");//set subtext - label,value
          Texts_Block2[6].subtext("Rating Desc",CSymbol.SpreadDesc());//set subtext - label,value
         }
    
    //--- Set basic properties
       Fontsize=10;
       X_start=2;//Reset X distance
       Y_start=GetTextMax(Texts_Block1,Fontsize).Height+29;//Re-adjust Y distance from section block 1
    
       /* Create objects # section 10*/
       if(is_date)//Check whether to display date information
         {
          //-- Check if the background object x-size for section 10 is the same size as section 10 text width
          if(ObjectGetInteger(0,"Datetime background",OBJPROP_XSIZE)!=GetText(Texts_Block2[0].text,Fontsize).Width)
            {
             //-- Will re-adjust background object to any changes of section 10 text width
             ObjectSetInteger(0,"Datetime background",OBJPROP_XSIZE,long(GetText(Texts_Block2[0].text,Fontsize).Width));
            }
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will update the text objects for section 10
          TextObj(0,"Date Text",Texts_Block2[0].subtext("Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[0].subtext("Date Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          TextObj(0,"Date",Texts_Block2[0].subtext("Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[0].subtext("Date"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          TextObj(0,"Time Text",Texts_Block2[0].subtext("Time Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[0].subtext("Time Text"),Fontsize).Width;//Re-adjust X distance
          //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring
          TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent),
                                              CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat;
          TextObj(0,"Time",Texts_Block2[0].subtext("Time"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
         }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       if(UpcomingNews.CountryName!=NULL&&is_news)//Check whether to display news information
         {
          /* Create objects # section 11*/
          Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;
          X_start=2;//Reset X distance
          //-- Check if the background object x-size for section 11 is the same size as section 11 text width
          if(ObjectGetInteger(0,"Event Date background",OBJPROP_XSIZE)!=GetText(Texts_Block2[1].text,Fontsize).Width)
            {
             //-- Will re-adjust background object to any changes of section 11 text width
             ObjectSetInteger(0,"Event Date background",OBJPROP_XSIZE,long(GetText(Texts_Block2[1].text,Fontsize).Width));
            }
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will update the text objects for section 11
          TextObj(0,"Event Date Text",Texts_Block2[1].subtext("Event Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[1].subtext("Event Date Text"),Fontsize).Width;//Re-adjust X distance
          //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring
          TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent),
                                              CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat;
          TextObj(0,"Event Date",Texts_Block2[1].subtext("Event Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 12*/
          Y_start+=GetText(Texts_Block2[1].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Check if the background object x-size for section 12 is the same size as section 12 text width
          if(ObjectGetInteger(0,"Event Name background",OBJPROP_XSIZE)!=GetText(Texts_Block2[2].text,Fontsize).Width)
            {
             //-- Will re-adjust background object to any changes of section 12 text width
             ObjectSetInteger(0,"Event Name background",OBJPROP_XSIZE,long(GetText(Texts_Block2[2].text,Fontsize).Width));
            }
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will update the text objects for section 12
          TextObj(0,"Event Name Text",Texts_Block2[2].subtext("Event Name Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[2].subtext("Event Name Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Event Name",Texts_Block2[2].subtext("Event Name"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 13*/
          Y_start+=GetText(Texts_Block2[2].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Check if the background object x-size for section 13 is the same size as section 13 text width
          if(ObjectGetInteger(0,"Event Country background",OBJPROP_XSIZE)!=GetText(Texts_Block2[3].text,Fontsize).Width)
            {
             //-- Will re-adjust background object to any changes of section 13 text width
             ObjectSetInteger(0,"Event Country background",OBJPROP_XSIZE,long(GetText(Texts_Block2[3].text,Fontsize).Width));
            }
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will update the text objects for section 13
          TextObj(0,"Event Country Text",Texts_Block2[3].subtext("Event Country Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[3].subtext("Event Country Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Event Country",Texts_Block2[3].subtext("Event Country"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 14*/
          Y_start+=GetText(Texts_Block2[3].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Check if the background object x-size for section 14 is the same size as section 14 text width
          if(ObjectGetInteger(0,"Event Currency background",OBJPROP_XSIZE)!=GetText(Texts_Block2[4].text,Fontsize).Width)
            {
             //-- Will re-adjust background object to any changes of section 14 text width
             ObjectSetInteger(0,"Event Currency background",OBJPROP_XSIZE,long(GetText(Texts_Block2[4].text,Fontsize).Width));
            }
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will update the text objects for section 14
          TextObj(0,"Event Currency Text",Texts_Block2[4].subtext("Event Currency Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[4].subtext("Event Currency Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Event Currency",Texts_Block2[4].subtext("Event Currency"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 15*/
          Y_start+=GetText(Texts_Block2[4].text,Fontsize).Height;//Re-adjust Y distance
          X_start=2;//Reset X distance
          //-- Check if the background object x-size for section 15 is the same size as section 15 text width
          if(ObjectGetInteger(0,"Event Importance background",OBJPROP_XSIZE)!=GetText(Texts_Block2[5].text,Fontsize).Width)
            {
             //-- Will re-adjust background object to any changes of section 15 text width
             ObjectSetInteger(0,"Event Importance background",OBJPROP_XSIZE,long(GetText(Texts_Block2[5].text,Fontsize).Width));
            }
          Y_start+=3;//Re-adjust Y distance
          X_start+=2;//Re-adjust X distance
          //--- Set text object color depending if the chart color mode is LightMode or not
          TextObj_color=(isLightMode)?clrBlack:clrWheat;
          //-- Will update the text objects for section 15
          TextObj(0,"Importance Text",Texts_Block2[5].subtext("Importance Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          X_start+=GetText(Texts_Block2[5].subtext("Importance Text"),Fontsize).Width;//Re-adjust X distance
          //--- Set text object color depending on upcoming news event's Importance
          TextObj_color=EventColor;
          TextObj(0,"Importance",Texts_Block2[5].subtext("Importance"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          /* Create objects # section 16*/
          if(is_spread)//Check whether to display spread information
            {
             Y_start+=GetText(Texts_Block2[5].text,Fontsize).Height;//Re-adjust Y distance
             X_start=2;//Reset X distance
             //-- Check if the background object x-size for section 16 is the same size as section 16 text width
             if(ObjectGetInteger(0,"Spread background",OBJPROP_XSIZE)!=GetText(Texts_Block2[6].text,Fontsize).Width)
               {
                //-- Will re-adjust background object to any changes of section 16 text width
                ObjectSetInteger(0,"Spread background",OBJPROP_XSIZE,long(GetText(Texts_Block2[6].text,Fontsize).Width));
               }
             Y_start+=3;//Re-adjust Y distance
             X_start+=2;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             color Spread_clr=CSymbol.SpreadColor();
             //-- Will create the text object for the Symbol's name
             X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             //-- Will update the text objects for section 16
             TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending if the chart color mode is LightMode or not
             TextObj_color=(isLightMode)?clrBlack:clrWheat;
             TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
            }
         }
       else
          /* Create objects # section 16*/
          if(is_spread)//Check whether to display spread information
            {
             Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;//Re-adjust Y distance
             X_start=2;//Reset X distance
             //-- Check if the background object x-size for section 16 is the same size as section 16 text width
             if(ObjectGetInteger(0,"Spread background",OBJPROP_XSIZE)!=GetText(Texts_Block2[6].text,Fontsize).Width)
               {
                //-- Will re-adjust background object to any changes of section 16 text width
                ObjectSetInteger(0,"Spread background",OBJPROP_XSIZE,long(GetText(Texts_Block2[6].text,Fontsize).Width));
               }
             Y_start+=3;//Re-adjust Y distance
             X_start+=2;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             color Spread_clr=CSymbol.SpreadColor();
             //-- Will create the text object for the Symbol's name
             X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             //-- Will update the text objects for section 16
             TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending if the chart color mode is LightMode or not
             TextObj_color=(isLightMode)?clrBlack:clrWheat;
             TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
             X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance
             //--- Set text object color depending on spread rating
             TextObj_color=Spread_clr;
             TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize);
            }
      }

    Функция ниже создаст объект события на графике со всеми доступными новостными событиями за текущий день.

    Раздел 17

    //+------------------------------------------------------------------+
    //|will create chart event objects                                   |
    //+------------------------------------------------------------------+
    void CCommonGraphics::NewsEvent()
      {
       if(!is_events||(MQLInfoInteger(MQL_TESTER)&&!MQLInfoInteger(MQL_VISUAL_MODE)))
         {return;}//exit if in strategy tester and not in visual mode or is_events variable is false
    //--- Retrieve news events for the current Daily period into array CalendarArray
       NewsObj.EconomicDetailsMemory(CalendarArray,iTime(Symbol(),PERIOD_D1,0));
    //--- Iterate through all events in CalendarArray
       for(uint i=0;i<CalendarArray.Size();i++)
         {
          //--- Create event object with the news properties
          EventObj(0,CalendarArray[i].EventName+" "+CalendarArray[i].CountryName+" "+CalendarArray[i].EventDate,
                   CalendarArray[i].EventName+"["+CalendarArray[i].CountryName+"]",StringToTime(CalendarArray[i].EventDate));
         }
    //--- Refresh the chart/ update the chart
       ChartRefresh();
      }


    Класс Trade Management

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

    #include <Trade\Trade.mqh>
    #include <Trade\OrderInfo.mqh>
    #include <Trade\SymbolInfo.mqh>
    #include "RiskManagement.mqh"
    #include "TimeManagement.mqh"
    //+------------------------------------------------------------------+
    //|TradeManagement class                                             |
    //+------------------------------------------------------------------+
    class CTradeManagement:CRiskManagement
      {
    private:
       CTrade            Trade;//Trade class object
       CSymbolProperties CSymbol;//SymbolProperties class object
       CTimeManagement   CTime;//TimeManagement class object
       bool              TradeResult;//boolean to store trade result
       double            mySL;//double variable to store Stoploss
       double            myTP;//double variable to store Takeprofit
    public:
       //--- Class constructor
                         CTradeManagement(string SYMBOL=NULL)
         {
          //--- Set symbol name
          CSymbol.SetSymbolName(SYMBOL);
         }
       //--- Class destructor
                        ~CTradeManagement(void) {}
       //--- Will retrieve if there are any open trades
       bool              OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL);
       //--- Will retrieve if there are any deals
       bool              OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open buy trade
       bool              Buy(double SL,double TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open buy trade with integer SL
       bool              Buy(int SL,double TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open buy trade with integer TP
       bool              Buy(double SL,int TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open buy trade with integer SL & TP
       bool              Buy(int SL,int TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open sell trade
       bool              Sell(double SL,double TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open sell trade with integer SL
       bool              Sell(int SL,double TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open sell trade with integer TP
       bool              Sell(double SL,int TP,ulong Magic,string COMMENT=NULL);
       //--- Will attempt open sell trade with integer SL & TP
       bool              Sell(int SL,int TP,ulong Magic,string COMMENT=NULL);
      };

    Функция ниже проверит наличие открытых позиций с определенным типом позиции (Buy/Sell), магическим числом и комментарием (сведениями о позиции). Мы будем использовать эту функцию, чтобы проверить, открыли ли мы уже сделку на момент появления новости, чтобы избежать открытия дополнительных/ненужных сделок.

    //+------------------------------------------------------------------+
    //|Will retrieve if there are any open trades                        |
    //+------------------------------------------------------------------+
    bool CTradeManagement::OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL)
      {
    //--- Iterate through all open positions
       for(int i=0; i<PositionsTotal(); i++)
         {
          //--- Check if Position ticket is above zero
          if(PositionGetTicket(i)>0)
            {
             //--- Check if the Position's Symbol,Magic,Type,Comment is correct
             if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()&&PositionGetInteger(POSITION_MAGIC)==Magic
                &&PositionGetInteger(POSITION_TYPE)==Type&&PositionGetString(POSITION_COMMENT)==COMMENT)
               {
                //--- Return true when there is an open position
                return true;
               }
            }
         }
    //--- No open positions found.
       return false;
      }

    Функция OpenedDeal проверит наличие открытых сделок на покупку/продажу. Эта функция нужна нам, чтобы помешать нам (советнику) открывать новые сделки, если сделка была закрыта во время новостного события. Без этой функции советник откроет сделку на покупку, например, во время выхода данных по занятости в несельскохозяйственном секторе (NFP). Если сделка будет закрыта из-за волатильности, мы не хотим, чтобы советник открыл еще одну. Это происходит потому, что мы уже совершили сделку по событию, и нет необходимости в дополнительных сделках, которые могут привести к убыткам. 

    //+------------------------------------------------------------------+
    //|Will retrieve if there are any deals                              |
    //+------------------------------------------------------------------+
    bool CTradeManagement::OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL)
      {
    //--- Check History starting from 2 minutes ago
       if(HistorySelect(CTime.TimeMinusOffset(TimeTradeServer(),CTime.MinutesS(2)),TimeTradeServer()))
         {
          //--- Iterate through all history deals
          for(int i=0; i<HistoryDealsTotal(); i++)
            {
             //--- Assign history deal ticket
             ulong ticket = HistoryDealGetTicket(i);
             //--- Check if ticket is more than zero
             if(ticket>0)
               {
                //--- Check if the Deal's Symbol,Magic,Type,Comment is correct
                if(HistoryDealGetString(ticket,DEAL_SYMBOL)==CSymbol.GetSymbolName()&&
                   HistoryDealGetInteger(ticket,DEAL_MAGIC)==Magic&&HistoryDealGetInteger(ticket,DEAL_TYPE)==Type
                   &&HistoryDealGetString(ticket,DEAL_COMMENT)==COMMENT)
                  {
                   //--- Return true when there are any deals
                   return true;
                  }
               }
            }
         }
    //--- No deals found.
       return false;
      }

    Функция ниже открывает рыночные ордера на покупку.

    //+------------------------------------------------------------------+
    //|Will attempt open buy trade                                       |
    //+------------------------------------------------------------------+
    bool CTradeManagement::Buy(double SL,double TP,ulong Magic,string COMMENT=NULL)
      {
    //--- Normalize the SL Price
       CSymbol.NormalizePrice(SL);
    //--- Normalize the TP Price
       CSymbol.NormalizePrice(TP);
    //--- Set the order type for Risk management calculation
       SetOrderType(ORDER_TYPE_BUY);
    //--- Set open price for Risk management calculation
       OpenPrice = CSymbol.Ask();
    //--- Set close price for Risk management calculation
       ClosePrice = SL;
    //--- Set Trade magic number
       Trade.SetExpertMagicNumber(Magic);
    //--- Check if there are any open trades or opened deals already
       if(!OpenTrade(POSITION_TYPE_BUY,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT))
         {
          //--- Iterate through the Lot-sizes if they're more than max-lot
          for(double i=Volume();i>=CSymbol.LotsMin();i-=CSymbol.LotsMax())
            {
             //--- normalize Lot-size
             NormalizeLotsize(i);
             //--- Open trade with a Lot-size not more than max-lot
             TradeResult = Trade.Buy((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,CSymbol.GetSymbolName(),CSymbol.Ask(),SL,TP,COMMENT);
             //--- Check if trade failed.
             if(!TradeResult)
               {
                return TradeResult;
               }
            }
         }
       else
         {
          //--- Trade failed because there is an open trade or opened deal
          return false;
         }
    //--- Return trade result.
       return TradeResult;
      }

    Функция ниже открывает рыночные ордера на продажу.

    //+------------------------------------------------------------------+
    //|Will attempt open sell trade                                      |
    //+------------------------------------------------------------------+
    bool CTradeManagement::Sell(double SL,double TP,ulong Magic,string COMMENT=NULL)
      {
    //--- Normalize the SL Price
       CSymbol.NormalizePrice(SL);
    //--- Normalize the TP Price
       CSymbol.NormalizePrice(TP);
    //--- Set the order type for Risk management calculation
       SetOrderType(ORDER_TYPE_SELL);
    //--- Set open price for Risk management calculation
       OpenPrice = CSymbol.Bid();
    //--- Set close price for Risk management calculation
       ClosePrice = SL;
    //--- Set Trade magic number
       Trade.SetExpertMagicNumber(Magic);
    //--- Check if there are any open trades or opened deals already
       if(!OpenTrade(POSITION_TYPE_SELL,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_SELL,Magic,COMMENT))
         {
          //--- Iterate through the Lot-sizes if they're more than max-lot
          for(double i=Volume();i>=CSymbol.LotsMin();i-=CSymbol.LotsMax())
            {
             //--- normalize Lot-size
             NormalizeLotsize(i);
             //--- Open trade with a Lot-size not more than max-lot
             TradeResult = Trade.Sell((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,CSymbol.GetSymbolName(),CSymbol.Bid(),SL,TP,COMMENT);
             //--- Check if trade failed.
             if(!TradeResult)
               {
                return TradeResult;
               }
            }
         }
       else
         {
          //--- Trade failed because there is an open trade or opened deal
          return false;
         }
    //--- Return trade result.
       return TradeResult;
      }


    Советник для новостной торговли

    У нас есть новые входные данные для советника. Пояснения приведены во введении.

    //--- width and height of the canvas (used for drawing)
    #define IMG_WIDTH  200
    #define IMG_HEIGHT 100
    //--- enable to set color format
    ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_XRGB_NOALPHA;
    //--- drawing array (buffer)
    uint ExtImg[IMG_WIDTH*IMG_HEIGHT];
    
    #include "News.mqh"
    CNews NewsObject;//Class object for News
    #include "TimeManagement.mqh"
    CTimeManagement CTM;//Class object for Time Management
    #include "WorkingWithFolders.mqh"
    CFolders Folder;//Class object for Folders
    #include "ChartProperties.mqh"
    CChartProperties Chart;//Class object for Chart Properties
    #include "RiskManagement.mqh"
    CRiskManagement CRisk;//Class object for Risk Management
    #include "CommonGraphics.mqh"
    CCommonGraphics *CGraphics;//Class pointer object for Common Graphics
    CCandleProperties *CP;//Class pointer object for Candle Properties
    #include "TradeManagement.mqh"
    CTradeManagement Trade;//Class object for Trade Management
    
    //--- used to separate Input Menu
    enum iSeparator
      {
       Delimiter//__________________________
      };
    
    //--- for chart color Mode selection
    enum DisplayMode
      {
       Display_LightMode,//LIGHT MODE
       Display_DarkMode//DARK MODE
      };
    
    sinput group "+--------|   DISPLAY   |--------+";
    sinput DisplayMode iDisplayMode=Display_LightMode;//CHART COLOUR MODE
    sinput Choice iDisplay_NewsInfo=Yes;//DISPLAY NEWS INFO
    sinput Choice iDisplay_EventObj=Yes;//DISPLAY EVENT OBJ
    sinput Choice iDisplay_Spread=Yes;//DISPLAY SPREAD RATING
    sinput Choice iDisplay_Date=Yes;//DISPLAY DATE
    sinput group "";
    sinput group "+--------|   DST SCHEDULE   |--------+";
    input DSTSchedule ScheduleDST=AutoDst_Selection;//SELECT DST OPTION
    sinput iSeparator iCustomSchedule=Delimiter;//__________________________
    sinput iSeparator iCustomScheduleL=Delimiter;//CUSTOM DST
    input DST_type CustomSchedule=DST_NONE;//SELECT CUSTOM DST
    sinput group "";
    sinput group "+--------| RISK MANAGEMENT |--------+";
    input RiskOptions RISK_Type=MINIMUM_LOT;//SELECT RISK OPTION
    input RiskFloor RISK_Mini=RiskFloorMin;//RISK FLOOR
    input double RISK_Mini_Percent=75;//MAX-RISK [100<-->0.01]%
    input RiskCeil  RISK_Maxi=RiskCeilMax;//RISK CEILING
    sinput iSeparator iRisk_1=Delimiter;//__________________________
    sinput iSeparator iRisk_1L=Delimiter;//PERCENTAGE OF [BALANCE | FREE-MARGIN]
    input double Risk_1_PERCENTAGE=3;//[100<-->0.01]%
    sinput iSeparator iRisk_2=Delimiter;//__________________________
    sinput iSeparator iRisk_2L=Delimiter;//AMOUNT PER [BALANCE | FREE-MARGIN]
    input double Risk_2_VALUE=1000;//[BALANCE | FREE-MARGIN]
    input double Risk_2_AMOUNT=10;//EACH AMOUNT
    sinput iSeparator iRisk_3=Delimiter;//__________________________
    sinput iSeparator iRisk_3L=Delimiter;//LOTSIZE PER [BALANCE | FREE-MARGIN]
    input double Risk_3_VALUE=1000;//[BALANCE | FREE-MARGIN]
    input double Risk_3_LOTSIZE=0.1;//EACH LOTS(VOLUME)
    sinput iSeparator iRisk_4=Delimiter;//__________________________
    sinput iSeparator iRisk_4L=Delimiter;//CUSTOM LOTSIZE
    input double Risk_4_LOTSIZE=0.01;//LOTS(VOLUME)
    sinput iSeparator iRisk_5=Delimiter;//__________________________
    sinput iSeparator iRisk_5L=Delimiter;//PERCENTAGE OF MAX-RISK
    input double Risk_5_PERCENTAGE=1;//[100<-->0.01]%
    sinput group "";
    sinput group "+--------| NEWS SETTINGS |--------+";
    input Calendar_Importance iImportance=Calendar_Importance_High;//CALENDAR IMPORTANCE
    input Event_Frequency iFrequency=Event_Frequency_ALL;//EVENT FREQUENCY
    input Event_Sector iSector=Event_Sector_ALL;//EVENT SECTOR
    input Event_Type iType=Event_Type_Indicator;//EVENT TYPE
    input Event_Currency iCurrency=Event_Currency_Symbol;//EVENT CURRENCY
    sinput group "";
    sinput group "+--------| TRADE SETTINGS |--------+";
    input uint iStoploss=500;//STOPLOSS [0=NONE]
    input uint iTakeprofit=500;//TAKEPROFIT [0=NONE]
    input uint iSecondsPreEvent=5;//PRE-ENTRY SEC
    input DayOfTheWeek TradingDay=AllDays;//TRADING DAY OF WEEK
    sinput group "";
    //--- to keep track of start-up time
    datetime Startup_date;

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

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- Assign if in LightMode or not
       isLightMode=(iDisplayMode==Display_LightMode)?true:false;
    //--- call function for common initialization procedure
       InitCommon();
    //--- store Init result
       int InitResult;
       if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester
         {
          //--- initialization procedure outside strategy tester
          InitResult=InitNonTester();
         }
       else
         {
          //--- initialization procedure inside strategy tester
          InitResult=InitTester();
         }
    //--- Create DB in memory
       NewsObject.CreateEconomicDatabaseMemory();
    //--- Initialize Common graphics class pointer object
       CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread),Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj));
       CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects
    //--- Initialize Candle properties pointer object
       CP = new CCandleProperties();
    //--- Store start-up time.
       Startup_date = TimeTradeServer();
    //--- return Init result
       return InitResult;
      }

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

    //+------------------------------------------------------------------+
    //|function for common initialization procedure                      |
    //+------------------------------------------------------------------+
    void InitCommon()
      {
    //Initializing CRiskManagement variable for Risk options
       RiskProfileOption = RISK_Type;
    //Initializing CRiskManagement variable for Risk floor
       RiskFloorOption = RISK_Mini;
    //Initializing CRiskManagement variable for RiskFloorMax
       RiskFloorPercentage = (RISK_Mini_Percent>100)?100:
                             (RISK_Mini_Percent<0.01)?0.01:RISK_Mini_Percent;//Percentage cannot be more than 100% or less than 0.01%
    //Initializing CRiskManagement variable for Risk ceiling
       RiskCeilOption = RISK_Maxi;
    //Initializing CRiskManagement variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
       Risk_Profile_1 = (Risk_1_PERCENTAGE>100)?100:
                        (Risk_1_PERCENTAGE<0.01)?0.01:Risk_1_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01%
    //Initializing CRiskManagement variables for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
       Risk_Profile_2.RiskAmountBoF = Risk_2_VALUE;
       Risk_Profile_2.RiskAmount = Risk_2_AMOUNT;
    //Initializing CRiskManagement variables for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
       Risk_Profile_3.RiskLotBoF = Risk_3_VALUE;
       Risk_Profile_3.RiskLot = Risk_3_LOTSIZE;
    //Initializing CRiskManagement variable for Risk option (CUSTOM LOTSIZE)
       Risk_Profile_4 = Risk_4_LOTSIZE;
    //Initializing CRiskManagement variable for Risk option (PERCENTAGE OF MAX-RISK)
       Risk_Profile_5 = (Risk_5_PERCENTAGE>100)?100:
                        (Risk_5_PERCENTAGE<0.01)?0.01:Risk_5_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01%
    //--- Initializing DST Schedule variables
       MyDST = ScheduleDST;
       MySchedule = CustomSchedule;
    //--- Initializing News filter variables
       myFrequency=iFrequency;
       myImportance=iImportance;
       mySector=iSector;
       myType=iType;
       myCurrency=iCurrency;
       Chart.ChartRefresh();//Load chart configurations
      }

    Функция ниже будет инициализирована только для обычной торговой среды.

    //+------------------------------------------------------------------+
    //|function for initialization procedure outside strategy tester     |
    //+------------------------------------------------------------------+
    int InitNonTester()
      {
    //--- Check if in Strategy tester!
       if(MQLInfoInteger(MQL_TESTER))
         {
          //--- Initialization failed.
          return(INIT_SUCCEEDED);
         }
    //--- create OBJ_BITMAP_LABEL object for drawing
       ObjectCreate(0,"STATUS",OBJ_BITMAP_LABEL,0,0,0);
       ObjectSetInteger(0,"STATUS",OBJPROP_XDISTANCE,5);
       ObjectSetInteger(0,"STATUS",OBJPROP_YDISTANCE,22);
    //--- specify the name of the graphical resource
       ObjectSetString(0,"STATUS",OBJPROP_BMPFILE,"::PROGRESS");
       uint   w,h;          // variables for receiving text string sizes
       uint    x,y;          // variables for calculation of the current coordinates of text string anchor points
       /*
       In the Do while loop below, the code will check if the terminal is connected to the internet.
       If the the program is stopped the loop will break, if the program is not stopped and the terminal
       is connected to the internet the function CreateEconomicDatabase will be called from the News.mqh header file's
       object called NewsObject and the loop will break once called.
       */
       bool done=false;
       do
         {
          //--- clear the drawing buffer array
          ArrayFill(ExtImg,0,IMG_WIDTH*IMG_HEIGHT,0);
    
          if(!TerminalInfoInteger(TERMINAL_CONNECTED))
            {
             //-- integer dots used as a loading animation
             static int dots=0;
             //--- set the font
             TextSetFont("Arial",-150,FW_EXTRABOLD,0);
             TextGetSize("Waiting",w,h);//get text width and height values
             //--- calculate the coordinates of the 'Waiting' text
             x=10;//horizontal alignment
             y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
             //--- output the 'Waiting' text to ExtImg[] buffer
             TextOut("Waiting",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CSymbol.Background()),clr_format);
             //--- calculate the coordinates for the dots after the 'Waiting' text
             x=w+13;//horizontal alignment
             y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
             TextSetFont("Arial",-160,FW_EXTRABOLD,0);
             //--- output of dots to ExtImg[] buffer
             TextOut(StringSubstr("...",0,dots),x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CSymbol.Background()),clr_format);
             //--- update the graphical resource
             ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format);
             //--- force chart update
             Chart.Redraw();
             dots=(dots==3)?0:dots+1;
             //-- Notify user that program is waiting for connection
             Print("Waiting for connection...");
             Sleep(500);
             continue;
            }
          else
            {
             //--- set the font
             TextSetFont("Arial",-120,FW_EXTRABOLD,0);
             TextGetSize("Getting Ready",w,h);//get text width and height values
             x=20;//horizontal alignment
             y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
             //--- output the text 'Getting Ready...' to ExtImg[] buffer
             TextOut("Getting Ready...",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CSymbol.Background()),clr_format);
             //--- update the graphical resource
             ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format);
             //--- force chart update
             Chart.Redraw();
             //-- Notify user that connection is successful
             Print("Connection Successful!");
             NewsObject.CreateEconomicDatabase();//calling the database create function
             done=true;
            }
         }
       while(!done&&!IsStopped());
    //-- Delete chart object
       ObjectDelete(0,"STATUS");
    //-- force chart to update
       Chart.Redraw();
    //--- Initialization succeeded.
       return(INIT_SUCCEEDED);
      }

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

    //+------------------------------------------------------------------+
    //|function for initialization procedure inside strategy tester      |
    //+------------------------------------------------------------------+
    int InitTester()
      {
    //--- Check if not in Strategy tester!
       if(!MQLInfoInteger(MQL_TESTER))
         {
          //--- Initialization failed.
          return(INIT_SUCCEEDED);
         }
    //Checks whether the database file exists
       if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))
         {
          //--- Warning messages
          Print("Necessary Files Do not Exist!");
          Print("Run Program outside of the Strategy Tester");
          Print("Necessary Files Should be Created First");
          //--- Initialization failed.
          return(INIT_FAILED);
         }
       else
         {
          //Checks whether the latest database date includes the time and date being tested
          datetime latestdate = CTM.TimeMinusOffset(NewsObject.GetLatestNewsDate(),CTM.DaysS());//Day before the latest recorded time in the database
          if(latestdate<TimeTradeServer())
            {
             Print("Necessary Files outdated!");
             Print("To Update Files: Run Program outside of the Strategy Tester");
            }
          Print("Database Dates End at: ",latestdate);
          PrintFormat("Dates after %s will not be available for backtest",TimeToString(latestdate));
         }
    //--- Initialization succeeded.
       return(INIT_SUCCEEDED);
      }

    Функция ниже будет запускаться на каждом новом тике.

    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
      {
    //--- Run procedures
       Execution();
      }

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

    //+------------------------------------------------------------------+
    //|Execute program procedures                                        |
    //+------------------------------------------------------------------+
    void Execution()
      {
    //--- Update realtime Graphic every 1 min
       if(CP.NewCandle(1,PERIOD_M1))
         {
          CGraphics.Block_2_Realtime(iSecondsPreEvent);
         }
    //--- function to open trades
       EnterTrade();
    //--- Check if not start-up date
       if(!CTM.DateisToday(Startup_date))
         {
          //--- Run every New Daily Candle
          if(CP.NewCandle(2,PERIOD_D1))
            {
             //--- Check if not in strategy tester
             if(!MQLInfoInteger(MQL_TESTER))
               {
                //--- Update/Create DB in Memory
                NewsObject.CreateEconomicDatabaseMemory();
               }
             CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create/Re-create chart objects
             //--- Update Realtime Graphics
             CGraphics.Block_2_Realtime(iSecondsPreEvent);
            }
          //--- Check if not in strategy tester
          if(!MQLInfoInteger(MQL_TESTER))
            {
             //--- Run every New Hourly Candle
             if(CP.NewCandle(3,PERIOD_H1))
               {
                //--- Check if DB in Storage needs an update
                if(NewsObject.UpdateRecords())
                  {
                   //--- initialization procedure outside strategy tester
                   InitNonTester();
                  }
               }
            }
         }
       else
         {
          //--- Run every New Daily Candle
          if(CP.NewCandle(4,PERIOD_D1))
            {
             //--- Update Event objects on chart
             CGraphics.NewsEvent();
            }
         }
      }

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

    //+------------------------------------------------------------------+
    //|function to open trades                                           |
    //+------------------------------------------------------------------+
    void EnterTrade()
      {
    //--- static variable for storing upcoming event Impact value
       static ENUM_CALENDAR_EVENT_IMPACT Impact=CALENDAR_IMPACT_NA;
    //--- Check if Upcoming news date has passed and if upcoming news is not null and if new minute candle has formed.
       if(datetime(UpcomingNews.EventDate)<TimeTradeServer()&&UpcomingNews.CountryName!=NULL&&CP.NewCandle(5,PERIOD_M1))
         {
          //--- Update for next upcoming news
          NewsObject.EconomicNextEvent();
          //--- Get impact value for upcoming news
          Impact=NewsObject.GetImpact();
         }
    //--- Check if upcoming news date is about to occur and if it is the trading day of week
       if(CTM.TimePreEvent(CTM.TimeMinusOffset(datetime(UpcomingNews.EventDate),(iSecondsPreEvent==0)?1:iSecondsPreEvent)
                           ,datetime(UpcomingNews.EventDate))
          &&CTM.isDayOfTheWeek(TradingDay))
         {
          //--- Check each Impact value type
          switch(Impact)
            {
             //--- When Impact news is negative
             case CALENDAR_IMPACT_NEGATIVE:
                //--- Check if profit currency is news event currency
                if(UpcomingNews.EventCurrency==CSymbol.CurrencyProfit())
                  {
                   //--- Open buy trade with Event id as Magic number
                   Trade.Buy(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading");
                  }
                else
                  {
                   //--- Open sell trade with Event id as Magic number
                   Trade.Sell(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading");
                  }
                break;
             //--- When Impact news is positive
             case CALENDAR_IMPACT_POSITIVE:
                //--- Check if profit currency is news event currency
                if(UpcomingNews.EventCurrency==CSymbol.CurrencyProfit())
                  {
                   //--- Open sell trade with Event id as Magic number
                   Trade.Sell(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading");
                  }
                else
                  {
                   //--- Open buy trade with Event id as Magic number
                   Trade.Buy(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading");
                  }
                break;
             //--- Unknown
             default:
                break;
            }
         }
      }


    Заключение

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

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



    Видео




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

    Прикрепленные файлы |
    NewsTrading_Part3.zip (567.27 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
    laurhaq
    laurhaq | 13 сент. 2024 в 21:44

    Большое спасибо

    Лоран

    Jefferson Judge Metha
    Jefferson Judge Metha | 23 окт. 2024 в 20:33
    Спасибо за статью, очень хорошая.

    Однако, я не хочу визуализировать это

    Комментарии я не хочу визуализировать их.
    Kabelo Frans Mampa
    Kabelo Frans Mampa | 24 окт. 2024 в 08:07
    Jefferson Judge Metha #:
    Спасибо за статью, очень хорошая.

    Однако, я не хочу визуализировать это

    Комментарии я не хочу визуализировать их.
    Привет, не стесняйтесь изменять код по своему усмотрению. Если вам нужна помощь, я готов помочь.
    Разработка системы репликации (Часть 67): Совершенствуем индикатор управления Разработка системы репликации (Часть 67): Совершенствуем индикатор управления
    В данной статье мы рассмотрим, чего можно добиться с помощью небольшой доработки кода. Данная доработка направлена на упрощение нашего кода, более активное использование вызовов библиотеки MQL5 и, прежде всего, на то, чтобы сделать его гораздо более стабильным, безопасным и простым для использования в другом коде, который мы будем разрабатывать в будущем.
    Разрабатываем мультивалютный советник (Часть 22): Начало перехода на горячую замену настроек Разрабатываем мультивалютный советник (Часть 22): Начало перехода на горячую замену настроек
    Если мы взялись за автоматизацию проведения периодической оптимизации, то надо позаботиться и об автоматическом обновлении настроек советников, которые уже работают на торговом счёте. Также это должно позволять запускать советник в тестере стратегий и менять его настройки в рамках одного прохода.
    Нейросети в трейдинге: Двухмерные модели пространства связей (Chimera) Нейросети в трейдинге: Двухмерные модели пространства связей (Chimera)
    Откройте для себя инновационный фреймворк Chimera — двухмерную модель пространства состояний, использующую нейросети для анализа многомерных временных рядов. Этот метод предлагает высокую точность с низкими вычислительными затратами, превосходя традиционные подходы и архитектуры Transformer.
    Алгоритм поиска по кругу — Circle Search Algorithm (CSA) Алгоритм поиска по кругу — Circle Search Algorithm (CSA)
    В статье представлен новый метаэвристический алгоритм оптимизации CSA (Circle Search Algorithm), основанный на геометрических свойствах окружности. Алгоритм использует принцип движения точек по касательным для поиска оптимального решения, сочетая фазы глобального исследования и локальной эксплуатации.