English Deutsch 日本語
preview
Упрощаем торговлю на новостях (Часть 4): Повышаем производительность

Упрощаем торговлю на новостях (Часть 4): Повышаем производительность

MetaTrader 5Торговые системы | 23 мая 2025, 10:30
321 2
Kabelo Frans Mampa
Kabelo Frans Mampa

Введение

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

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


Класс Time Variables

Ранее этот класс использовался для объявления массива с фиксированным размером в 2000 индексов, который будет хранить время свечей и будет использоваться в классе Candle Properties для проверки того, равно ли это сохраненное время свечи текущему времени свечи, чтобы определить, сформировалась ли новая свеча или нет. Мы расширим этот класс, объявив перечисления для времени и функции, которые будут преобразовывать целочисленные значения в любое из объявленных перечислений, чтобы иметь контролируемый метод для работы с секундами, минутами и часами в других классах, которые будут включать этот класс.

Размещение класса показано ниже:

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
//--- Enumeration For Hours in a Day
enum HOURLY
  {
   H1=1,//01
   H2=2,//02
   H3=3,//03
   H4=4,//04
   H5=5,//05
   H6=6,//06
   H7=7,//07
   H8=8,//08
   H9=9,//09
   H10=10,//10
   H11=11,//11
   H12=12,//12
   H13=13,//13
   H14=14,//14
   H15=15,//15
   H16=16,//16
   H17=17,//17
   H18=18,//18
   H19=19,//19
   H20=20,//20
   H21=21,//21
   H22=22,//22
   H23=23,//23
   H24=0//00
  };

//--- Enumeration For Minutes in an Hour
enum MINUTELY
  {
   M0,//00
   M1,//01
   M2,//02
   M3,//03
   M4,//04
   M5,//05
   M6,//06
   M7,//07
   M8,//08
   M9,//09
   M10,//10
   M11,//11
   M12,//12
   M13,//13
   M14,//14
   M15,//15
   M16,//16
   M17,//17
   M18,//18
   M19,//19
   M20,//20
   M21,//21
   M22,//22
   M23,//23
   M24,//24
   M25,//25
   M26,//26
   M27,//27
   M28,//28
   M29,//29
   M30,//30
   M31,//31
   M32,//32
   M33,//33
   M34,//34
   M35,//35
   M36,//36
   M37,//37
   M38,//38
   M39,//39
   M40,//40
   M41,//41
   M42,//42
   M43,//43
   M44,//44
   M45,//45
   M46,//46
   M47,//47
   M48,//48
   M49,//49
   M50,//50
   M51,//51
   M52,//52
   M53,//53
   M54,//54
   M55,//55
   M56,//56
   M57,//57
   M58,//58
   M59//59
  };

//--- Enumeration For Seconds Pre-event datetime
enum PRESECONDLY
  {
   Pre_S30=30//30
  };

//--- Enumeration For Seconds in a Minute
enum SECONDLY
  {
   S0,//00
   S1,//01
   S2,//02
   S3,//03
   S4,//04
   S5,//05
   S6,//06
   S7,//07
   S8,//08
   S9,//09
   S10,//10
   S11,//11
   S12,//12
   S13,//13
   S14,//14
   S15,//15
   S16,//16
   S17,//17
   S18,//18
   S19,//19
   S20,//20
   S21,//21
   S22,//22
   S23,//23
   S24,//24
   S25,//25
   S26,//26
   S27,//27
   S28,//28
   S29,//29
   S30,//30
   S31,//31
   S32,//32
   S33,//33
   S34,//34
   S35,//35
   S36,//36
   S37,//37
   S38,//38
   S39,//39
   S40,//40
   S41,//41
   S42,//42
   S43,//43
   S44,//44
   S45,//45
   S46,//46
   S47,//47
   S48,//48
   S49,//49
   S50,//50
   S51,//51
   S52,//52
   S53,//53
   S54,//54
   S55,//55
   S56,//56
   S57,//57
   S58,//58
   S59//59
  };
//+------------------------------------------------------------------+
//|TimeVariables class                                               |
//+------------------------------------------------------------------+
class CTimeVariables
  {
private:
   //--- Array to store candlestick times
   datetime          CandleTime[2000];
public:
                     CTimeVariables(void);
   //--- Set datetime value for an array index
   void              SetTime(uint index,datetime time);
   //--- Get datetime value for an array index
   datetime          GetTime(uint index);
   //--- Convert Integer to the Enumeration HOURLY
   HOURLY            Hourly(uint Hour);
   //--- Convert Integer to the Enumeration MINUTELY
   MINUTELY          Minutely(uint Minute);
   //--- Convert Integer to the Enumeration SECONDLY
   SECONDLY          Secondly(uint Second);
  };
//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CTimeVariables::CTimeVariables()
  {
//--- Set default datetime values for all indexes in array CandleTime
   for(uint i=0; i<CandleTime.Size(); i++)
     {
      CandleTime[i]=D'1970.01.01';
     }
  }

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

//+------------------------------------------------------------------+
//|Get the datetime value for an array index in array CandleTime     |
//+------------------------------------------------------------------+
datetime CTimeVariables::GetTime(uint index)
  {
   return (index>=0&&index<CandleTime.Size())?CandleTime[index]:datetime(0);
  }

//+------------------------------------------------------------------+
//|Convert Integer to the Enumeration HOURLY                         |
//+------------------------------------------------------------------+
HOURLY CTimeVariables::Hourly(uint Hour)
  {
   return (Hour>23)?HOURLY(0):HOURLY(Hour);
  }

//+------------------------------------------------------------------+
//|Convert Integer to the Enumeration MINUTELY                       |
//+------------------------------------------------------------------+
MINUTELY CTimeVariables::Minutely(uint Minute)
  {
   return (Minute>59)?MINUTELY(0):MINUTELY(Minute);
  }

//+------------------------------------------------------------------+
//|Convert Integer to the Enumeration SECONDLY                       |
//+------------------------------------------------------------------+
SECONDLY CTimeVariables::Secondly(uint Second)
  {
   return (Second>59)?SECONDLY(0):SECONDLY(Second);
  }
//+------------------------------------------------------------------+

Приведенный ниже код определяет перечисление с именем HOURLY. Перечисление (enum) — это определяемый пользователем тип данных, состоящий из набора именованных целочисленных констант. Его часто используют, когда необходимо представить определенный набор значений с помощью осмысленных имен, чтобы сделать код более читабельным.

enum HOURLY
  {
   H1=1,  // Represents Hour 01
   H2=2,  // Represents Hour 02
   H3=3,  // Represents Hour 03
   H4=4,  // Represents Hour 04
   H5=5,  // Represents Hour 05
   H6=6,  // Represents Hour 06
   H7=7,  // Represents Hour 07
   H8=8,  // Represents Hour 08
   H9=9,  // Represents Hour 09
   H10=10,  // Represents Hour 10
   H11=11,  // Represents Hour 11
   H12=12,  // Represents Hour 12
   H13=13,  // Represents Hour 13
   H14=14,  // Represents Hour 14
   H15=15,  // Represents Hour 15
   H16=16,  // Represents Hour 16
   H17=17,  // Represents Hour 17
   H18=18,  // Represents Hour 18
   H19=19,  // Represents Hour 19
   H20=20,  // Represents Hour 20
   H21=21,  // Represents Hour 21
   H22=22,  // Represents Hour 22
   H23=23,  // Represents Hour 23
   H24=0    // Represents Hour 00 (Midnight)
  };

Значения:

Каждое значение в перечислении соответствует определенному часу суток, начиная с H1=1 для первого часа, H2=2 и так далее, до H23=23 для 23-го часа. H24=0 используется для обозначения полуночи (00:00 часов). Вы можете использовать в своем коде такие имена, как H1, H2 и так далее, чтобы сделать его более интуитивно понятным при работе с данными, связанными со временем. Вместо того чтобы использовать только цифры для часов, я предпочитаю использовать значения HOURLY.

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

HOURLY current_hour = H10; // Setting the current hour to 10:00 AM
if (current_hour == H10) 
{
   Print("The current hour is 10:00 AM");
}

В приведенном выше примере current_hour присваивается значение H10, и он проверяет, является ли текущий час H10, выводя соответствующее сообщение. Это облегчает чтение и понимание кода, особенно при работе с бесчисленными временными операциями.

Перечисление для минут - MINUTELY.

Это перечисление определяет константы для каждой минуты часа, от M0 (00) до M59 (59).

enum MINUTELY
{
   M0, M1, M2, M3, ..., M59
};

Вместо работы исключительно с целыми числами (например, 0, 1, 2) мы используем осмысленные метки, такие как M0, M1 и так далее.

Пример:

  • M0 представляет минуту 00.
  • M30 представляет тридцатую минуту.

Перечисление секунд до события - PRESECONDLY.

Это перечисление определяет секунды до события. Например, Pre_S30 используется для обозначения 30 секунд до наступления новостного события. Значение фиксированы для предварительного ввода новостного события. Например, если новость выйдет в 14:00, мы будем рассматривать возможность входа в сделку только в 13:59:30. Это означает, что прежняя возможность войти в сделку за 5 секунд до события больше не будет доступна.

У внесенного мной изменения есть свои плюсы и минусы.

Главный недостаток:

  • Меньше возможности для настроек: это недостаток, поскольку пользователь/трейдер может захотеть открывать сделки только за 5 секунд до события из-за случайной волатильности, которая возникает перед наступлением важного события. Это означает, что стоп-лосс может сработать просто на основе того, насколько рано вы входите в сделку до наступления события. Таким образом, несколько секунд могут существенно повлиять на вашу прибыльность при торговле на новостях.

Главное преимущество:

  • Фиксированный график: Это преимущество, поскольку мы можем настроить советник на проверку возможности входа в сделку только с интервалом в 30 секунд, что значительно улучшит скорость тестирования на истории. Нам не приходится так часто использовать ресурсы компьютера, что сокращает время тестирования. В случае, когда мы хотим войти в сделку за 5 секунд до события, советник должен каждые 5 секунд или 1 секунду проверять, подходящее ли это время для размещения сделки. Это потребляет ресурсы компьютера, замедляя его работу при тестировании на истории.

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

Ликвидность

Вот факторы, которые делают неблагоприятным вход в торговлю неликвидными активами/на неликвидных рынках:

  • Проскальзывание: Цены актива или символа меняются невероятно часто, поэтому цена, по которой пользователь/трейдер хотел войти на рынок/выйти с него, больше недоступна, и используется менее выгодная/ожидаемая цена. Непредсказуемость — главный недостаток проскальзывания.
  • Спреды: Широкие спреды встречаются чаще, что ограничивает потенциальную прибыльность трейдера.
  • Котировки отсутствуют/недоступны: Сделки могут быть полностью прекращены, что означает, что трейдер потеряет возможность извлечь выгоду из потенциально прибыльного движения актива/рынка.

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

enum PRESECONDLY
{
   Pre_S30 = 30 // 30 seconds before an event
};

Перечисление для секунд - SECONDLY.

Подобно MINUTELY, это перечисление представляет каждую секунду минуты, начиная от S0 (00 секунд) до S59 (59 секунд).

enum SECONDLY
{
   S0, S1, S2, ..., S59
};

Пример:

  • S0 представляет секунду 00.
  • S30 представляет тридцатую секунду.

Функция ниже преобразует целое число часа (Hour) в соответствующее значение из перечисления HOURLY. Она гарантирует, что переданное значение (в переменной Hour) является допустимым (в пределах от 0 до 23), а затем возвращает соответствующее значение перечисления HOURLY.

  • Тип возвращаемого значения: HOURLY – функция возвращает значение из перечисления HOURLY, которое мы обсуждали ранее. Это перечисление содержит значения, соответствующие 24 часам суток, где H1=1, H2=2, ..., H23=23 и H24=0.
  • Название функции: Hourly. Это метод, принадлежащий классу CTimeVariables, на что указывает оператор разрешения области видимости (::). Итак, это часть класса CTimeVariables.
  • Параметры:

  • uint Hour - беззнаковое целое число, представляющее час, которое передается в функцию в качестве параметра.

HOURLY CTimeVariables::Hourly(uint Hour)
{
   return (Hour>23)?HOURLY(0):HOURLY(Hour);
}

Логика функции:

В этой строке используется тернарный оператор (? :), что является сокращением для оператора if-else. Тернарный оператор проверяет условие и возвращает одно из двух значений в зависимости от того, является ли условие истинным или ложным.

Условие: (Hour > 23)

  • Проверяет, превышает ли значение часа 23. Поскольку допустимый диапазон часов составляет от 0 до 23 (24 часа в сутках), любое значение больше 23 является недопустимым.

При true: HOURLY(0)
  • Если Hour > 23 (недопустимое значение часа), функция вернет HOURLY(0), что соответствует H24=0 (полночь или 00:00).

При false: HOURLY(Hour)

  • Если Hour находится в допустимом диапазоне (от 0 до 23), он преобразует целое число Hour в соответствующее значение в перечислении HOURLY. Например, если Hour = 10, он возвращает HOURLY(10), что соответствует H10.

return (Hour>23)?HOURLY(0):HOURLY(Hour);

Пример:

  • При вызове Hourly(10), функция вернет HOURLY(10) (что является значением перечисления для 10:00).
  • Если Hourly(25), то, поскольку 25 не является допустимым часом, функция вернет HOURLY(0) (что соответствует 00:00 или полуночи).

Ключевые моменты:

  • Обработка недопустимых значений часов: Функция гарантирует, что если значение Hour выходит за пределы допустимого диапазона (больше 23), по умолчанию будет установлено значение HOURLY(0), что эквивалентно полуночи.
  • Преобразование: функция эффективно преобразует целое число часов в значение перечисления HOURLY, что упрощает его использование в логике, основанной на времени.

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

Функция преобразует целое число (от 0 до 59) в значение перечисления MINUTELY. Если входное целое число превышает 59, функция по умолчанию принимает значение M0 (минута 0).

MINUTELY CTimeVariables::Minutely(uint Minute)
{
   return (Minute>59)?MINUTELY(0):MINUTELY(Minute);
}

Если минута превышает 59, возвращается M0 в качестве запасного варианта для предотвращения ошибок.

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

This function performs a similar task for seconds. Она преобразует целое число (от 0 до 59) в значение перечисления SECONDLY. Если целое число превышает 59, по умолчанию используется значение S0 (второй 0).

SECONDLY CTimeVariables::Secondly(uint Second)
{
   return (Second>59)?SECONDLY(0):SECONDLY(Second);
}

Если значение секунды превышает 59, возвращается S0 для корректной обработки недопустимых входных данных.

Основные выводы:

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

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

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

Прогнозы и идеи:

Различные рыночные условия будут влиять на полезность класса TimeVariables. Например:

  • На нестабильных рынках, особенно во время важных новостей (например, при выходе данных по занятости в несельскохозяйственном секторе или выступления представителей центральных банков), точность вплоть до секунды становится критически важной, поскольку значительные изменения цен могут происходить за миллисекунды.
  • В условиях низкой волатильности для размещения сделок может быть достаточно точности на уровне минут, поскольку движение цен, как правило, более медленное.


Упрощение доступа к базе данных

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

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

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

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

Например, предположим, что в нашем массиве ниже есть следующие значения времени.

time[0] = '14:00'
time[1] = '14:15'
time[2] = '14:30'
time[3] = '14:45'
time[4] = '14:50'
time[5] = '14:55'
time[6] = '15:00'
time[7] = '15:05'
time[8] = '15:10'
time[9] = '15:15'
time[10] = '15:30'
time[11] = '15:45'
time[12] = '15:55'

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

Исследование случая:

Давайте рассмотрим реальный пример, чтобы продемонстрировать, как эта оптимизация может повысить производительность:

  • Сценарий: Трейдер использует советник, который отслеживает основные выпуски новостей, например, выход данных о занятости в несельскохозяйственном секторе (Non-Farm Payrolls, NFP) в США запланирован на 14:30. Советник запрограммирован на размещение стоп-ордера на покупку или продажу в зависимости от результата новостей. Без оптимизации советник непрерывно обращается к базе данных каждую секунду в течение всего дня, проверяя наличие следующего события. К моменту выхода NFP у советника может возникнуть задержка в реакции на новости из-за чрезмерных запросов к базе данных, что снизит его шансы на обнаружение оптимальных точек входа.
  • Решение с сокращением доступа к базе данных: Вместо постоянного доступа к базе данных советник загружает все события за день в 00:00 и сегментирует их по часам. Когда время приближается к 14:00, советник проверяет только события в пределах интервала времени 14:00 и пропускает любые проверки событий за пределами этого интервала. Когда наступает 14:30, советник готов немедленно отреагировать на публикацию NFP без каких-либо задержек, размещая сделку в нужный момент.

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

Папка TimeSeries



Класс TimeByHour

Приведенный ниже код определяет класс CTimeByHour, который предназначен для управления и извлечения данных о времени и событиях для каждого часа дня. Класс использует несколько компонентов, таких как структуры, массивы и концепцию объектно-ориентированного программирования (ООП). Целью этого класса является создание объектов массива для каждого часа суток в 24-часовом формате, то есть мы объявим 24 объекта массива. Эти объекты массива будут хранить целочисленные переменные Hour и Minute и структурную переменную календаря myEData (в этой переменной будет храниться вся информация о событии для конкретного часа и минуты, когда оно произойдет в течение дня). Заявленная структура TimeDate сохранит час и минуту даты, где массив myTimeData будет хранить данные параллельно со структурным массивом myEvents.

//+------------------------------------------------------------------+
//|                                                   TimeByHour.mqh |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>
#include "../TimeVariables.mqh"
#include "../CommonVariables.mqh"
#include "../TimeManagement.mqh"
//--- Structure to store time data in Hour and Minute
struct TimeDate
  {
   int               Hour;
   int               Minute;
  } myTimeData[];
//--- Structure array to store event data in parallel with myTimeData array
Calendar myEvents[];
//+------------------------------------------------------------------+
//|TimeByHour class                                                  |
//+------------------------------------------------------------------+
class CTimeByHour:public CObject
  {
private:
   //--- classes' object declarations
   CTimeManagement   CTime;
   CTimeVariables    CTV;

protected:
   //--- class constructor with parameters
                     CTimeByHour(HOURLY myHour,MINUTELY myMinute,Calendar &myEventData):
      //--- Assign integer variables Hour and Minute with time data from myHour and myMinute respectively
                     Hour(int(myHour)),Minute(int(myMinute))
     {
      //--- Assign variable myEData with event info from variable myEventData
      myEData = myEventData;
     }

   virtual void      myTime(Calendar &myNews[]);
   //--- Array object declarations for each hour of the day
   CArrayObj         *myH1,*myH2,*myH3,...,*myH24;
   //--- Integer variables to store time data in hour and minute format
   int               Hour;
   int               Minute;
   //--- Calendar structure variable to store event info
   Calendar          myEData;

public:
   //--- class constructor without parameters
                     CTimeByHour(void)
     {
     }
   //--- Array object variable
   CArrayObj         *getmyTime;
   //--- Retrieve array object for an individual hour
   CObject           *getTime(HOURLY myHour)
     {
      switch(myHour)
        {
         case  H1:
            //--- retrieve array obj for 01 Hour
            return myH1;
            break;
         case H2:
            //--- retrieve array obj for 02 Hour
            return myH2;
            break;
         case H3:
            //--- retrieve array obj for 03 Hour
            return myH3;
            break;
         // ...
         default:
            //--- retrieve array obj for 24|00 Hour
            return myH24;
            break;
        }
     }
   //--- class pointer variable
   CTimeByHour        *myClass;
   //--- class destructor
                    ~CTimeByHour(void)
     {
      //--- delete all pointer variables
      delete getmyTime;
      delete myClass;
      delete myH1;
      delete myH2;
      delete myH3;
      // ...
     }
   //--- Function to retrieve timedata and calendar info for a specific hour of the day via parameters passed by reference
   void              GetDataForHour(HOURLY myHour,TimeDate &TimeData[],Calendar &Events[])
     {
      //--- Clean arrays
      ArrayRemove(TimeData,0,WHOLE_ARRAY);
      ArrayRemove(Events,0,WHOLE_ARRAY);
      //--- retrieve array object for the specific hour
      getmyTime = getTime(myHour);
      // Iterate through all the items in the list.
      for(int i=0; i<getmyTime.Total(); i++)
        {
         // Access class obj via array obj index
         myClass = getmyTime.At(i);
         //Re-adjust arrays' sizes
         ArrayResize(TimeData,i+1,i+2);
         ArrayResize(Events,i+1,i+2);
         //--- Assign values to arrays' index
         TimeData[i].Hour = myClass.Hour;
         TimeData[i].Minute = myClass.Minute;
         Events[i] = myClass.myEData;
        }
     }
  };
//+------------------------------------------------------------------+
    

Структуры:

  • TimeDate - структура для хранения данных о времени с полями Hour и Minute в виде целочисленных значений.
  • myTimeData[] - массив структуры TimeDate для хранения данных о времени за несколько часов.

struct TimeDate
{
   int Hour;
   int Minute;
} myTimeData[];

  • myEvents[] - массив типа Calendar для хранения данных о событиях параллельно с myTimeData.

Calendar myEvents[];

Объявление класса CTimeByHour:

  • CTimeByHour - класс, расширяющий CObject. Класс управляет данными о времени и событиях по часам.

class CTimeByHour:public CObject

Приватные члены:

  • CTimeManagement и CTimeVariables - объекты пользовательских классов (CTimeManagement, CTimeVariables), включенных из TimeManagement.mqh и TimeVariables.mqh, управляющие данными и переменными, связанными со временем.

private:
   CTimeManagement   CTime;
   CTimeVariables    CTV;

Конструктор:

  • Параметризованный конструктор класса. Инициализирует две целочисленные переменные (Hour и Minute), используя перечисления HOURLY и MINUTELY, и присваивает информацию о событии (myEventData) myEData.

protected:
   CTimeByHour(HOURLY myHour, MINUTELY myMinute, Calendar &myEventData):
      Hour(int(myHour)), Minute(int(myMinute))
   {
      myEData = myEventData;
   }

Члены-данные:

  • myH1 ... myH24 - указатели на объекты CArrayObj, каждый из которых соответствует определенному часу (от 01 до 24) дня. Каждый CArrayObj содержит массив объектов для определенного часа.
  • Hour и Minute - целочисленные переменные для хранения времени.
  • myEData - объект календаря, в котором хранится информация о событиях.

   CArrayObj *myH1, *myH2, ..., *myH24;
   int Hour;
   int Minute;
   Calendar myEData;

Публичные методы:

  • CTimeByHour(void) - конструктор по умолчанию, который ничего не инициализирует.
  • getmyTime - указатель на объект массива, содержащий данные о времени для определенного часа.

public:
   CTimeByHour(void) {}
   CArrayObj *getmyTime;

Извлечение объекта массива для Hour:

  • getTime(HOURLY myHour) - метод, который использует оператор switch для извлечения соответствующего объекта CArrayObj для определенного часа дня на основе перечисления HOURLY. Каждый случай соответствует одному часу (например, H1, H2, ... H24).

CObject *getTime(HOURLY myHour)
{
   switch(myHour)
   {
      case H1: return myH1;
      case H2: return myH2;
      ...
      case H24: return myH24;
   }
}

Деструктор:

  • ~CTimeByHour(void) - деструктор очищает динамически выделенную память, вызывая delete для указателей CArrayObj (myH1 ... myH24), а также других указателей класса.

~CTimeByHour(void)
{
   delete getmyTime;
   delete myClass;
   delete myH1, myH2, ..., myH24;
}

Получить данные за определенный час:

  • GetDataForHour - метод извлекает данные о времени и событиях для определенного часа (myHour).
  • ArrayRemove - очищает массивы (TimeData[], Events[]).
  • getmyTime = getTime(myHour) - извлекает объект массива для указанного часа.
  • for loop - выполняет итерацию по всем элементам в извлеченном CArrayObj (то есть данным о времени и событиях для каждой записи).
  • ArrayResize - динамически изменяет размеры массивов (TimeData[], Events[]) для размещения новых данных.
  • myClass - ссылка на текущий объект, обрабатываемый в массиве.

Для каждого объекта метод присваивает Hour, Minute и myEData соответствующему индексу в массивах TimeData[] и Events[].

void GetDataForHour(HOURLY myHour, TimeDate &TimeData[], Calendar &Events[])
{
   ArrayRemove(TimeData, 0, WHOLE_ARRAY);
   ArrayRemove(Events, 0, WHOLE_ARRAY);

   getmyTime = getTime(myHour);
   
   for(int i = 0; i < getmyTime.Total(); i++)
   {
      myClass = getmyTime.At(i);
      ArrayResize(TimeData, i + 1);
      ArrayResize(Events, i + 1);

      TimeData[i].Hour = myClass.Hour;
      TimeData[i].Minute = myClass.Minute;
      Events[i] = myClass.myEData;
   }
}



Класс TimeByDay

Этот класс будет отвечать за присвоение значений объектам массива, объявленным ранее в заголовочном файле TimeByHour, а также за извлечение этих значений и сортировку по конкретному часу и минуте, сохраненным в соответствующем объекте массива. Код начинается с импорта других файлов: TimeByHour.mqh, который обрабатывает данные времени на уровне часов, и CommonVariables.mqh, который содержит общие константы и переменные. Класс CTimeByDay наследуется от CTimeByHour. Класс обрабатывает данные о времени по дням и позволяет взаимодействовать с данными о времени по часам, управляемыми CTimeByHour.

//+------------------------------------------------------------------+
//|                                                    TimeByDay.mqh |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#include "TimeByHour.mqh"
#include "../CommonVariables.mqh"
//+------------------------------------------------------------------+
//|TimeByDay class                                                   |
//+------------------------------------------------------------------+
class CTimeByDay:private CTimeByHour
  {
private:
   //--- Function to clear all array objects
   void              Clear();
   //--- Function to clean array dates in accordance to the current minute
   void              DatePerMinute(TimeDate &TData[],Calendar &EData[],MINUTELY min,TimeDate &TimeData[],Calendar &EventData[])
     {
      //--- Iterate through all the idexes in TData array
      for(uint i=0;i<TData.Size();i++)
        {
         //--- Check if Minutes match
         if(TData[i].Minute==int(min))
           {
            //--- Resize arrays
            ArrayResize(TimeData,TimeData.Size()+1,TimeData.Size()+2);
            ArrayResize(EventData,EventData.Size()+1,EventData.Size()+2);
            //--- Assign data from each array to the other
            TimeData[TimeData.Size()-1] = TData[i];
            EventData[EventData.Size()-1] = EData[i];
           }
        }
     }
public:
   //--- Function to set time for array objects based on calendar array myNews
   void              SetmyTime(Calendar &myNews[])
     {
      //--- Clear previous data stored in array objects
      Clear();
      //--- clean arrays in parallel declared in TimeByHour header file
      ArrayRemove(myTimeData,0,WHOLE_ARRAY);
      ArrayRemove(myEvents,0,WHOLE_ARRAY);
      //--- Set new values to each array object accordingly
      myTime(myNews);
     }
   //--- Function to get time for the specific hour and minute for news events
   void              GetmyTime(HOURLY myHour,MINUTELY myMinute,TimeDate &TimeData[],Calendar &Events[])
     {
      //--- clean arrays in parallel declared in TimeByHour header file
      ArrayRemove(myTimeData,0,WHOLE_ARRAY);
      ArrayRemove(myEvents,0,WHOLE_ARRAY);
      //--- Declare temporary arrays to get news data for a specific hour
      TimeDate myTData[];
      Calendar myData[];
      //--- Get Data for the specific hour of the day
      GetDataForHour(myHour,myTData,myData);
      //--- Filter the Data for a specific Minute of the hour
      DatePerMinute(myTData,myData,myMinute,TimeData,Events);
      //--- Clear data from the temporary array variables
      ArrayRemove(myTData,0,WHOLE_ARRAY);
      ArrayRemove(myData,0,WHOLE_ARRAY);
     }
public:
   //--- Class constructor
                     CTimeByDay(void)
     {
      //--- Initialize array objects
      myH1 = new CArrayObj();
      myH2 = new CArrayObj();
      myH3 = new CArrayObj();
      //...
     }
   //--- Class destructor
                    ~CTimeByDay(void)
     {
     }
  };

//+------------------------------------------------------------------+
//|Add data to Array Objects for each Hour of the day                |
//+------------------------------------------------------------------+
void              CTimeByHour::myTime(Calendar &myNews[])
  {
//--- Iterate through myNews calendar array
   for(uint i=0;i<myNews.Size();i++)
     {
      //--- Assign datetime from myNews calendar array
      datetime Date = datetime(myNews[i].EventDate);
      //--- Assign HOURLY Enumeration value from datetime variable Date
      HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date));
      //--- Assign MINUTELY Enumeration value from datetime variable Date
      MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date));
      //--- Switch statement to identify each value scenario for myHour
      switch(myHour)
        {
         case  H1:
            //--- add array obj values for 01 Hour
            myH1.Add(new CTimeByHour(myHour,myMinute,myNews[i]));
            break;
         case H2:
            //--- add array obj values for 02 Hour
            myH2.Add(new CTimeByHour(myHour,myMinute,myNews[i]));
            break;
         case H3:
            //--- add array obj values for 03 Hour
            myH3.Add(new CTimeByHour(myHour,myMinute,myNews[i]));
            break;
         //...
         default:
            //--- add array obj values for 24|00 Hour
            myH24.Add(new CTimeByHour(myHour,myMinute,myNews[i]));
            break;
        }
     }
  }

//+------------------------------------------------------------------+
//|Clear Data in Array Objects                                       |
//+------------------------------------------------------------------+
void CTimeByDay::Clear(void)
  {
//--- Empty all array objects
   myH1.Clear();
   myH2.Clear();
   myH3.Clear();
   //...
  }
//+------------------------------------------------------------------+

Приватные функции:

  • Эта функция используется для очистки (или сброса) всех объектов массива, хранящих почасовые данные о времени.

void Clear();

Функция ниже сортирует данные о времени (TData[]) и событиях календаря (EData[]), чтобы оставить только те записи, которые соответствуют определенной минуте (представленной аргументом min).

  • Она проходит по TData[] и для каждого элемента проверяет, соответствует ли минута значению min. Если они совпадают, массивы TimeData[] и EventData[] изменяются в размерах, и в них копируются соответствующие данные из TData[] и EData[].

void DatePerMinute(TimeDate &TData[], Calendar &EData[], MINUTELY min, TimeDate &TimeData[], Calendar &EventData[]);

Публичные функции:

Функция ниже сбрасывает и назначает новые данные о времени и событиях дня. Она использует метод myTime из CTimeByHour, который обрабатывает данные о времени на уровне часов на основе новостных событий, переданных в myNews[].

  • Сначала она очищает предыдущие сохраненные данные о времени и событиях с помощью Clear().
  • Затем она удаляет все данные из параллельных массивов (myTimeData и myEvents) и устанавливает новые значения с помощью функции myTime(), унаследованной от CTimeByHour.

void SetmyTime(Calendar &myNews[]);

Функция ниже извлекает данные о времени для определенного часа (myHour) и минуты (myMinute).

  • Сначала она очищает массивы myTimeData и myEvents.
  • Временные массивы myTData[] и myData[] объявлены для хранения данных о времени и событиях.
  • GetDataForHour() вызывается для заполнения временных массивов данными за указанный час.
  • Данные дополнительно фильтруются для конкретной минуты с помощью DatePerMinute().

void GetmyTime(HOURLY myHour, MINUTELY myMinute, TimeDate &TimeData[], Calendar &Events[]);

Конструктор:

  • Конструктор инициализирует объекты массива для каждого часа суток (от 1 до 24). Эти объекты массива будут хранить данные о времени и событиях для определенных часов.

CTimeByDay(void)
{
   // Initialize array objects for each hour
   myH1 = new CArrayObj();
   myH2 = new CArrayObj();
   // (Initializes for all 24 hours)
}

CTimeByHour::myTime()

Функция определена в CTimeByHour и наследуется CTimeByDay. Она обрабатывает массив myNews[] и связывает события с определенными часами дня.

  • Для каждого события в myNews[] извлекается час и минута события.
  • Функция использует оператор switch для определения того, какой объект почасового массива (например, myH1, myH2 и так далее) должен хранить данные о времени и событиях.
  • Каждое временное событие добавляется как объект CTimeByHour в соответствующий объект массива.

for(uint i=0; i<myNews.Size(); i++)
{
   datetime Date = datetime(myNews[i].EventDate);
   HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date));
   MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date));
   // Switch case to handle different hours
}

Функция Clear()

  • Функция очищает все объекты почасового массива (myH1, myH2 и так далее), сбрасывая данные о времени, хранящиеся для каждого часа.

void CTimeByDay::Clear(void)
{
   // Clears all array objects
   myH1.Clear();
   myH2.Clear();
   // (Clears all 24 hours)
}



Заключение

В статье были продемонстрированы методы повышения производительности советника путем разделения времени событий на отдельные массивы для каждого часа дня и снижения частоты доступа к базе данных календаря в памяти. Структурируя значения времени как перечисления (MINUTELY, SECONDLY), мы делаем код более простым в управлении и интерпретации, что снижает риск логических ошибок.

Перечисления MINUTELY, SECONDLY и PRESECONDLY представляют минуты, секунды и время до события соответственно, обеспечивая лучшую читаемость и контроль над временными интервалами. Функции преобразования упрощают работу с целыми числами в качестве входных данных, гарантируя их преобразование в осмысленные перечислимые значения. Класс CTimeByHour предоставляет механизм для хранения, извлечения и управления данными о времени и событиях для каждого часа дня. Эти методы будут реализованы в последующих статьях.

Основные выводы:

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

Спасибо за внимание! До встречи в новой статье!

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

Прикрепленные файлы |
NewsTrading_Part4.zip (591.45 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Hamid Rabia
Hamid Rabia | 5 нояб. 2024 в 12:58

Здравствуйте,

Спасибо за эту статью, не могли бы вы помочь мне добавить фильтр (filter.csv), содержащий только те новости, которые я хотел бы торговать?

Kabelo Frans Mampa
Kabelo Frans Mampa | 5 нояб. 2024 в 16:27
Hamid Rabia #:

Здравствуйте,

Спасибо за эту статью, не могли бы вы помочь мне добавить фильтр (filter.csv), содержащий только те новости, которые я хотел бы торговать?

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

Ординальное кодирование номинальных переменных Ординальное кодирование номинальных переменных
В настоящей статье мы обсудим и продемонстрируем, как преобразовать номинальные предикторы в числовые форматы, подходящие для алгоритмов машинного обучения, используя как Python, так и MQL5.
Как интегрировать концепцию Smart Money (OB) в сочетании с индикатором Фибоначчи для оптимального входа в сделку Как интегрировать концепцию Smart Money (OB) в сочетании с индикатором Фибоначчи для оптимального входа в сделку
SMC (Order Block) — это ключевые области, где институциональные трейдеры совершают значительные покупки или продажи. После значительного движения цены уровни Фибоначчи помогают определить потенциальный откат от недавнего максимума колебания (swing high) к минимуму колебания (swing low) для определения оптимальной точки входа в сделку.
Возможности Мастера MQL5, которые вам нужно знать (Часть 43): Обучение с подкреплением с помощью SARSA Возможности Мастера MQL5, которые вам нужно знать (Часть 43): Обучение с подкреплением с помощью SARSA
SARSA (State-Action-Reward-State-Action, состояние-действие-вознаграждение-состояние-действие) — еще один алгоритм, который можно использовать при реализации обучения с подкреплением. Рассмотрим, как можно реализовать этот алгоритм в качестве независимой модели (а не просто механизма обучения) в советниках, собранных в Мастере, аналогично тому, как мы это делали в случаях с Q-обучением и DQN.
Создание советника на MQL5 на основе стратегии PIRANHA с использованием Полос Боллинджера Создание советника на MQL5 на основе стратегии PIRANHA с использованием Полос Боллинджера
В настоящей статье мы создаем советника (EA) на MQL5 на основе стратегии PIRANHA, использующего Полосы Боллинджера для повышения эффективности торговли. Мы обсуждаем ключевые принципы стратегии, реализацию кода, а также методы тестирования и оптимизации. Эти знания позволят эффективно использовать советник в ваших торговых сценариях