Библиотеки: Fast iBarShift and Bars for MT5

 

Fast iBarShift and Bars for MT5:

Существует множество вариантов функции iBarShift для MetaTrader 5. Но этот вариант отличается простотой, быстротой и правильностью.

int iBarShift(const string Symb,const ENUM_TIMEFRAMES TimeFrame,datetime time,bool exact=false)
  {
   int Res=iBars(Symb,TimeFrame,time+1,UINT_MAX);
   if(exact) if((TimeFrame!=PERIOD_MN1 || time>TimeCurrent()) && Res==iBars(Symb,TimeFrame,time-PeriodSeconds(TimeFrame)+1,UINT_MAX)) return(-1);
   return(Res);
  }

iBarShift

На момент опубликования этого кода встроенная функция Bars, которая используется в данной функции iBarShift, содержит в себе баг, который проявляется как подвисание программы на более чем 10 секунд. В основном это происходит, когда функции Bars необходимо возвращать ноль.

Например, при выполнении кода:

Print("1");
   Print(Bars(_Symbol,PERIOD_D1,D'2018.05.02 01:58:03',D'2018.05.02 12:56:11'));
   Print("2");

"2" напечатается после "1" более чем через 10 секунд.

Рекомендую всем в своих программах заменить штатную функцию Bars на данную функцию iBars, особенно, если вы замечаете странные подвисания на более чем 10 секунд.

Автор: Nikolai Semko

 

C билда 1847 MT5 был исправлен баг зависания в функции Bars. Также была добавлена штатная функция iBarShift.
Поэтому для увеличения быстродействия этих функций (где-то в раз 10) лучше теперь использовать такой вариант кода :

int fBars(string symbol_name,ENUM_TIMEFRAMES  timeframe,datetime start_time,datetime stop_time)
  {
   static string LastSymb=NULL;
   static ENUM_TIMEFRAMES LastTimeFrame=0;
   static datetime LastTime=0;
   static datetime LastTime0=0;
   static int PerSec=0;
   static int PreBars=0;
   static datetime LastBAR=0;
   static datetime LastTimeCur=0;
   static bool flag=true;
   static int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);
   datetime TimeCur;
   if(timeframe==0) timeframe=_Period;
   const bool changeTF=LastTimeFrame!=timeframe;
   const bool changeSymb=LastSymb!=symbol_name;
   const bool change=changeTF || changeSymb || flag;

   LastTimeFrame=timeframe; LastSymb=symbol_name;
   if(changeTF) PerSec=::PeriodSeconds(timeframe); if(PerSec==0) { flag=true; return(0);}

   if(stop_time<start_time)
     {
      TimeCur=stop_time;
      stop_time=start_time;
      start_time=TimeCur;
     }
   if(changeSymb)
     {
      if(!SymbolInfoInteger(symbol_name,SYMBOL_SELECT))
        {
         SymbolSelect(symbol_name,true);
         ChartRedraw();
        }
     }
   TimeCur=TimeCurrent();
   if(timeframe==PERIOD_W1) TimeCur-=(TimeCur+345600)%PerSec; // 01.01.1970 - Thursday. Minus 4 days.
   if(timeframe<PERIOD_W1) TimeCur-=TimeCur%PerSec;
   if(start_time>TimeCur) { flag=true; return(0);}
   if(timeframe==PERIOD_MN1)
     {
      MqlDateTime dt;
      TimeToStruct(TimeCur,dt);
      TimeCur=dt.year*12+dt.mon;
     }

   if(changeTF || changeSymb || TimeCur!=LastTimeCur)
      LastBAR=(datetime)SeriesInfoInteger(symbol_name,timeframe,SERIES_LASTBAR_DATE);

   LastTimeCur=TimeCur;
   if(start_time>LastBAR) { flag=true; return(0);}

   datetime tS,tF=0;
   if(timeframe==PERIOD_W1) tS=start_time-(start_time+345599)%PerSec-1;
   else if(timeframe<PERIOD_MN1) tS=start_time-(start_time-1)%PerSec-1;
   else  //  PERIOD_MN1
     {
      MqlDateTime dt;
      TimeToStruct(start_time-1,dt);
      tS=dt.year*12+dt.mon;
     }
   if(stop_time<=LastBAR)
     {
      if(timeframe<PERIOD_W1) tF=stop_time-(stop_time)%PerSec;
      else if(timeframe==PERIOD_W1) tF=stop_time-(stop_time+345600)%PerSec;
      else //  PERIOD_MN1
        {
         MqlDateTime dt0;
         TimeToStruct(stop_time-1,dt0);
         tF=dt0.year*12+dt0.mon;
        }
     }
   if(change || tS!=LastTime || tF!=LastTime0)
     { PreBars=Bars(symbol_name,timeframe,start_time,stop_time); LastTime=tS; LastTime0=tF; }
   flag=false;
   return(PreBars);
  }


int fBarShift(string symb,ENUM_TIMEFRAMES TimeFrame,datetime time,bool exact=false)
  {
   int Res=fBars(symb,TimeFrame,time+1,UINT_MAX);
   if(exact) if((TimeFrame!=PERIOD_MN1 || time>TimeCurrent()) && Res==fBars(symb,TimeFrame,time-PeriodSeconds(TimeFrame)+1,UINT_MAX)) return(-1);
   return(Res);
  }
Файлы:
iBars.mqh  14 kb
 
Nikolai Semko:

C билда 1847 MT5 была исправлен баг зависания в функции Bars. Также была добавлена штатная функция iBarShift.
Поэтому для увеличения быстродействия этих функций (где-то в раз 10) лучше теперь использовать такой вариант кода :

Отправьте код в сервисдеск, пусть его внедрят штатно в платформу

 
Vitaly Muzichenko:

Отправьте код в сервисдеск, пусть его внедрят штатно в платформу

Да кто я такой, чтобы им указывать. 
Этот код кроссплатформенный. И самое интересное, что на MT4 выигрыша нет, так что в MT4 функция Bars написана оптимально.

 
Nikolai Semko:

Да кто я такой, чтобы им указывать. 
Этот код кроссплатформенный. И самое интересное, что на MT4 выигрыша нет, так что в MT4 функция Bars написана оптимально.

За этой веткой следят. Вставьте через карман свой пост.

Альтернативные реализации стандартных функций/подходов
Альтернативные реализации стандартных функций/подходов
  • 2016.09.01
  • www.mql5.com
NormalizeDouble Результат 1123275 и 1666643 в пользу MyNormalizeDouble (Optimize=1). Без оптимизации - быстрее раза в четыре (на память...
 
fxsaber:

За этой веткой следят. Вставьте через карман свой пост.

Ок. Только почему-то не получилось через карман. Не фурычит. Через ссылку сбросил.

 
Nikolai Semko:

Поэтому для увеличения быстродействия этих функций (где-то в раз 10) лучше теперь использовать такой вариант кода :

Вы не погорячились насчёт быстродействия?  Особенно учитывая огромное количество всевозможных проверок в вашем коде.  Лень вникать в суть этого, но с трудом верится, что такая портянка работает быстро.

 
Alexey Navoykov:

Вы не погорячились насчёт быстродействия?  Особенно учитывая огромное количество всевозможных проверок в вашем коде.  Лень вникать в суть этого, но с трудом верится, что такая портянка работает быстро.

Лучше бы Вам лень было писать это сообщение. ))

Я специально к этим функциям приложил индикатор для теста скорости. 

Функция Bars выполняется за микросекунды, а проверки, арифметические операции(даже вычисление квадратного корня из двоичного числа) меньше наносекунды:

void OnStart()
  {
   ulong t;
   double sum=0;
   t=GetMicrosecondCount();
   for (double i=1; i<2;i+=0.000001 ) sum+=sqrt(i);
   t=GetMicrosecondCount()-t;
   Print("Сумма 1 000 000 корней = " + DoubleToString(sum,18)+ " за " + IntegerToString((int)t) + " микросекунд"); 
  }


2018.06.14 19:23:31.188 SpeedSQRT (EURUSD,M4)   Сумма 1 000 000 корней = 1218952.6235881459433586 за 1990 микросекунд
2018.06.14 19:26:30.814 SpeedSQRT (EURUSD,M4)   Сумма 1 000 000 корней = 1218952.6235881459433586 за 1946 микросекунд
2018.06.14 19:26:34.188 SpeedSQRT (EURUSD,M4)   Сумма 1 000 000 корней = 1218952.6235881459433586 за 1946 микросекунд
2018.06.14 19:26:36.344 SpeedSQRT (EURUSD,M4)   Сумма 1 000 000 корней = 1218952.6235881459433586 за 1973 микросекунд

Из этого теста скорости видно, что 1 миллион циклов по расчету сумм корней двоичных чисел выполняется за 2000 микросекунд. 
Значит один цикл выполняется за 2 наносекунды. Один цикл - это 1 проверка, две суммы двоичных чисел, одно вычисление квадратного корня из двоичного числа. 

Файлы:
 
Nikolai Semko:

Лучше бы Вам лень было писать это сообщение. ))

Я специально к этим функциям приложил индикатор для теста скорости. 

Функция Bars выполняется за микросекунды, а проверки, арифметические операции(даже вычисление квадратного корня из двоичного числа) меньше наносекунды:

Из этого теста скорости видно, что 1 миллион циклов по расчету сумм корней двоичных чисел выполняется за 2000 микросекунд. 
Значит один цикл выполняется за 2 наносекунды. Один цикл - это 1 проверка, две суммы двоичных чисел, одно вычисление квадратного корня из двоичного числа. 

Я новые билды пока не ставил, поэтому воспроизвести не могу. Но я вам верю.  Просто как я понял, ваша функция оптимизирована для частного случая, который и представлен в вашем индикаторе.  Символ не меняется, таймфрейм тоже, и т.д.  Поэтому, полагаю, функция Bars там вызывается однократно.   Причём и символ, и таймфрейм тупо заданы константами.  Поэтому вполне даже вероятно, что компилятор заоптимизировал всю вашу функцию, выбросив всё лишнее.

Короче, тесты проведены некорректно.  Там должен быть массив символов и массив периодов.  И по всем ним пройтись вашей функцией.  Тогда уже можно будет о чём-то говорить.

 
Alexey Navoykov:

Я новые билды пока не ставил, поэтому воспроизвести не могу. Но я вам верю.  Просто как я понял, ваша функция оптимизирована для частного случая, который и представлен в вашем индикаторе.  Символ не меняется, таймфрейм тоже, и т.д.  Поэтому, полагаю, функция Bars там вызывается однократно.   Причём и символ, и таймфрейм тупо заданы константами.  Поэтому вполне даже вероятно, что компилятор заоптимизировал всю вашу функцию, выбросив всё лишнее.

Короче тесты проведены некорректно.  Там должен быть массив символов и массив периодов.  И по всем ним пройтись вашей функцией.  Тогда уже можно будет о чём то говорить.

Ничего сего - частный случай: "Символ не меняется, таймфрейм тоже"!

А что - у Вас много таких индикаторов или советников, в которых постоянно идет смена ТФ и символов?
Конечно, Вы правы. Если каждое обращение Bars будет сопровождаться сменой ТФ или символа, тогда штатная будет работать быстрее. Хотя и в этом случае просто необходимо заменить статические переменные массивом статических переменных размерностью равным количеством  ТФ. Но это как раз я считаю частным случаем. Но такое решение этого частного случая имеет право на жизнь, если это необходимо. Возможно имеет смысл внести поправки в код для таких случаев. А ... Вы об этом наверное и говорили.

А на счет констант - спасибо, что заметили. Я исправил, т.к. в оригинальной функции параметры не константные. По скорости ничего не изменилось.

 
Nikolai Semko:

А что - у Вас много таких индикаторов или советников, в которых постоянно идет смена ТФ и символов?

Ну да, немало.  Я как-раз и ориентируюсь на портфельный анализ и портфельную торговлю.   Впрочем, когда речь идёт о больших расчётах, то там вообще iBarShift неуместна, т.к. работа идёт с массивами.   По сути, iBarShift и прочие аналогичные функции предназначены для редких вызовов.  В цикле их гонять нерационально.  Просто получаем массив в CopyTime и быстренько всё находим.  Поэтому тестовый пример по сути малоприменим в реальной практике.  Если конечно кодер не чайник )   А ради чайников утруждаться не стоит, я думаю )

Мне кажется, вашу функцию логичней обернуть в класс.  Тогда и проверку символа/периода не придётся проводить на каждом вызове.  Для каждой таймсерии будет свой объект.