Клинч в индикаторах mql5.

 

Недавно столкнулся с одной крайне неприятной проблемой. Индикатор (вместе с символом и остальными индикаторами) просто повисали и переставали обновляться. Ни обновление графика по кнопке обновить, ни переключение ТФ/счета не помогали. Помогал лишь рестарт терминала. Убил много времени на выяснение причины, на форуме ничего по этому вопросу не нашел и решил поделиться с общественностью, чтобы люди знали, да и я сам не забыл, что это за проблема.

По всей видимости, это была ошибка клинча. Далее я поясню, почему я так решил. Кто не в курсе, из документации:

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

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

Однако, несмотря на то, что в документации написано слово "клинч" нет, как мне кажется, многих данных. А именно:

1. Примеров функций, вызывающих клинч;

2. Способов решения проблемы клинча, если он уже произошел;

3. Признаков клинча;

Сейчас попытаюсь ответить на каждый вопрос. Но сначала, приведу код функции, которая, как я считаю, приводила к клинчу.

//+------------------------------------------------------------------+
//| Проверка синхронизации символов                                  |
//+------------------------------------------------------------------+
bool CheckSync( const int barsS1,          // Количество баров по основному символу/периоду
                const int barsS2,          // Количество баров по второму символу/периоду
                const datetime& time[],    // Массив времени открытия баров символа/периода текущего графика
                const int rates_total      // Количество просчитанных баров текущего графика
              )
  {
//--- Проверяем данные по символам
   if(barsS1<=0 || barsS2<=0)
     {
      //--- Проверяем отображение комментария синхронизации
      if(inpSyncComment) // Если флаг установлен - отображаем 
         Comment(__FILE__," "+TimeToString(TimeCurrent(),TIME_DATE|TIME_SECONDS)+": Синхронизация (",barsS1,"|",barsS2,")");
      //--- Выходим
      return( false );
     }
//--- Определяем время открытия последнего бара по символам/периодам
   const datetime timeS1 = (datetime)SeriesInfoInteger( _symbol_1,      _timeframe, SERIES_LASTBAR_DATE );
   const datetime timeS2 = (datetime)SeriesInfoInteger( inpSymbol_2,    _timeframe, SERIES_LASTBAR_DATE ); 
//--- Проверяем синхронизацию
   if(timeS1==timeS2) // Если символы синхронизованы
     {
      //--- Проверяем отображение комментария синхронизации
      if(inpSyncComment)                          // Если отображается
         Comment("");                             // Сбрасываем комментарий
      //--- Выходим
      return( true );
     }
   else                                           // Если символы не синхронизованы
     {
      //--- Проверяем отображение комментария синхронизации
      if(inpSyncComment) // Если отображается
         Comment(__FILE__,": Синхронизация ("+TimeToString(timeS1)+" - "+TimeToString(timeS2)+"..");
      //--- Выходим
      return( false );
     }
  }

На вход функции  CheckSync() подавались barsS1 и barsS2 получал следующим образом:

//--- Количество баров по нужным символам/периоду 
const int barsS1 = Bars( _symbol_1, _timeframe );               // Количество баров первого символа
const int barsS2 = Bars( inpSymbol_2, _timeframe );             // Количество баров второго символа

Индикатор должен работать с парой символов, причем ранее неизвестно, будет ли один из символов символом текущего чарта. Про таймфрейм тоже неизвестно, на каком ТФ будет запущен индикатор. Необходимо, чтобы по двум символам гарантировано была открыта свеча (прошла хотя бы одна сделка - были OHLC - параметры). Вот такой код и приводил, как мне кажется, к клинчу, т.к. в некоторых случаях получалось так, что были вызваны функции Bars() и SeriesInfo() с параметрами, которые рекомендуется не использовать (т.е. символ и ТФ текущего чарта). Причем, клинч происходил наиболее часто на крайне низковолатильных инструментах, котировки которых могли стоять по-многу минут на месте.

Итак, попытаюсь дать некоторые ответы:

1. На счет примеров функций, начнем с разбора примера в документации. Смотрим код после:

if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol) 
      return(-4);

Нам нужны функции, приводящие к обновлению таймсерий. Далее по коду есть строки:

Оно нам понадобится для того, чтобы не запрашивать лишние данные. Затем выясним самую первую дату в истории по символу на торговом сервере (независимо от периода) посредством уже знакомой функции SeriesInfoInteger() с модификатором SERIES_SERVER_FIRSTDATE.

datetime first_server_date=0; 
while(!SeriesInfoInteger(symbol,PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date) && !IsStopped()) 
    Sleep(5);

Но здесь, как мы видим, функция вызывается для периода М1 (что, по-моему, странно, т.к. есть вероятность, что на Д1 будут бары, для которых нет минутной истории). Очевидно, что SeriesInfo-функции приводят к обновлению таймсерий. Кроме идентификатора SERIES_SYNCHRONIZED. Почему кроме? Cо слов Slava:

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

Slava, 2017.02.14 13:50

К индикаторам тоже относится. Создайте 1-минутный таймер и спрашивайте количество баров у всех интересующих Вас таймсерий.

Проверкой факта синхронизированности синхронизация не удерживается.

Также для синхронизации можно вызывать Copy-функции. И функцию Bars().

Если этот пост будут читать разработчики, хотелось бы услышать, какие еще функции приводят к синхронизации?!

2. Если клинч произошел, по-видимому, уже ничего не сделать, символ висит, висит, и будет висеть. Помогает только перезагрузка терминала.

3. Признаки клинча. У меня график просто повисал, индикаторы не рассчитывались, котировки не обновлялись. Труба:)

Решение же проблемы было достаточно простым (после того, как проблема была определена). Проверялись символ/период текущего графика и искались альтернативные способы получения нужных данных:

int barsS1 = ( _Symbol == _symbol_1 && _Period == _timeframe ) ? rates_total : Bars( _symbol_1, _timeframe );
int barsS2 = ( _Symbol == inpSymbol_2 && _Period == _timeframe ) ? rates_total : Bars( inpSymbol_2, _timeframe ); 

и в функции CheckSync():

//--- Определяем время открытия последнего бара по символам/периодам
//const datetime timeS1 = (datetime)SeriesInfoInteger( _symbol_1,      _timeframe, SERIES_LASTBAR_DATE );
//const datetime timeS2 = (datetime)SeriesInfoInteger( inpSymbol_2,    _timeframe, SERIES_LASTBAR_DATE ); 
const datetime timeS1 = ( _Symbol == _symbol_1 && _Period == _timeframe ) ? time[ rates_total-1 ] :                                                                                                                                                                                                               
                                                                            (datetime)SeriesInfoInteger( _symbol_1, _timeframe, SERIES_LASTBAR_DATE );
const datetime timeS2 = ( _Symbol == inpSymbol_2 && _Period == _timeframe ) ? time[ rates_total-1 ] :                                                                                                                                                                                                             
                                                                            (datetime)SeriesInfoInteger( inpSymbol_2, _timeframe, SERIES_LASTBAR_DATE );

После этого, пока что, все работает нормально. Но все равно остались вопросы:

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

2. Если есть такая "жесткая фича" - может быть стоит проверять функции подгрузки истории на наличие параметров, которые могут привести к клинчу и выдавать предупреждение пользователю на этапе компиляции? Было бы очень хорошо, и все, даже начинающие разработчики, бы знали, что так делать не стоит!

3. Можно ли вызывать функции подгрузки после того, как точно прошла синхронизация? Или от вызова функций с символом/ТФ чарта индикатора стоит категорически отказаться?

В общем, написал много, надеюсь, кому-нибудь пригодится. Тема синхронизации актуальна и важна в mql5. И хотелось бы услышать ответ разработчиков и мнение/опыт форумчан по этому поводу.


 
Alexey Kozitsyn:

Недавно столкнулся с одной крайне неприятной проблемой. Индикатор (вместе с символом и остальными индикаторами) просто повисали и переставали обновляться.

Простой вопрос: сколько и каких индикаторов используется? Для чистоты эксперимента надо бы старую версию индикатора запускать в единственном числе на чистом терминале - тогда будет зависание?

 
Stanislav Korotky:

Простой вопрос: сколько и каких индикаторов используется? Для чистоты эксперимента надо бы старую версию индикатора запускать в единственном числе на чистом терминале - тогда будет зависание?

Индикатор, изначально, использовался один. На одном чарте. Остальные (стандартный МАКД) был добавлен для того, чтобы понять, завис символ, или по нему просто нет котировок (т.к. котировок иногда не было много минут).

 

Да, еще есть нюанс. Советник, использующий данные текущего бара этого индикатора, при зависании (клинче) генерировал ошибку:

ERR_INDICATOR_DATA_NOT_FOUND

4806

Запрошенные данные не найдены 

 
Alexey Kozitsyn:

В общем, написал много, надеюсь, кому-нибудь пригодится. Тема синхронизации актуальна и важна в mql5. И хотелось бы услышать ответ разработчиков и мнение/опыт форумчан по этому поводу.


Спасибо. Мы напишем свои комментарии по этому вопросу, как только его исследуем.

 
Slava:

Спасибо. Мы напишем свои комментарии по этому вопросу, как только его исследуем.

Спасибо, а то по синхронизации происходят какие-то странные вещи на мультисимвольном индикаторе.

Еще, если не сложно, хотелось бы увидеть "эталон" синхронизации, при использовании которого и зависании индикатора можно однозначно сказать, что проблемы с терминалом/сервером брокера.

Также, если можно, хотелось бы видеть билд СЕРВЕРА брокера/ДЦ, на котором происходит работа. Т.к. иногда от этого может зависеть работоспособность программы (если нужно, могу предоставить доказательства).

 
Slava:

Спасибо. Мы напишем свои комментарии по этому вопросу, как только его исследуем.

Сколько обычно уходит времени на исследование подобной проблемы?

 
Aleksey Vyazmikin:

Сколько обычно уходит времени на исследование подобной проблемы?

Видимо годы.. Я тоже озадачен этим вопросом. Оказывает не только я. Написал в сервис деск. Теперь понимаю, почему мне до сих пор не ответили за 2 суток. Здесь уже пол года вопрос висит.. Хотя, это могло быть вообще "чёрным ящиком" и не было бы по этому поводу головняков.

Причина обращения: