Чтение цены, объема, спреда и времени по индексу бара

Иногда требуется узнать информацию не о последовательности баров, а лишь об одном. В принципе, для этого можно пользоваться рассмотренными ранее Copy-функциями, задав в них количество (параметр count) равным 1, но это не очень удобно. Более простой вариант предлагают следующие функции, возвращающие одно значение определенного типа для бара по его номеру в таймсерии.

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

type iValue(const string symbol, ENUM_TIMEFRAMES timeframe, int offset)

Здесь type соответствует одному из типов datetime, double, long или int, в зависимости от конкретной функции. Символ и таймфрейм индентифицируют запрашиваемый временной ряд. Индекс требуемого бара offset передается в нотации таймсерий, то есть 0 означает самый свежий, правый бар (как правило, еще не завершенный), а увеличение номера приводит к продвижению вглубь истории. Как и в случае Copy-функций, можно использовать NULL и 0 для задания символа и периода, равными свойствам текущего графика.

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

Функция

Описание

iTime

время открытия бара

iOpen

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

iHigh

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

iLow

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

iClose

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

iTickVolume

тиковый объем бара (аналог iVolume)

iVolume

тиковый объем бара (аналог iTickVolume)

iRealVolume

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

iSpread

минимальный спред бара (в пунктах)

Функции возвращают запрошенную величину или 0 в случае ошибки (к сожалению, в некоторых случаях 0 может быть реальным значением). Для получения дополнительной информации об ошибке необходимо вызвать функцию GetLastError.

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

В качестве примера решим задачу с получением более или менее реалистичной оценки размера спреда для каждого бара. Дело в том, что в котировках хранится минимальное значение спреда, что может вызывать неоправданно завышенные ожидания при конструировании торговых стратегий. Для получения абсолютно точных значений среднего, медианного или максимального спреда за бар потребовалось бы анализировать реальные тики, но мы пока не научились с ними работать. А кроме того, это был бы весьма затратный по ресурсам процесс. Более рациональным является подход по анализу спредов на младшем таймфрейме M1: для баров более старших таймфреймов достаточно искать максимальный спред во внутренних барах M1. Конечено, строго говоря, он будет не максимальным, а максимальным из минимальных, но учитывая быстротечность минутных отсчетов, можно надеяться засечь характерные расширения спредов хотя бы на некоторых барах M1, а этого уже достаточно, чтобы получить приемлемое соотношение точности и скорости анализа.

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

input string WorkSymbol = NULL// Symbol (leave empty for current)
input ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT;
input int BarCount = 100;

Поскольку для каждого бара важна только информации о его времени и спреде, была описана специальная структура с двумя полями. В принципе, можно было использовать стандартную структуру MqlRates и складывать "максимальные" спреды в какое-нибудь неиспользуемое поле (например, real_volume для символов Forex), но тогда данные для большинства полей копировались бы и занимали память вхолостую.

struct SpreadPerBar
{
   datetime time;
   int spread;
};

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

void OnStart()
{
   SpreadPerBar peaks[];
   ArrayResize(peaksBarCount);
   ZeroMemory(peaks);
   ...

Далее в цикле по барам выполняется основная часть алгоритма. Для каждого бара с помощью функции iTime мы определяем две временных метки, задающих границы бара. Фактически это время открытия i-го бара и соседнего (i+1)-го бара. Учитывая принципы индексации, можно сказать, что (i+1)-й бар является предыдущим (более старым, см. переменную prev), а i-й — следующим (более новым, см. переменную next). Время открытия бара принадлежит только одному бару, то есть метка prev содержится в (i+1)-м баре, а метка next — в i-м. Таким образом, при обработке каждого бара его правая граница должна исключаться из интервала [prev;next).

Нас интересуют спреды на одноминутном таймфрейме, и потому мы будем использовать функцию CopySpread для PERIOD_M1. При этом полуоткрытый интервал достигается путем указания в параметрах start/stop точного значения prev и уменьшенного на 1 секунду значения next. Информация о спредах копируется в динамический массив spreads (память под него распределяет сама функция).

   for(int i = 0i < BarCount; ++i)
   {
      int spreads[]; // приемный массив для спредов M1 внутри i-го бара
      const datetime next = iTime(WorkSymbolTimeFramei);
      const datetime prev = iTime(WorkSymbolTimeFramei + 1);
      const int n = CopySpread(WorkSymbolPERIOD_M1prevnext - 1spreads);
      const int m = ArrayMaximum(spreads);
      if(m > -1)
      {
         peaks[i].spread = spreads[m];
         peaks[i].time = prev;
      }
   }

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

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

   PrintFormat("Maximal speeds per intraday bar\nProcessed %d bars on %s %s",
      BarCountStringLen(WorkSymbol) > 0 ? WorkSymbol : _Symbol,
      EnumToString(TimeFrame == PERIOD_CURRENT ? _Period : TimeFrame));
   ArrayPrintM(peaks);

Запустив скрипт на графике EURUSD,H1, получим статистику спредов внутри часовых баров (приведено с сокращениями):

Maximal speeds per intraday bar
Processed 100 bars on EURUSD PERIOD_H1
[ 0] 2021.10.12 14:00        1
[ 1] 2021.10.12 13:00        1
[ 2] 2021.10.12 12:00        1
[ 3] 2021.10.12 11:00        1
[ 4] 2021.10.12 10:00        0
[ 5] 2021.10.12 09:00        1
[ 6] 2021.10.12 08:00        2
[ 7] 2021.10.12 07:00        2
[ 8] 2021.10.12 06:00        1
[ 9] 2021.10.12 05:00        1
[10] 2021.10.12 04:00        1
[11] 2021.10.12 03:00        1
[12] 2021.10.12 02:00        4
[13] 2021.10.12 01:00       16
[14] 2021.10.12 00:00       65
[15] 2021.10.11 23:00       15
[16] 2021.10.11 22:00        2
[17] 2021.10.11 21:00        1
[18] 2021.10.11 20:00        1
[19] 2021.10.11 19:00        2
[20] 2021.10.11 18:00        1
[21] 2021.10.11 17:00        1
[22] 2021.10.11 16:00        1
[23] 2021.10.11 15:00        2
[24] 2021.10.11 14:00        1

Налицо увеличение спредов в ночные часы: так, в окрестности полуночи котировки содержат спреды 7-15 пунктов, а в наших измерениях они равны 15-65. Однако и в другие периоды обнаруживаются ненулевые значения, хотя в метриках часовых баров стоят, как правило, нули.