Поиск индекса бара по времени (iBarShift)

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

int iBarShift(const string symbol, ENUM_TIMEFRAMES timeframe, datetime time, bool exact = false)

Функция возвращает индекс бара в таймсерии для указанной пары параметров symbol/timeframe, в который попадает значение параметра time. Напомним, что каждый бар характеризуется временем открытия и длительностью, общей для всех баров ряда, то есть периодом. Например, на часовом таймфрейме бар, помеченный временем открытия 13:00, длится с 13:00:00 по 13:59:59 (включая всю последнюю минуту и секунду).

Если для указанного времени бар отсутствует (например, время приходится на неторговые часы или дни), то функция ведет себя по-разному в зависимости от параметра exact: при exact = true функция вернет -1, а при exact = false — индекс ближайшего бара, у которого время открытия меньше указанного. В случае, когда такого бара нет, то есть история раньше указанного времени отсутствует, функция вернет -1. Но здесь есть нюанс.

Внимание! Возврат из функции iBarShift конкретного номера бара, то есть значения, отличного от -1, не означает, что при последующей попытке доступа к таймсериям по этому индексу удастся получить цены или другие характеристики этого бара. В частности, это может произойти, если запрошенный бар имеет индекс, превышающий лимит баров в окне терминала (TerminalInfoInteger(TERMINAL_MAXBARS)). Такое может произойти по мере формирования новых баров: тогда более старые бары с номерами, превышающими лимит, "сдвигаются" влево за границу видимых баров в окне и чисто номинально остаются на некоторое время в памяти. Обязанность проверять такие ситуации оставлена на плечах прикладных разработчиков.

Проверим работу функций Bars/iBars (см. предыдущий раздел), iBarShift с помощью скрипта SeriesBars.mq5.

void OnStart()
{
   const datetime target = PRTF(ChartTimeOnDropped());
   PRTF(iBarShift(NULL0target));
   PRTF(iBarShift(NULL0targettrue));
   PRTF(iBarShift(NULL0TimeCurrent()));
   PRTF(Bars(NULL0targetTimeCurrent()));
   PRTF(Bars(NULL0TimeCurrent(), target));
   PRTF(iBars(NULL0));
   PRTF(Bars(NULL0));
   PRTF(Bars(NULL00TimeCurrent()));
   PRTF(Bars(NULL0TimeCurrent(), TimeCurrent()));
}

Здесь нам встречается еще незнакомая функция ChartTimeOnDropped (мы опишем её позднее): она возвращает время конкретного бара (в активном графике), на который был мышью перемещен из Навигатора и сброшен скрипт. Для начала перетащим скрипт на область графика, где есть котировки.

В журнале создадутся записи следующего вида (числа будут другими, в соответствии с вашими настройками, действиями и текущим временем):

ChartTimeOnDropped()=2021.10.01 09:00:00 / ok
iBarShift(NULL,0,target)=125 / ok
iBarShift(NULL,0,target,true)=125 / ok
iBarShift(NULL,0,TimeCurrent())=0 / ok
Bars(NULL,0,target,TimeCurrent())=126 / ok
Bars(NULL,0,TimeCurrent(),target)=126 / ok
iBars(NULL,0)=10004 / ok
Bars(NULL,0)=10004 / ok
Bars(NULL,0,0,TimeCurrent())=10004 / ok
Bars(NULL,0,TimeCurrent(),TimeCurrent())=0 / ok

В данном случае скрипт был отбуксирован на бар со временем 2021.10.01 09:00 (использовался часовой таймфрейм). Согласно iBarShift это время соответствовало бару под номером 125.

Количество баров от бара под мышью до последнего (текущего времени) составило 126. Это сочетается с номером бара 125, поскольку нумерация начинается с 0.

Общее количество баров на графике, полученное разными способами (iBars, Bars без диапазона дат и Bars с полным диапазоном от 0 до текущего момента TimeCurrent), равно 10004. В настройках терминала стояло ограничение 10000, но за время сеанса успели сформироваться дополнительные 4 часовых бара.

Номер бара, в который попадает текущее время iBarShift(..., TimeCurrent()), всегда равно 0 для существующего символа и таймфрейма, при условии exact = false. Если exact = true, то мы можем иногда получить -1, поскольку серверное время увеличивается по приходу тиков всех инструментов рынка, а текущий символ может временно не торговаться. Тогда серверное время может уйти вперед более чем на размер одного бара, и для TimeCurrent не найдется нового бара для точного попадания в него.

Если перетащить и сбросить скрипт в пустой области справа от текущего, последнего бара (то есть в будущее), получим примерно такую картину:

ChartTimeOnDropped()=2021.10.09 02:30:00 / ok
iBarShift(NULL,0,target)=0 / ok
iBarShift(NULL,0,target,true)=-1 / ok
Bars(NULL,0,target,TimeCurrent())=0 / ok
Bars(NULL,0,TimeCurrent(),target)=0 / ok
iBars(NULL,0)=10004 / ok
Bars(NULL,0)=10004 / ok
Bars(NULL,0,0,TimeCurrent())=10004 / ok
Bars(NULL,0,TimeCurrent(),TimeCurrent())=0 / ok

Функция iBarShift в режиме поиска любого предыдущего бара (exact = false) возвращает 0, поскольку текущий бар ближе всего к будущему. Однако точный поиск (exact = true) дает результат -1. Также функции Bars с подсчетом количества баров в диапазоне от текущего времени до "целевого" будущего теперь возвращают 0 (баров там пока нет).

Особую важность функция iBarShift имеет для написания мультивалютных MQL-программ. Довольно часто расписания торгов разными финансовыми инструментами не совпадают, поэтому для конкретного времени бар может существовать на одном символе, но отсутствовать на другом. С помощью функции iBarShift в режиме поиска ближайшего (предыдущего) бара вы всегда можете получить индексы баров с ценами, которые были актуальными для разных символов на один и тот же момент. Как правило, даже для символов Forex индексы исторических баров для одного и того же времени могут отличаться.

Например, следующие инструкции выведут в журнал разные количества баров и их номера на одном и том же диапазоне дат для трех символов: EURUSD, XAUUSD, USDRUB на часовом таймфрейме (сервер MQ Demo):

PRTF(Bars("EURUSD"PERIOD_H1D'2021.05.01', D'2021.09.01')); // 2087
PRTF(Bars("XAUUSD"PERIOD_H1D'2021.05.01', D'2021.09.01')); // 1991
PRTF(Bars("USDRUB"PERIOD_H1D'2021.05.01', D'2021.09.01')); // 694
PRTF(iBarShift("EURUSD"PERIOD_H1D'2021.09.01')); // 671
PRTF(iBarShift("XAUUSD"PERIOD_H1D'2021.09.01')); // 638
PRTF(iBarShift("USDRUB"PERIOD_H1D'2021.09.01')); // 224