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

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

int iHighest(const string symbol, ENUM_TIMEFRAMES timeframe, ENUM_SERIESMODE type, int count = WHOLE_ARRAY, int offset = 0)

int iLowest(const string symbol, ENUM_TIMEFRAMES timeframe, ENUM_SERIESMODE type, int count = WHOLE_ARRAY, int offset = 0)

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

Идентификатор

Описание

MODE_OPEN

цена открытия

MODE_LOW

минимальная цена

MODE_HIGH

максимальная цена

MODE_CLOSE

цена закрытия

MODE_VOLUME

тиковый объем

MODE_REAL_VOLUME

реальный объем

MODE_SPREAD

спред

Параметр offset задает индекс, с которого начинается поиск. Напомним, что нумерация ведется как в таймсерии, то есть увеличение offset приводит к смещению в прошлое, а 0-й индекс означает текущий бар (это значение по умолчанию). Количество анализируемых баров указывается в параметре count (по умолчанию, весь массив WHOLE_ARRAY).

В случае ошибки функции возвращают -1, а код ошибки можно узнать с помощью GetLastError.

Для демонстрации работы одной из этих функций (iHighest) модифицируем пример из предыдущего раздела по оценке реальных размеров спредов по барам и сравним результаты — они, разумеется, должны совпасть. Новая версия скрипта прилагается в файле SeriesSpreadHighest.mq5.

Изменения коснулись структуры SpreadPerBar и рабочего цикла внутри OnStart.

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

struct SpreadPerBar
{
   datetime time;
   int spread;
   int max// сквозной индекс M1-бара со спредом, значение которого максимально
            // среди всех M1-баров внутри текущего бара старшего таймфрейма
   int num// количество M1-баров в текущем баре старшего таймфрейма
   int pos// начальный индекс M1-бара внутри текущего бара старшего таймфрейма
};

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

   for(int i = 0i < BarCount; ++i)
   {
      const datetime next = iTime(WorkSymbolTimeFramei);
      const datetime prev = iTime(WorkSymbolTimeFramei + 1);
      ...

Границы текущего бара prev и next определяются как раньше. Однако вместо копирования элементов таймсерии между этими метками в собственный массив spreads и последующего вызова ArrayMaximum для него, мы определяем индексы и количество M1-баров, формирующих текущий бар старшего таймфрейма. Делается это следующим образом.

Функция iBarShift позволяет узнать смещение (переменная p) в истории M1, где находится правая граница бара с временем next - 1. Функция Bars вычисляет количество баров M1 (переменная n), попадающих между метками prev и next - 1. Эти два значения становятся параметрами в вызове функции iHighest, чтобы найти максимальное значение типа MODE_SPREAD, среди n баров M1, начиная с индекса p. Если максимум найден без проблем (m > -1), нам остается взять соответствующее значение с помощью iSpread и поместить в структуру.

      const int p = iBarShift(WorkSymbolPERIOD_M1next - 1);
      const int n = Bars(WorkSymbolPERIOD_M1prevnext - 1);
      const int m = iHighest(WorkSymbolPERIOD_M1MODE_SPREADnp);
      if(m > -1)
      {
         peaks[i].spread = iSpread(WorkSymbolPERIOD_M1m);
         peaks[i].time = prev;
         peaks[i].max = m;
         peaks[i].num = n;
         peaks[i].pos = p;
      }
   }

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

Maximal speeds per intraday bar
Processed 100 bars on EURUSD PERIOD_H1
               [time] [spread] [max] [num] [pos]
[ 0] 2021.10.12 15:00        0     7    60     7
[ 1] 2021.10.12 14:00        1    89    60    67
[ 2] 2021.10.12 13:00        1   181    60   127
[ 3] 2021.10.12 12:00        1   213    60   187
[ 4] 2021.10.12 11:00        1   248    60   247
[ 5] 2021.10.12 10:00        0   307    60   307
[ 6] 2021.10.12 09:00        1   385    60   367
[ 7] 2021.10.12 08:00        2   469    60   427
[ 8] 2021.10.12 07:00        2   497    60   487
[ 9] 2021.10.12 06:00        1   550    60   547
[10] 2021.10.12 05:00        1   616    60   607
[11] 2021.10.12 04:00        1   678    60   667
[12] 2021.10.12 03:00        1   727    60   727
[13] 2021.10.12 02:00        4   820    60   787
[14] 2021.10.12 01:00       16   906    60   847
[15] 2021.10.12 00:00       65   956    60   907
[16] 2021.10.11 23:00       15   967    60   967
[17] 2021.10.11 22:00        2  1039    60  1027
[18] 2021.10.11 21:00        1  1090    60  1087
[19] 2021.10.11 20:00        1  1148    60  1147
[20] 2021.10.11 19:00        2  1210    60  1207
[21] 2021.10.11 18:00        1  1313    60  1267
[22] 2021.10.11 17:00        1  1345    60  1327
[23] 2021.10.11 16:00        1  1411    60  1387
[24] 2021.10.11 15:00        2  1461    60  1447
[25] 2021.10.11 14:00        1  1526    60  1507
...

Например, на момент запуска скрипта бар с меткой 2021.10.12 14:00 начинался с 67-го бара M1 (т.е. был открыт 67 минут назад), а M1-бар с максимальным спредом внутри этого H1-бара нашелся под индексом 89. Очевидно, что этот индекс должен быть меньше, чем номер M1-бара, на котором начинался предыдущий H1-бар: 2021.10.12 13:00 — он отметился 127 минут назад. В этом H1-баре, в свою очередь, был найден максимальный спред по индексу 181. И это меньше индекса 187 у еще более старого бара 2021.10.12 12:00.

Индексы в колонках pos и max постоянно возрастают, потому что мы обходим бары в порядке от настоящего в прошлое. В колонке num почти всегда будет выводиться 60, поскольку большинство баров H1 состоит из 60 баров M1. Но так бывает не всегда. Например, ниже показаны неполные часовые бары, состоящие из меньшего числа минут: это могут быть как последствия более раннего закрытия рынка из-за праздничного расписания, так и реальные пропуски в торговой активности (отсутствие ликвидности).

...
[38] 2021.10.11 01:00       20  2346    60  2287
[39] 2021.10.11 00:00       85  2404    58  2347
[40] 2021.10.08 23:00       15  2406    55  2405
[41] 2021.10.08 22:00        2  2463    60  2460
...