Скачать MetaTrader 5

Многократный пересчет нулевого бара в некоторых индикаторах

17 июля 2006, 15:48
Nikolay Kositsin
9
2 940

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

Обычно авторы этих индикаторов, изрядно поломав голову, в конце-концов находят самый простой и далеко не самый умный способ решения проблемы: они просто заставляют свои индикаторы только для вычисления значения на одном последнем, нулевом баре при каждом запуске функции int start() пересчитывать значение индикатора для всех баров графика заново!


Кажется на первый взгляд, что все проблемы при таком решении могут запросто исчезнуть, но на самом деле проблемы эти просто перемещаются в область избыточного поглощения компьютерных ресурсов подобным индикатором, которое при определенном количестве некорректно работающих индикаторов может просто сильно тормозить и подвешивать компьютер!


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


Индикатор = Функция (Переменная1, Переменная2, Переменная3, .... и.т.д.).

Все индикаторы можно разбить на две большие группы:

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

  • Вторая - это индикаторы, в которых хотя бы одна переменная зависит от своего же значения, полученного при расчете индикатора на предыдущих барах:

    ПеременнаяN(i) = ФункцияN( ПеременнаяN(i+1)),

    где левое значение получено на i-том баре, а значение в скобках в правой части получено на баре i+1. Или хотя бы одна переменная зависит от другой переменной, значение которой было получено при расчёте индикатора на предыдущих барах:

    ПеременнаяN(i) = ФункцияR( ПеременнаяX(i+1)),

    где аналогично левое значение получено i-том на баре, а значение в скобках в правой части получено на баре i+1.

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

//+------------------------------------------------------------------+
//|                                                           T3.mq4 |
//|                                                           MojoFX |
//| http://groups.yahoo.com/group/MetaTrader_Experts_and_Indicators/               |
//+------------------------------------------------------------------+
#property copyright "MojoFX - Conversion only"
#property link "http://groups.yahoo.com/group/MetaTrader_Experts_and_Indicators/"
//----
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Red
//----
extern int MA_Period = 14;
extern double b = 0.7;
//----
double MapBuffer[];
double e1, e2, e3, e4, e5, e6;
double c1, c2, c3, c4;
double n, w1, w2, b2, b3;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
    SetIndexStyle(0, DRAW_LINE);
    IndicatorDigits(MarketInfo(Symbol(), MODE_DIGITS));
    IndicatorShortName("T3" + MA_Period);
    SetIndexBuffer(0, MapBuffer);
    b2 = b * b;
    b3 = b2 * b;
    c1 = -b3;
    c2 = (3 * (b2 + b3));
    c3 = -3 * (2 * b2 + b + b3);
    c4 = (1 + 3 * b + b3 + 3 * b2);
    n = MA_Period;
    if(n < 1) 
        n=1;
    n = 1 + 0.5 * (n - 1);
    w1 = 2 / (n + 1);
    w2 = 1 - w1;
    //----
    return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
    // В данном случае происходит полный пересчёт значений индикатора 
    // на всех барах при каждом запуске функции start()
    for(int bar = Bars-1; bar >= 0; bar--)
    /*
          Если строку "for(int bar=Bars-1; bar>=0; bar--)" заменить на четыре, 
          идущих ниже строки для пересчёта индикатора только на последних барах 
          при каждом запуске функции start(), то данный индикатор будет нормально 
          работать только на истории:
 
         int  limit,counted_bars=IndicatorCounted();
         if(counted_bars>0) counted_bars--;
         limit=Bars-1-counted_bars;
         for(int bar=limit; bar>=0; bar--)
         */
      {
        // Переменные e1,e2,e3,e4,e5,e6 являются функциями от самих себя, 
        // подсчитанных на предыдущем баре  
        e1 = w1 * Close[bar] + w2 * e1;
        e2 = w1 * e1 + w2 * e2;
        e3 = w1 * e2 + w2 * e3;
        e4 = w1 * e3 + w2 * e4;
        e5 = w1 * e4 + w2 * e5;
        e6 = w1 * e5 + w2 * e6;
 
        MapBuffer[bar]=c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3;
      }
    //----
    return(0);
  }
//+------------------------------------------------------------------+

В данном случае наиболее незатейливым способом решения проблемы будет замена переменных, которые я указал в тексте индикатора, индикаторными массивами:

//+------------------------------------------------------------------+
//| T3.mq4                                                           |
//| MojoFX                                                           |
//| http://groups.yahoo.com/group/MetaTrader_Experts_and_Indicators/ |
//+------------------------------------------------------------------+
#property copyright "MojoFX - Conversion only"
#property link "http://groups.yahoo.com/group/MetaTrader_Experts_and_Indicators/"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Red
extern int T3_Period = 14;
extern double b = 0.7;
double MapBuffer[];
//---- Превращение переменных в буферы
double e1[], e2[], e3[], e4[], e5[], e6[];
//----
double c1, c2, c3, c4;
double n, w1, w2, b2, b3;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   SetIndexStyle(0, DRAW_LINE);
   IndicatorDigits(MarketInfo(Symbol(), MODE_DIGITS));
   IndicatorShortName("T3" + T3_Period);
   SetIndexBuffer(0, MapBuffer);
//---- Прописка переменных в индикаторные буферы
   IndicatorBuffers(7);
   SetIndexBuffer(1, e1);
   SetIndexBuffer(2, e2);
   SetIndexBuffer(3, e3);
   SetIndexBuffer(4, e4);
   SetIndexBuffer(5, e5);
   SetIndexBuffer(6, e6);
//----
   b2=b*b;
   b3=b2*b;
   c1=-b3;
   c2=(3*(b2+b3));
   c3=-3*(2*b2+b+b3);
   c4=(1+3*b+b3+3*b2);
   n=T3_Period;
   if (n<1) n=1;
   n = 1 + 0.5*(n-1);
   w1 = 2 / (n + 1);
   w2 = 1 - w1;
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int start()
  {
//----+ проверка количества баров на достаточность для корректного 
//      расчёта индикатора
   if(Bars - 1 < T3_Period)
       return(0);
//----+ Введение целых переменных и получение уже подсчитанных баров
   int MaxBar, limit, counted_bars = IndicatorCounted();
//---- проверка на возможные ошибки
   if(counted_bars < 0)
       return(-1);
//---- последний подсчитанный бар должен быть пересчитан
   if(counted_bars > 0) 
       counted_bars--;
//---- определение номера самого старого бара, начиная с которого будет
//     произедён пересчёт всех баров
   MaxBar = Bars - 1 - T3_Period;
//---- определение номера самого старого бара, начиная с которого будет
//     произедён пересчёт только новых баров
   limit = (Bars - 1 - counted_bars);
//---- инициализация нуля
   if(limit > MaxBar)
     {
       for(int bar = Bars - 1; bar >= limit; bar--)
           MapBuffer[bar] = 0.0;
       limit = MaxBar; 
     }
//+--- основной цикл расчёта индикатора
   for(bar = limit; bar >= 0; bar--)
     {  
       e1[bar] = w1*Close[bar] + w2*e1[bar+1];
       e2[bar] = w1*e1[bar] + w2*e2[bar+1];
       e3[bar] = w1*e2[bar] + w2*e3[bar+1];
       e4[bar] = w1*e3[bar] + w2*e4[bar+1];
       e5[bar] = w1*e4[bar] + w2*e5[bar+1];
       e6[bar] = w1*e5[bar] + w2*e6[bar+1];
       MapBuffer[bar] = c1*e6[bar] + c2*e5[bar] + c3*e4[bar] + c4*e3[bar];
     } 
//+--- завершение основного цикла
   return(0);
  }
//+----------------------------------------------------------------+

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

Следовательно, следует запомнить значения этих переменных в конце цикла на втором баре в каких-то переменных памяти, которые не локальны ни в каком блоке программы индикатора, то есть объявлены в самом начале текста индикатора. Можно также эти переменные памяти сделать статическими и тогда их можно будет ввести в функции int start() в самом начале текста. И при каждом запуске функции start() до цикла расчета индикатора, если количество подсчитанных баров не равно нулю
if(IndicatorCounted()!=0)
производить присвоение актуальным переменным индикатора значений из этих переменных памяти. После этого можно делать расчет неподсчитанных баров по тому же самому коду, что и всех баров с самого начала! Иногда лучше запоминание этих переменных делать не в конце цикла на втором баре, а в самом начале на первом баре. Мы в дальнейших примерах поступим именно так! Вот вариант того же самого индикатора, написанный с учетом этих размышлений:
//+------------------------------------------------------------------+
//|                                                           T3.mq4 |
//|                              Copyright © 2005,            MojoFX | 
//|  http://groups.yahoo.com/group/MetaTrader_Experts_and_Indicators | 
//+------------------------------------------------------------------+
#property copyright "MojoFX - Conversion only"
#property link "http://groups.yahoo.com/group/MetaTrader_Experts_and_Indicators/"
#property indicator_chart_window
#property indicator_buffers  1
#property indicator_color1  Yellow 
//---- 
extern int  T3_Period = 8;  
extern double b = 0.7;
//---- 
//---- 
double MapBuffer[];
//----  
double e1, e2, e3, e4, e5, e6;
double n, c1, c2, c3, c4, w1, w2, b2, b3;
//---- введение переменных для сохранения переменных  e1,e2,e3,e4,e5,e6
int time2; double E1, E2, E3, E4, E5, E6;
//+------------------------------------------------------------------+ 
//|  T3 initialization function                                      |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators setting
   SetIndexStyle(0, DRAW_LINE);
   IndicatorDigits(MarketInfo(Symbol(), MODE_DIGITS));
   IndicatorShortName("T3" + T3_Period);
   SetIndexBuffer(0, MapBuffer);
   b2 = b*b;
   b3 = b2*b;
   c1 = -b3;
   c2 = (3*(b2 + b3));
   c3 = -3*(2*b2 + b + b3);
   c4 = (1 + 3*b + b3 + 3*b2);
   if(T3_Period < 1) 
       T3_Period = 1;
   n = 1 + 0.5*(T3_Period - 1);
   w1 = 2 / (n + 1);
   w2 = 1 - w1;
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//| T3 iteration function                                            |
//+------------------------------------------------------------------+
int start()
  {
//----+ проверка количества баров на достаточность для корректного 
//      расчёта индикатора
   if(Bars-1 < T3_Period)
       return(0);
//----+ Введение целых переменных и получение уже подсчитанных баров
   int MaxBar, limit, counted_bars = IndicatorCounted();
//---- проверка на возможные ошибки
   if(counted_bars < 0)
       return(-1);
//---- последний подсчитанный бар должен быть пересчитан
   if(counted_bars > 0) 
       counted_bars--;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт всех баров
   MaxBar = Bars - 1 - T3_Period;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт только новых баров
   limit = (Bars - 1 - counted_bars);
//---- инициализация нуля
   if(limit > MaxBar)
     {
       for(int bar = Bars - 1; bar >= MaxBar; bar--)
           MapBuffer[bar] = 0.0;
       limit = MaxBar; 
     }
//+--- до основного цикла расчёта индикатора восстанавливаем значения
//     переменных, какими они были после рачёта на втором баре
//+--- восстановление значений переменных +=======+
   int Tnew = Time[limit+1];
   if(limit < MaxBar)
   if(Tnew == time2)
     {
       e1 = E1; 
       e2 = E2; 
       e3 = E3; 
       e4 = E4; 
       e5 = E5; 
       e6 = E6;  
     }
   else 
     {
       if(Tnew > time2)
           Print("ERROR01");
       else 
           Print("ERROR02");
       return(-1);
     }
//+--- +==========================================+
//+--- Основной цикл расчёта индикатора
   for(bar = limit; bar >= 0; bar--)
     {
       //+--- Запоминаем значения переменных, какими они были после 
       //     второго бара
       //+--- Сохранение значений переменных +=============+ 
       if(bar == 1)
           if(((limit == 1)&&(time2 != Time[2])) || (limit > 1))
             {
               time2 = Time[2];
               E1 = e1; 
               E2 = e2; 
               E3 = e3; 
               E4 = e4; 
               E5 = e5; 
               E6 = e6;
             }
       //+---+============================================+
       e1 = w1*Close[bar] + w2*e1;
       e2 = w1*e1 + w2*e2;
       e3 = w1*e2 + w2*e3;
       e4 = w1*e3 + w2*e4;
       e5 = w1*e4 + w2*e5;
       e6 = w1*e5 + w2*e6;
       MapBuffer[bar]=c1*e6 + c2*e5 + c3*e4 + c4*e3;
     } 
   //+--- завершение основного цикла
   return(0);
  }
//+-----------------------------------------------------------------+

По сравнению с предыдущим индикатор использует для восстановления в памяти переменных несоизмеримо меньше информации! Но выглядит он, бесспорно, куда сложнее предыдущего!

И вот вариант части кода того же самого индикатора внутри функции int start() для особо продвинутых разработчиков советников, которые объединяют советник с индикаторами, в этом коде советник будет считать значение индикатора всегда только на нулевом баре. Вполне естественно, что данный вариант кода годится только для тестирования советника на истории, а для работы советника при подключении терминала к серверу следует иметь ещё вариант кода из индикатора, который мы рассмотрели в последнем примере. Предполагается, что если начал формироваться новый нулевой бар, то значение индикатора на первом баре уже окончательно досчитано и пересчёту не подлежит, то же самое касается и переменных, которые запоминаются в момент смены бара. Тут уместно будет заметить, что если не сделать такого усложнения кода для советника, то вообще непонятно, что подобный советник будет считать и на основании чего он будет совершать сделки! А если вместо подобного кода применить проверенный вариант с полным пересчётом индикатора на каждом тике на всех барах только ради значения на последнем, нулевом баре, то результат такого тестирования советника можно ожидать целый месяц!

int start()
  {
//----+ проверка количества баров на достаточность для корректного 
//      расчёта индикатора
   if(Bars-1 < T3_Period)
       return(0);
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт всех баров
   int MaxBar = Bars - 1 - T3_Period;
//---- инициализация нуля
   if(MaxBar = 0)
       for(int bar = Bars - 1; bar > 0; bar--)
           MapBuffer[bar] = 0.0;
//+--- до основного расчёта индикатора восстанавливаем значения 
//     переменных, какими они были после расчёта на первом баре
//+--- восстановление значений переменных +=======+
   int Tnew0 = Time[2];
   int Tnew1 = Time[2+1];
   if(Tnew0 == time2)
     {
       e1 = E1; 
       e2 = E2; 
       e3 = E3; 
       e4 = E4; 
       e5 = E5; 
       e6 = E6;  
     }
   else 
       if(Tnew1 != time2)
         {
           if(Tnew1 > time2)
               Print("ERROR01");
           else 
               Print("ERROR02");
           return(-1);
         }
//+--- +==========================================+
//+--- Запоминаем значения переменных, какими они были после 
//     первого бара
//+--- Сохранение значений переменных +=============+ 
   if(Tnew0 != time2)
     {
       time2 = Tnew0;
       E1 = e1; 
       E2 = e2; 
       E3 = e3; 
       E4 = e4; 
       E5 = e5; 
       E6 = e6;
     }
//+---+============================================+
 
//+--- расчёт индикатора (Расчёт идёт всё время только на нулевом 
//     баре)
   e1 = w1*Close[0] + w2*e1;
   e2 = w1*e1 + w2*e2;
   e3 = w1*e2 + w2*e3;
   e4 = w1*e3 + w2*e4;
   e5 = w1*e4 + w2*e5;
   e6 = w1*e5 + w2*e6;
   MapBuffer[0] = c1*e6 + c2*e5 + c3*e4 + c4*e3;
//----+ ------------------------------------------------+
//----+ Здесь следует поместить код вашего эксперта     |
//----+ ------------------------------------------------+
   return(0);
  }
//+----------------------------------------------------------------+

Естественно, это не обошлось без некоторого усложнения кода!

Вот пример индикатора, в котором для корректной работы требуется сделать восстановление всего одной логической переменной при каждом запуске функции int start(). Но без этой, казалось бы совсем едва заметной мелочи, индикатор нормально работать не будет:

//+------------------------------------------------------------------+
//|                                                   3LineBreak.mq4 |
//|                               Copyright © 2004, Poul_Trade_Forum |
//|                                                         Aborigen |
//+------------------------------------------------------------------+ 
#property copyright "Poul Trade Forum"
#property indicator_chart_window 
#property indicator_buffers 2
#property indicator_color1 Gold
#property indicator_color2 Magenta
//---- 
extern int Lines_Break=3;
//---- 
double HighBuffer[];
double LowBuffer [];
//---- Введение переменных для многократного пересчёта нулевого бара 
int time2;bool SWING;
//+------------------------------------------------------------------+
//| 3LineBreak initialization function                               |
//+------------------------------------------------------------------+ 
int init()
  {   
//---- Стиль исполнения графика виде гистограммы 
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexStyle(1,DRAW_HISTOGRAM);
//---- 2 индикаторных буффера использованы для счёта.
   SetIndexBuffer(0,HighBuffer);
   SetIndexBuffer(1,LowBuffer );
//---- установка значений индикатора, которые не будут видимы на 
//     графике
   SetIndexEmptyValue(0,0);
   SetIndexEmptyValue(1,0);
//---- имена для окон данных и лэйбы для субъокон.
   IndicatorShortName("3LineBreak");
   SetIndexLabel   (0,"3LineBreak");
//---- установка номера бара, начиная с которого будет отрисовываться
//     индикатор  
   SetIndexDrawBegin(0,Lines_Break);
   SetIndexDrawBegin(1,Lines_Break);
//---- завершение инициализации
 
   return(0);
  }
//+------------------------------------------------------------------+
//| 3LineBreak iteration function                                    |
//+------------------------------------------------------------------+ 
int start()
{
//---- Введение переменных с плавающей точкой
double VALUE1,VALUE2;
//---- Введение логических переменных
bool Swing=true,OLDSwing;
//----+ Введение целых переменных и получение уже подсчитанных баров
int MaxBar,limit,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан )
if (counted_bars>0) counted_bars--;
//---- определение номера самого старого бара, начиная с которого
//     будет произедён пересчёт всех баров
MaxBar=Bars-1-Lines_Break;
//---- определение номера самого старого бара, начиная с которого
//     будет произедён пересчёт только новых баров
limit=(Bars-1-counted_bars);
//---- инициализация нуля
if (limit>MaxBar)
 {
  for (int bar=limit; bar>MaxBar;bar--)
   { 
    HighBuffer[bar]=0.0; 
    LowBuffer [bar]=0.0; 
   }
  limit=MaxBar;
 }
//----
//+--- восстановление значений переменных +================+
int Tnew=Time[limit+1];
if (limit<MaxBar)
 if (Tnew==time2)Swing=SWING; 
 else
  {
   if (Tnew>time2)Print("ERROR01");
   else Print("ERROR02");
   return(-1);  
  }
//+--- +==================================================+
 
//+--- основной цикл расчёта индикатора
for (bar=limit; bar>=0;bar--)
 {
  //+--- Сохранение значений переменных +=============+ 
  if (bar==1)
       if(((limit==1)&&(time2!=Time[2]))||(limit>1))
        {
         time2=Time[2];
         SWING=Swing;
        }
  //+---+============================================+
  OLDSwing=Swing;
  //----
  VALUE1=High[Highest(NULL,0,MODE_HIGH,Lines_Break,bar+1)];
  VALUE2= Low[Lowest (NULL,0,MODE_LOW, Lines_Break,bar+1)];
  //----
  if ( OLDSwing &&  Low [bar]<VALUE2) Swing=false;
  if (!OLDSwing &&  High[bar]>VALUE1) Swing=true;
  //----
  if (Swing)
   { 
    HighBuffer[bar]=High[bar]; 
    LowBuffer [bar]=Low [bar]; 
   }
 
  if (!Swing)
   { 
    LowBuffer [bar]=High[bar]; 
    HighBuffer[bar]=Low [bar]; 
   }   
 }
//+--- завершение основного цикла
   return(0);
}


Здесь тоже вполне аналогичная ситуация:

//+------------------------------------------------------------------+
//|                                                  BrainTrend1.mq4 |
//|                                     BrainTrading Inc. System 7.0 |
//|                                     http://www.braintrading.com  |
//+------------------------------------------------------------------+
#property copyright "BrainTrading Inc. System 7.0"
#property link      "http://www.braintrading.com"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Red
#property indicator_color2 Lime
//---- 
double Ind_Buffer1[];
double Ind_Buffer2[];
double value2, Range, val1, val2, d, val3;
int    f, p, x1, x2, value11;
//+------------------------------------------------------------------+
//| BrainTrend1 initialization function                              |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexBuffer(0, Ind_Buffer1);
//---- 
   SetIndexStyle(1, DRAW_HISTOGRAM);
   SetIndexBuffer(1, Ind_Buffer2);
//----   
   string short_name;
   short_name = "BrainTrend1";
   IndicatorShortName(short_name);
   SetIndexLabel(0, "" + short_name + "_Down");
   SetIndexLabel(1, "" + short_name + "_Up");
   IndicatorDigits(MarketInfo(Symbol(), MODE_DIGITS));
//----  
   f = 7; 
   d = 2.3; 
   x1 = 53; 
   x2 = 47; 
   value11 = 9;
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//| BrainTrend1 iteration function                                   |
//+------------------------------------------------------------------+
int start()
  {
//---- проверка количества баров на достаточность для расчёта
   if(Bars < 11)
       return(0);
//---- Введение статичесих переменных памяти для многократного 
//     пересчёта нулевого бара 
   static int MEMORY, time2;
//----+ Введение целых переменных и получение уже подсчитанных баров
   int limit, MaxBar,bar, counted_bars = IndicatorCounted();
//---- проверка на возможные ошибки
   if(counted_bars < 0)
       return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
   if(counted_bars > 0) 
       counted_bars--;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт всех баров
   MaxBar = Bars - 1 - 10;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт только новых баров
   limit = Bars - counted_bars - 1; 
   if(limit > MaxBar)
   limit = MaxBar;
   Comment("BrainTrading Inc. System 7.0");
//+--- восстановление значений переменных +================+
   int Tnew = Time[limit+1];
   if(limit < MaxBar)
  if(Tnew == time2)
       p=MEMORY; 
   else
     {
       if(Tnew > time2)
           Print("ERROR01");
       else 
           Print("ERROR02");
       return(-1);  
     }
//+--- +===================================================+
   bar = limit;
   while(bar >= 0)
     {
       //+--- Сохранение значений переменных +====+ 
      if(bar == 1)
          if(((limit == 1) && (time2 != Time[2])) || (limit > 1))
             {
               time2 = Time[2];
               MEMORY = p;
            }
      //+---+====================================+
      Range = iATR(NULL, 0, f, bar) / d;
       value2 = iStochastic(NULL, 0, value11, value11, 1, 0, 0, 0, bar);
      val1 = 0.0;
       val2 = 0.0;
      val3 = MathAbs(Close[bar] - Close[bar+2]);
       if(value2 < x2 && val3 > Range) 
          p = 1;
      if(value2 > x1 && val3 > Range) 
           p = 2;
      if(value2 < x2 && (p == 1||p == 0))
        {
          if(val3 > Range)
            {
               val1 = High[bar];
               val2 = Low [bar];
             }
        }
       if(value2 > x1 && (p == 2||p == 0))
        {
           val2 = High[bar];
           val1 = Low [bar];
        }
      Ind_Buffer1[bar] = val1;
      Ind_Buffer2[bar] = val2;     
       bar--;
     } 
//+--- завершение основного цикла
   return(0);
  }

В следующем индикаторе для корректной работы требуется сделать восстановление не только обычных переменных, но и буфера:

//+------------------------------------------------------------------+
//|                                                  BrainTrend2.mq4 |
//|                                     BrainTrading Inc. System 7.0 |
//|                                     http://www.braintrading.com  |
//+------------------------------------------------------------------+
#property copyright "BrainTrading Inc. System 7.0"
#property link      "http://www.braintrading.com"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Blue
#property indicator_color2 Red
//---- 
double Ind_Buffer1[];
double Ind_Buffer2[];
double spread;
//----
bool   river = True;
int    artp, limit, Curr, glava;
double dartp, cecf, Emaxtra, widcha, TR;
double Values[1], ATR, Weight, val1, val2, low, high, Series1;
//---- Введение переменных для многократного пересчёта нулевого бара 
bool   RIVER; int time2, GLAVA; double EMAXTRA,VALUES[1];
//+------------------------------------------------------------------+
//| BrainTrend2 initialization function                              |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexBuffer(0, Ind_Buffer1);
   SetIndexStyle(1, DRAW_HISTOGRAM);
   SetIndexBuffer(1, Ind_Buffer2);
   spread = MarketInfo(Symbol(), MODE_SPREAD)*Point;
//----  
   dartp = 7.0; cecf = 0.7; artp = 7;  
//---- изменение размера буфера до требуемого
   ArrayResize(Values, artp);
//---- аналогичное изменение размера буфера памяти в первом измерении
//     до требуемого
   ArrayResize(VALUES, artp);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| BrainTrend2 iteration function                                   |
//+------------------------------------------------------------------+
int start()
  {
//---- проверка количества баров на достаточность для расчёта
   if(Bars < 11)
       return(0);
//----+ Введение целых переменных и получение уже подсчитанных баров
   int limit, MaxBar, bar, J, counted_bars = IndicatorCounted();
//---- проверка на возможные ошибки
   if(counted_bars < 0)
      return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
   if(counted_bars > 0) 
       counted_bars--;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт всех баров
   MaxBar = Bars - 3;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт только новых баров
   limit = (Bars - 1 - counted_bars);
//---- инициализация нуля
   if(limit >= MaxBar)
     {
       limit = MaxBar;
       Emaxtra = Close[limit+1];
       glava = 0;
       double T_Series2 = Close[limit+2];
       double T_Series1 = Close[limit+1];
       if(T_Series2 > T_Series1) 
           river = True; 
       else 
           river = False;
       for(int ii = Bars - 1; ii > MaxBar; ii--)
         { 
           Ind_Buffer1[ii] = 0.0;
           Ind_Buffer2[ii] = 0.0;        
         }
     } 
//----
//+--- восстановление значений переменных +================+
   int Tnew = Time[limit+1];
   if(limit < MaxBar)
       if(Tnew == time2)
         {
           for(int xx = 0;xx <= artp - 1; xx++)
               Values[xx] = VALUES[xx];
           glava = GLAVA;
           Emaxtra = EMAXTRA;
           river = RIVER;
         }  
       else
         {
           if(Tnew > time2)
               Print("ERROR01");
           else 
               Print("ERROR02");
           return(-1);  
         }
//+--- +==================================================+
//+--- Основной цикл расчёта индикатора 
   bar = limit;
   while(bar >= 0)      
     {  
       //+--- Сохранение значений переменных +=============+ 
       if(bar == 1)
           if(((limit == 1) && (time2 != Time[2])) || (limit > 1))
             {
               for(int kk = 0;kk <= artp - 1; kk++)
                 VALUES[kk] = Values[kk];
               GLAVA = glava;
               EMAXTRA = Emaxtra;
               RIVER = river;
               time2 = Time[2];
             }
       //+---+============================================+
       Series1 = Close[bar+1];
       low = Low[bar];
       high = High[bar];
       TR = spread + high - low;
       if(MathAbs(spread + high - Series1) > TR ) 
           TR = MathAbs(spread + high - Series1);
       if(MathAbs(low - Series1) > TR)  
           TR = MathAbs(low - Series1);
       if(bar == MaxBar)
           for(J = 0; bar <= artp - 1; J++)
               Values[J] = TR;    
       Values[glava] = TR;
       ATR = 0;
       Weight = artp;
       Curr = glava;
       for(J = 0; J <= artp - 1; J++) 
         {
           ATR += Values[Curr]*Weight;
           Weight -= 1.0;
           Curr--;
           if(Curr == -1) 
               Curr = artp - 1;
         }
       ATR = 2.0*ATR / (dartp*(dartp + 1.0));
       glava++;
       if(glava == artp) 
           glava = 0;
       widcha = cecf*ATR;
       if(river && low < Emaxtra - widcha) 
         {
           river = False;
           Emaxtra = spread + high;
         }
       if(!river && spread + high > Emaxtra + widcha) 
         {
           river = True;
           Emaxtra = low;
         }
       if(river && low > Emaxtra) 
         {
           Emaxtra = low;
         }
       if(!river && spread + high < Emaxtra ) 
         {
           Emaxtra = spread + high;
         }
       //Range1 = iATR(NULL,0,10,bar);
       if(river==true ) 
         {
           val1 = high;
           val2 = low;
         } 
       else 
         {
           val1 = low;
           val2 = high;
         }
       Ind_Buffer1[bar] = val1;
       Ind_Buffer2[bar] = val2;  
       bar--;
     }  
//+--- завершение основного цикла
   return(0);
  }
Ну и напоследок, всеми столь нежно и трепетно любимый индикатор ZigZag, который, как показывает анализ, тоже всё время пересчитывается на всех барах и поэтому тоже содержит в себе проблему, которой и посвящена данная статья! Код этого индикатора, как мне думается, даже несколько излишне помещать в текст этой статьи, потому как его всегда можно достать из папки "indicators" клиентского терминала Metatrader 4.

Конечно, в этом индикаторе код для более экономного использования ресурсов компьютера, написанный с учётом всех вышеизложенных представлений, мог бы выглядеть достаточно заковыристо, устрашающе и громоздко. В данной ситуации можно пойти несколько иным путём. Если после расчёта индикатора зафиксировать координаты и значения последнего и предпоследнего перегиба ломанной индикатора ZigZag, то в дальнейшем при следующем запуске функции int start() можно вполне корректно пересчитывать не подсчитанные значения индикатора уже от ближней из этих двух координат. А получившийся недосчёт последних двух вершин достать из памяти. К этому следует добавить, что этот индикатор не совсем корректно работает, периодически создавая абсолютно ненормальные горбы, от которых тоже следовало бы избавиться, желательно совместив эту задачу с предыдущей. Так как для удаления горбов с графика всегда требует как минимум трёх вершин индикатора ZigZag, то и расчёт индикатора на новых барах будем начинать именно с предпоследнего от самого начала излома ломанной индикатора. Вот вариант исправления кода индикатора в этом случае:

//+------------------------------------------------------------------+
//|                                                       ZigZag.mq4 |
//|                      Copyright © 2005, MetaQuotes Software Corp. |
//|                                       http://www.metaquotes.net/           |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net/"
#property indicator_chart_window
#property indicator_buffers  1
#property indicator_color1 Red
#property indicator_width1 0
#property indicator_style1 1
//---- 
extern int ExtDepth = 12;
extern int ExtDeviation = 5;
extern int ExtBackstep = 3;
//---- 
double ZigZagBuffer[];
//+------------------------------------------------------------------+
//| ZigZag initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
    //---- 
    SetIndexBuffer(0, ZigZagBuffer); 
    SetIndexStyle(0, DRAW_SECTION);
    SetIndexEmptyValue(0, 0.0);
    IndicatorShortName("ZigZag(" + ExtDepth + "," + ExtDeviation + "," +
                      ExtBackstep + ")");
    //---- 
    return(0);
  }
//+------------------------------------------------------------------+
//|  ZigZag iteration function                                       |
//+------------------------------------------------------------------+
int start()
  {
    //+ проверка количества баров на достаточность для корректного
    //  расчёта индикатора
    if(Bars - 1 < ExtDepth)
        return(0);
    //+ Введение целых переменных памяти для пересчёта индикатора 
    //  только на неподсчитанных барах
    static int time2, time3, time4;  
    //+ Введение переменных с плавающей точкой для пересчёта 
    //  индикатора только на неподсчитанных барах
    static  double ZigZag2, ZigZag3, ZigZag4;
    //+ Введение целых переменных для пересчёта индикатора только
    //  на неподсчитанных барах и получение уже подсчитанных баров
    int MaxBar, limit, supr2_bar, supr3_bar, supr4_bar;
    int counted_bars = IndicatorCounted();
    // проверка на возможные ошибки
    if(counted_bars < 0)
        return(-1);
    // последний подсчитанный бар должен быть пересчитан
    if(counted_bars > 0) 
        counted_bars--;
    //----+ Введение переменных    
    int shift, back, lasthighpos, lastlowpos;
    double val, res, TempBuffer[1];
    double curlow, curhigh, lasthigh, lastlow;
    // определение номера самого старого бара, начиная с которого
    // будет произедён полый пересчёт всех баров
    MaxBar = Bars - ExtDepth; 
    // определение номера стартового  бара в цикле, начиная с 
    // которого будет произедиться  пересчёт новых баров
    if(counted_bars == 0)
        limit = MaxBar;
    else 
      {
        //----
        supr2_bar = iBarShift(NULL, 0, time2, TRUE);
        supr3_bar = iBarShift(NULL, 0, time3, TRUE);
        supr4_bar = iBarShift(NULL, 0, time4, TRUE);
        //----
        limit = supr3_bar;      
        if((supr2_bar < 0) || (supr3_bar < 0) || (supr4_bar < 0))
          {
            limit = MaxBar;
            Print("Стартовый бар не найден,",
            " будет произведён пересчёт индикатора на всех барах" );
          }
      }
    // инициализация нуля
    if(limit >= MaxBar) 
      {
        for(shift = Bars - 1; shift >= MaxBar; shift--)
            ZigZagBuffer[shift] = 0.0; 
        limit = MaxBar; 
      } 
    // изменение размера временного буфера
    if(ArrayResize(TempBuffer, Limit + ExtBackstep + 1)!=
       limit + ExtBackstep + 1)
        return(-1);
    //+ начало первого большого цикла
    for(shift = limit; shift >= 0; shift--)
      {
        //--- 
        val = Low[Lowest(NULL, 0, MODE_LOW, ExtDepth, shift)];
        if(val == lastlow) 
            val = 0.0;
        else 
          { 
            lastlow = val; 
            if((Low[shift] - val) > (ExtDeviation*Point)) 
                val = 0.0;
            else
              {
                for(back = 1; back <= ExtBackstep; back++)
                  {
                    res = ZigZagBuffer[shift+back];
                    if((res !=0 ) && (res > val)) 
                        ZigZagBuffer[shift+back] = 0.0; 
                  }
              }
          } 
        ZigZagBuffer[shift] = val;
        //--- 
        val = High[Highest(NULL, 0, MODE_HIGH, ExtDepth, shift)];
        if(val == lasthigh) 
            val = 0.0;
        else 
          {
            lasthigh = val;
            if((val - High[shift]) > (ExtDeviation*Point)) 
                val = 0.0;
            else
              {
                for(back = 1; back <= ExtBackstep; back++)
                  {
                    res = TempBuffer[shift+back];
                    if((res != 0) && (res < val)) 
                    TempBuffer[shift+back] = 0.0; 
                  } 
              }
          }
        TempBuffer[shift] = val;
      }
    //+ конец первого большого цикла 
    // final cutting 
    lasthigh = -1; 
    lasthighpos = -1;
    lastlow = -1; 
    lastlowpos = -1;
    //----+ начало второго большого цикла
    for(shift = limit; shift >= 0; shift--)
      {
        curlow = ZigZagBuffer[shift];
        curhigh = TempBuffer[shift];
        if((curlow == 0) && (curhigh == 0)) 
            continue;
        //---
        if(curhigh != 0)
          {
            if(lasthigh > 0) 
              {
                if(lasthigh < curhigh) 
                    TempBuffer[lasthighpos] = 0;
                else 
                    TempBuffer[shift] = 0;
              }
            if(lasthigh < curhigh || lasthigh < 0)
              {
                lasthigh = curhigh;
                lasthighpos = shift;
              }
            lastlow = -1;
          }
        //----
        if(curlow != 0)
          {
            if(lastlow > 0)
              {
                if(lastlow > curlow) 
                    ZigZagBuffer[lastlowpos] = 0;
                else 
                  ZigZagBuffer[shift] = 0;
              }
            //---
            if((curlow < lastlow) || (lastlow < 0))
              {
                lastlow = curlow;
                lastlowpos = shift;
              } 
            lasthigh = -1;
          }
      }
    //+ конец второго большого цикла
    //+ начало третьего цикла
    for(shift = limit; shift >= 0; shift--)
      {
        res = TempBuffer[shift];
        if(res != 0.0) 
            ZigZagBuffer[shift] = res;
      }
    //+ конец третьего цикла
    //+ Восстановление значений индикаторного буффера, которые 
    //  могли быть утеряны 
    if(limit < MaxBar)
      {
        ZigZagBuffer[supr2_bar] = ZigZag2; 
        ZigZagBuffer[supr3_bar] = ZigZag3; 
        ZigZagBuffer[supr4_bar] = ZigZag4; 
        for(int qqq = supr4_bar - 1; qqq > supr3_bar; qqq--)
            ZigZagBuffer[qqq] = 0; 
        for(int ggg=supr3_bar - 1; ggg > supr2_bar; ggg--)
            ZigZagBuffer[ggg] = 0;
      }
    //+ исправление возникающих горбов 
    double vel1, vel2, vel3, vel4;
    int bar1, bar2, bar3, bar4;
    int count;
    if(limit == MaxBar)
        supr4_bar = MaxBar;
    for(int bar = supr4_bar; bar >= 0; bar--)
      {
        if(ZigZagBuffer[bar] != 0)
          {
            count++;
            vel4 = vel3;
            bar4 = bar3;
            vel3 = vel2;
            bar3 = bar2;
            vel2 = vel1;
            bar2 = bar1;
            vel1 = ZigZagBuffer[bar];
            bar1 = bar;
            if(count < 3)
                continue; 
            if((vel3 < vel2) && (vel2 < vel1))
                ZigZagBuffer[bar2] = 0;
            if((vel3 > vel2) && (vel2 > vel1))
                ZigZagBuffer[bar2] = 0;
            if((vel2 == vel1) && (vel1 != 0))
                ZigZagBuffer[bar1] = 0;      
          }
      } 
    //+ запоминание времени трёх последних перегибов Зигзага и 
    //  значений индикатора в этих точках 
    time2 = Time[bar2];
    time3 = Time[bar3];
    time4 = Time[bar4];
    ZigZag2 = vel2;  
    ZigZag3 = vel3; 
    ZigZag4 = vel4; 
    //---- завершение вычислений значений индикатора
    return(0);
  }
 //---+ +----------------------------------------------------------+


В таком виде ZigZag стал куда менее прожорливым на драгоценнейшие компьютерные ресурсы, которых, как показывает опыт, сколь бы их много не было, увы, всегда почему-то сильно не хватает! Название индикаторного буфера я поменял на более понятное "ZigZagBuffer", второй буфер с временными данными я из индикаторных буферов убрал, потому как никакой необходимости в этом нет и его название тоже сменил на "TempBuffer". В трёх циклах расчёта индикатора ввёл переменную "limit", как номер стартового бара , с которого производится пересчёт только не подсчитанных баров.

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

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

MEMORY_bar_time = Time[lastlowBar]
и в дальнейшем восстанавливать текущее положение бара по этому сохраненному в памяти времени
iBarShift(NULL, 0, MEMORY_bar_time, TRUE).

Может получиться такая ситуация, что циклов расчёта индикатора может быть несколько и в каждом цикле могут быть переменные, которые следует запомнить, причем одна и та же переменная в разных циклах может требовать отдельного запоминания в конце на втором баре или в самом начале цикла на первом баре;
2. Введение переменных с желательно соответствующими исходным переменным именами. Лучше эти переменные объявить в самом начале текста индикатора не локальными. Зачастую эти переменные можно определить как статические и в самом коде индикатора сразу после оператора start();
3. До всех циклов расчёта индикатора ввести код восстановления переменных.
4. В каждом цикле, где участвуют актуальные переменные, следует добавить код запоминания этих переменных.

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


И в качестве послесловия к статье, спустя пять месяцев, с момента ее написания, мне бы хотелось еще раз обратиться к индикатору ZigZag, ввиду его достаточной популярности, и имеющихся в MQL4 возможностей сделать его еще лучше! Я решил все-таки произвести оптимизацию этого индикатора с учетом всех изложенных мной в данной статье принципов. Индикаторных буферов пришлось оставить два (симметрично: для вершин и впадин индикатора), а отрисовку линии ZIGZAG'а сделать именно в стиле ZIGZAG (операторы


В результате индикатор стал корректно изображать одновременно две своих верщины (Хай и Лоу) на одном и том же баре!

//+------------------------------------------------------------------+
//|                                                    ZigZag_NK.mq4 |
//|                      Copyright © 2005, MetaQuotes Software Corp. |
//|                                       http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net/"
//---- отрисовка индикатора в основном окне
#property indicator_chart_window
//---- количество индикаторных буфферов
#property indicator_buffers  2
//---- цвет индикатора
#property indicator_color1 Aqua
#property indicator_color2 Aqua
//---- толщина индикаторной линии
#property indicator_width1 0
#property indicator_width2 0
//---- стиль  индикаторной линии
#property indicator_style1 1
#property indicator_style2 1
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
extern int ExtDepth=12;
extern int ExtDeviation=5;
extern int ExtBackstep=3;
//---- 
//---- индикаторные буфферы
double LowestBuffer[],HighestBuffer[];
//----+ Введение целых переменных памяти для пересчёта индикатора только
//      на неподсчитанных барах
int    MaxBar,time2,LASTLOWPOS,LASTHIGHPOS,LHTIME,LLTIME,size;  
//----+ Введение переменных памяти с плавающей точкой для пересчёта 
//      индикатора только на неподсчитанных барах
double LowestMEMORY[1],HighestMEMORY[1],LASTLOW0,LASTLOW1,LASTHIGH0,
       LASTHIGH1,LASTHIGHEST,LASTLOWEST;
//+------------------------------------------------------------------+
//| ZigZag initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//---- два индикаторных буффера использован для счёта 
   SetIndexBuffer(0,LowestBuffer);
   SetIndexBuffer(1,HighestBuffer);
//---- Стиль исполнения графика виде ломанной ZigZag 
   SetIndexStyle(0,DRAW_ZIGZAG);
   SetIndexStyle(1,DRAW_ZIGZAG);
//---- установка значений индикатора, которые не будут видимы на графике
   SetIndexEmptyValue(0,0.0);
   SetIndexEmptyValue(1,0.0);
//---- имена для окон данных и лэйбы для субъокон
   SetIndexLabel(0,"Low" );
   SetIndexLabel(1,"High");
   IndicatorShortName("ZigZag (ExtDepth="+ExtDepth+", ExtDeviation="+
                      ExtDeviation+", ExtBackstep="+ExtBackstep+" )");
//---- определение размеров временных буфферов 
   size=ExtBackstep+1;
//---- изменение размеров временных буфферов 
   if(ArrayResize(LowestMEMORY,size)!=size)return(-1);
   if(ArrayResize(HighestMEMORY,size)!=size)return(-1);
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//|  ZigZag iteration function                                       |
//+------------------------------------------------------------------+
int start()
  {
   //----+ проверка количества баров на достаточность для корректного 
   //      расчёта индикатора
   if (Bars-1<ExtDepth)return(0);
   //----+ Введение целых переменных и получение уже подсчитанных баров
   int limit,iii,counted_bars=IndicatorCounted();
   //---- проверка на возможные ошибки
   if (counted_bars<0)return(-1);
   //---- последний подсчитанный бар должен быть пересчитан 
   if (counted_bars>0) counted_bars--;
   //----+ Введение переменных    
   int    bar,back,lasthighpos,lastlowpos;
   double curlow,curhigh,lasthigh0,lastlow0,lasthigh1,lastlow1,val,res;
   //---- определение номера самого старого бара, начиная с которого
   //     будет произедён полый пересчёт всех баров
   MaxBar=Bars-ExtDepth; 
   //---- определение номера стартового  бара в цикле, начиная с которого
   //     будет произодиться  пересчёт новых баров
   limit=Bars-counted_bars-1;
        
   //+--- восстановление значений переменных +======+
   int Tnew=Time[limit+1];
   if (limit<MaxBar)
    {
     if (Tnew==time2)
      {
       lastlow0=LASTLOW0;
       lasthigh0=LASTHIGH0;
       //+---
       lastlow1=LASTLOW1;
       lasthigh1=LASTHIGH1;
       //+---
       lastlowpos=iBarShift(NULL,0,LLTIME,true); 
       lasthighpos=iBarShift(NULL,0,LHTIME,true); 
       //+--- проверка переменных lasthighpos и lastlowpos на 
       //     корректность для дальнейшего расчёта
       if ((lasthighpos<0)||(lastlowpos<0))
        {
         Print("Ошибка восстановления переменных!!!" + 
            " Потеряны значения переменных lasthighpos и lastlowpos");
         Print("Будет произведён пересчёт индикатора на всех барах!");
         return(-1);
        }
       //+---
       LowestBuffer[lastlowpos]=LASTLOWEST;
       HighestBuffer[lasthighpos]=LASTHIGHEST;
       //+---     
       for(iii=size-1; iii>=0; iii--)
        {
         LowestBuffer[iii+limit+1]=LowestMEMORY[iii];
         HighestBuffer[iii+limit+1]=HighestMEMORY[iii];
        }
       //+---    
      } 
     else 
      {
       if (Tnew>time2)
            Print("Ошибка восстановления переменных!!! Tnew>time2");
       else Print("Ошибка восстановления переменных!!! Tnew");
       Print("Будет произведён пересчёт индикатора на всех барах!");
       return(-1);  
      }
    }
   //+--- +==========================================+
   
   //---- инициализация нуля
   if (limit>=MaxBar) 
     {
      for (bar=Bars-1; bar>=MaxBar;bar--)
       {
        LowestBuffer [bar]=0.0; 
        HighestBuffer[bar]=0.0; 
       }
      lastlow1=-1; 
      lasthigh1=-1; 
      lastlowpos=-1; 
      lasthighpos=-1;
      limit=MaxBar; 
     }
     
   //----+  <<< начало первого большого цикла >>> -----------+  
   for(bar=limit; bar>=0; bar--)
     {
      //+--- Сохранение значений переменных +=========+ 
      if (bar==1)
        {
         if(((limit==1)&&(time2==Time[2]))||(limit>1))
          {
           LASTLOW0=lastlow0;
           LASTHIGH0=lasthigh0;
           //+---
           for(iii=size-1; iii>=0; iii--)
            {
             LowestMEMORY[iii]=LowestBuffer[iii+2];
             HighestMEMORY[iii]=HighestBuffer[iii+2];
            }
          }
        }
      //+---+=========================================+     
  
      //--- low
      val=Low[Lowest(NULL,0,MODE_LOW,ExtDepth,bar)];
      if(val==lastlow0) val=0.0;
      else 
        { 
         lastlow0=val; 
         if((Low[bar]-val)>(ExtDeviation*Point))val=0.0;
         else
           {
            for(back=1; back<=ExtBackstep; back++)
              {
               res=LowestBuffer[bar+back];
               if((res!=0)&&(res>val))LowestBuffer[bar+back]=0.0; 
              }
           }
        } 
      LowestBuffer[bar]=val;
      //--- high
      val=High[Highest(NULL,0,MODE_HIGH,ExtDepth,bar)];
      if(val==lasthigh0) val=0.0;
      else 
        {
         lasthigh0=val;
         if((val-High[bar])>(ExtDeviation*Point))val=0.0;
         else
           {
            for(back=1; back<=ExtBackstep; back++)
              {
               res=HighestBuffer[bar+back];
               if((res!=0)&&(res<val))HighestBuffer[bar+back]=0.0; 
              } 
           }
        }
      HighestBuffer[bar]=val;
     }
    //----+ конец первого большого цикла 
   
   //----+  <<< начало второго большого цикла >>> -----------+  
   for(bar=limit; bar>=0; bar--)
     {
      //+--- Сохранение значений переменных +====+ 
      if (bar==1)
       {
        if(((limit==1)&&(time2==Time[2]))||(limit>1))
         {
          time2=Time [2];
          LASTLOW1=lastlow1;
          LASTHIGH1=lasthigh1;
          //+---
          LLTIME=Time[lastlowpos];
          LHTIME=Time[lasthighpos];
          //+---
          LASTLOWEST=LowestBuffer[lastlowpos];
          LASTHIGHEST=HighestBuffer[lasthighpos];
         }
       }
     //+---+====================================+     
     
      curlow=LowestBuffer[bar];
      curhigh=HighestBuffer[bar];
      //---
      if((curlow==0)&&(curhigh==0))continue;
      //---
      if(curhigh!=0)
        {
         if(lasthigh1>0) 
           {
            if(lasthigh1<curhigh)HighestBuffer[lasthighpos]=0;
            else HighestBuffer[bar]=0;
           }
         //---
         if(lasthigh1<curhigh || lasthigh1<0)
           {
            lasthigh1=curhigh;
            lasthighpos=bar;
           }
         lastlow1=-1;
        }
      //----
      if(curlow!=0)
        {
         if(lastlow1>0)
           {
            if(lastlow1>curlow) LowestBuffer[lastlowpos]=0;
            else LowestBuffer[bar]=0;
           }
         //---
         if((curlow<lastlow1)||(lastlow1<0))
           {
            lastlow1=curlow;
            lastlowpos=bar;
           } 
         lasthigh1=-1;
        }
     } 
   //----+ конец второго большого цикла 
   //+--------------------------------------------------------+ 
        
 //---- завершение вычислений значений индикатора
return(0);
}
//+-----------------------------------------------------------+

Мне так думается, что в таком виде это будет самая удачная версия этого индикатора. Помимо восстановления обычных переменных в циклах, в данном индикаторе пришлось восстанавливать и многократно пересчитываемые значения в индикаторных буферах ( индикатор пересчитывает не только своё значение на текущем баре но и ещ и значения на предыдущих барах на глубину "ExtBackstep"!)

Что касается предложений удаления висячих в "воздухе" вершин ЗигЗага, то тут можно заметить, что в них тоже есть определённый логический смысл, и поэтому удалять их едва ли целесообразно. Конечно, можно ввести какой-то более хитроумный алгоритм коррекции положения этих вершин, но это уже будет несколько иной индикатор! А поскольку тема моей статьи всё-таки другая, и вопросы модификации алгоритмов ЗигЗага первоначально в ней не ставятся, то я считаю эту тему вполне исчерпанной.

Прикрепленные файлы |
NulBarRecount.zip (12.82 KB)
ZigZag_NK.mq4 (11.29 KB)
Eugeni Neumoin
Eugeni Neumoin | 17 сен 2006 в 15:58
К сожалению, стандартный - встроенный в метатрейдер ZigZag - имеет следующий недостаток.
При расчете используются два буфера. Один для найденных нижних перегибов (минимумов), другой для верхних перегибов (максимумов).
Бывают ситуации, когда на одном баре находится и минимум и максимум. В частности, на внешнем баре.
Почему-то автор - тот, кто первый написал этот индикатор - решил, что для построения ZigZag важны именно максимумы. И при нахождении минимума и максимума на одном баре выбирается только максимум.
Вот кусочек кода, где это происходит:

//----+ начало третьего цикла
for(shift=limit; shift>=0; shift--)
{
res=TempBuffer[shift];
if(res!=0.0) ZigZagBuffer[shift]=res;
}
//----+ конец третьего цикла

Горбы как раз и возникают, когда был найден минимум и максимум на одном баре.
Такое вот волюнтаристское решение. Не буду здесь рассуждать, как это проявляется в реальной рыночной ситуации. Но, считаю, что это ошибочное решение.
MQL4 Comments
MQL4 Comments | 21 сен 2006 в 08:54
nen:
Mihause:

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

При прокрутке истории назад закачивается отсутствовавшая история. Для этой части истории расчет индикатора вообще не был проведен.
Чтобы не выводился мусор, о котором Вы пишите, необходимо:
1) Прописать в индикаторные буферы 0 - ноль
2) рассчитать индикатор с самого начала.
Пример из моего индикатора:

if (timeFirstBar==Time[Bars-1])
{
cbi=Bars-IndicatorCounted()-1;
}
else
{
cbi=Bars-1;
for (int iBar=cbi; iBar>0; iBar--) {zzL[iBar]=0.0; zzH[iBar]=0.0; zz[iBar]=0. 0;}
timeFirstBar=Time[cbi];
}

Далее идет код индикатора.

timeFirstBar - время самого первого бара (Bars-1)
cbi - количество непосчитанных баров или номер бара, с которого производится пересчет индикатора

Также это поможет при переходе между реальным и демо счетами. Но частично.
Ясно. Благодарю за совет.
Eugeni Neumoin
Eugeni Neumoin | 22 сен 2006 в 22:03

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

//+------------------------------------------------------------------+
//| ZigZag. mq4 |
//| Copyright © 2005, MetaQuotes Software Corp. |
//| http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link "http://www.metaquotes.net/"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Red
#property indicator_width1 0
#property indicator_style1 1
//----
extern int ExtDepth=12;
extern int ExtDeviation=5;
extern int ExtBackstep=3;
//----
//----
double ZigZagBuffer[];
//+------------------------------------------------------------------+
//| ZigZag initialization function |
//+------------------------------------------------------------------+
int init()
{
//----
SetIndexBuffer(0,ZigZagBuffer);
SetIndexStyle(0,DRAW_SECTION);
SetIndexEmptyValue(0,0.0);
IndicatorShortName("ZigZag("+ExtDepth+","+ExtDeviation+", "+ExtBackstep+")");
//----
return(0);
}
//+------------------------------------------------------------------+
//| ZigZag iteration function |
//+------------------------------------------------------------------+
int start()
{
//----+ проверка количества баров на достаточность для корректного расчёта индикатора
if (Bars-1<ExtDepth)return(0);
//----+ Введение целых переменных памяти для пересчёта индикатора только на неподсчитанных барах
static int time2,time3,time4;
//----+ Введение переменных с плавающей точкой для пересчёта индикатора только на неподсчитанных барах
static double ZigZag2,ZigZag3,ZigZag4;
//----+ Введение целых переменных для пересчёта индикатора только на неподсчитанных барах и получение уже подсчитанных баров
int MaxBar,limit,supr2_bar,supr3_bar,supr4_bar,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан
if (counted_bars>0) counted_bars--;
//----+ Введение переменных
int shift, back,lasthighpos,lastlowpos;
double val,res,TempBuffer[1];
double curlow,curhigh,lasthigh,lastlow;
//---- определение номера самого старого бара, начиная с которого будет произедён полый пересчёт всех баров
MaxBar=Bars-ExtDepth;
//---- определение номера стартового бара в цикле, начиная с которого будет произедиться пересчёт новых баров
if (counted_bars==0)limit=MaxBar;
else
{
//----
supr2_bar=iBarShift(NULL,0,time2,TRUE);
supr3_bar=iBarShift(NULL,0,time3,TRUE);
supr4_bar=iBarShift(NULL,0,time4,TRUE);
//----
limit=supr3_bar;
if ((supr2_bar<0)||(supr3_bar<0)||(supr4_bar<0))
{
limit=MaxBar;
Print("Стартовый бар не найден, буден произведён пересчёт индикатора на всех барах" );
}
}

//---- инициализация нуля
if (limit>=MaxBar)
{
for (shift=Bars-1; shift>=MaxBar;shift--)ZigZagBuffer [shift]=0. 0;
limit=MaxBar;
}
//----
//---- изменение размера временного буфера
if(ArrayResize(TempBuffer,limit+ExtBackstep+1)!=limit+ExtBackstep+1)return(-1);

//----+-------------------------------------------------+

//----+ начало первого большого цикла
for(shift=limit; shift>=0; shift--)
{
//---
val=Low[Lowest(NULL,0,MODE_LOW,ExtDepth,shift)];
if(val==lastlow) val=0.0;
else
{
lastlow=val;
if((Low[shift]-val)>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=ZigZagBuffer[shift+back];
if((res!=0)&&(res>val)) ZigZagBuffer[shift+back]=0. 0;
}
}
}
if (Low[shift]==val) ZigZagBuffer[shift]=val;
//---
val=High[Highest(NULL,0,MODE_HIGH,ExtDepth,shift)];
if(val==lasthigh) val=0.0;
else
{
lasthigh=val;
if((val-High[shift])>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=TempBuffer[shift+back];
if((res!=0)&&(res<val)) TempBuffer[shift+back]=0. 0;
}
}
}
if (High[shift]==val) TempBuffer[shift]=val;
}
//----+ конец первого большого цикла

// final cutting
lasthigh=-1; lasthighpos=-1;
lastlow= -1; lastlowpos= -1;
//----+-------------------------------------------------+

//----+ начало второго большого цикла

for(shift=limit; shift>=0; shift--)
{
curlow=ZigZagBuffer[shift];
curhigh=TempBuffer[shift];
if((curlow==0)&&(curhigh==0)) continue;
//---
if(curhigh!=0)
{
if(lasthigh>0)
{
if(lasthigh<curhigh) TempBuffer[lasthighpos]=0;
else TempBuffer[shift]=0;
}
//---
if(lasthigh<curhigh || lasthigh<0)
{
lasthigh=curhigh;
lasthighpos=shift;
}
lastlow=-1;
}
//----
if(curlow!=0)
{
if(lastlow>0)
{
if(lastlow>curlow) ZigZagBuffer[lastlowpos]=0;
else ZigZagBuffer[shift]=0;
}
//---
if((curlow<lastlow)||(lastlow<0))
{
lastlow=curlow;
lastlowpos=shift;
}
lasthigh=-1;
}
}
//----+ конец второго большого цикла

//----+-------------------------------------------------+

//----+ начало третьего цикла
for(shift=limit; shift>=0; shift--)
{
res=TempBuffer[shift];
if(res!=0.0) ZigZagBuffer[shift]=res;
}
//----+ конец третьего цикла

//+--- Восстановление значений индикаторного буффера, которые могли быть утеряны
if (limit<MaxBar)
{
ZigZagBuffer[supr2_bar]=ZigZag2;
ZigZagBuffer[supr3_bar]=ZigZag3;
ZigZagBuffer[supr4_bar]=ZigZag4;
for(int qqq=supr4_bar-1; qqq>supr3_bar; qqq--)ZigZagBuffer[qqq]=0;
for(int ggg=supr3_bar-1; ggg>supr2_bar; ggg--)ZigZagBuffer[ggg]=0;
}
//+---+============================================+

//+--- исправление возникающих горбов
double vel1, vel2, vel3, vel4;
int bar1, bar2, bar3, bar4;
int count;
if (limit==MaxBar)supr4_bar=MaxBar;
for(int bar=supr4_bar; bar>=0; bar--)
{
if (ZigZagBuffer[bar]!=0)
{
count++;
vel4=vel3;bar4=bar3;
vel3=vel2;bar3=bar2;
vel2=vel1;bar2=bar1;
vel1=ZigZagBuffer[bar];bar1=bar;
if (count<3)continue;
if ((vel3<vel2)&&(vel2<vel1))ZigZagBuffer[bar2]=0;
if ((vel3>vel2)&&(vel2>vel1))ZigZagBuffer[bar2]=0;
if ((vel2==vel1)&&(vel1!=0 ))ZigZagBuffer[bar1]=0;
}
}
//+--- запоминание времени трёх последних перегибов Зигзага и значений индикатора в этих точках
time2=Time[bar2];
time3=Time[bar3];
time4=Time[bar4];
ZigZag2=vel2;
ZigZag3=vel3;
ZigZag4=vel4;
//+---
//---- завершение вычислений значений индикатора
return(0);
}
//---+ +---------------------------------------------------------------------+

Eugeni Neumoin
Eugeni Neumoin | 27 сен 2006 в 05:10

ZigZag с устраненными ошибками. Алгоритм устранения горбов проще, чем предложенный автором статьи. И отсутствует прорисовка перегибов в воздухе. Но в этом коде нет оптимизации.

//+------------------------------------------------------------------+
//| Custom Moving Average. mq4 |
//| Copyright © 2005, MetaQuotes Software Corp. |
//| http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link "http://www.metaquotes.net/"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Red
//---- indicator parameters
extern int ExtDepth=12;
extern int ExtDeviation=5;
extern int ExtBackstep=3;
//---- indicator buffers
double ExtMapBuffer[];
double ExtMapBuffer2[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
{
IndicatorBuffers(2);
//---- drawing settings
SetIndexStyle(0,DRAW_SECTION);
//---- indicator buffers mapping
SetIndexBuffer(0,ExtMapBuffer);
SetIndexBuffer(1,ExtMapBuffer2);
SetIndexEmptyValue(0,0.0);
SetIndexEmptyValue(1,0.0);
// ArraySetAsSeries(ExtMapBuffer,true);
// ArraySetAsSeries(ExtMapBuffer2,true);
//---- indicator short name
IndicatorShortName("ZigZag("+ExtDepth+","+ExtDeviation+", "+ExtBackstep+")");
//---- initialization done
return(0);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int start()
{
int shift, back,lasthighpos,lastlowpos;
double val,res;
double curlow,curhigh,lasthigh,lastlow;

for(shift=Bars-ExtDepth; shift>=0; shift--)
{
val=Low[Lowest(NULL,0,MODE_LOW,ExtDepth,shift)];
if(val==lastlow) val=0.0;
else
{
lastlow=val;
if((Low[shift]-val)>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=ExtMapBuffer[shift+back];
if((res!=0)&&(res>val)) ExtMapBuffer[shift+back]=0. 0;
}
}
}
if (Low[shift]==val) ExtMapBuffer[shift]=val;
//--- high
val=High[Highest(NULL,0,MODE_HIGH,ExtDepth,shift)];
if(val==lasthigh) val=0.0;
else
{
lasthigh=val;
if((val-High[shift])>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=ExtMapBuffer2[shift+back];
if((res!=0)&&(res<val)) ExtMapBuffer2[shift+back]=0. 0;
}
}
}
if (High[shift]==val) ExtMapBuffer2[shift]=val;
}

// final cutting
lasthigh=-1; lasthighpos=-1;
lastlow=-1; lastlowpos=-1;

for(shift=Bars-ExtDepth; shift>=0; shift--)
{
curlow=ExtMapBuffer[shift];
curhigh=ExtMapBuffer2[shift];
if((curlow==0)&&(curhigh==0)) continue;
//---
if(curhigh!=0)
{
if(lasthigh>0)
{
if(lasthigh<curhigh) ExtMapBuffer2[lasthighpos]=0;
else ExtMapBuffer2[shift]=0;
}
//---
if(lasthigh<curhigh || lasthigh<0)
{
lasthigh=curhigh;
lasthighpos=shift;
}
lastlow=-1;
}
//----
if(curlow!=0)
{
if(lastlow>0)
{
if(lastlow>curlow) ExtMapBuffer[lastlowpos]=0;
else ExtMapBuffer[shift]=0;
}
//---
if((curlow<lastlow)||(lastlow<0))
{
lastlow=curlow;
lastlowpos=shift;
}
lasthigh=-1;
}
}

for(shift=Bars-1; shift>=0; shift--)
{
if(shift>=Bars-ExtDepth) ExtMapBuffer[shift]=0. 0;
else
{
res=ExtMapBuffer2[shift];
if(res!=0.0)
{
if (ExtMapBuffer[shift]!=0. 0) ExtMapBuffer[shift]=0. 0; else ExtMapBuffer[shift]=res;
}
}
}
}

Eugeni Neumoin
Eugeni Neumoin | 11 окт 2006 в 11:05

И опять про ZigZag.
В той части, где исправление возникающих горбов последнюю строчку кода в цикле проверки на гобы
надо заменить на: if ((vel2==vel1)&&(vel1!=0 )) {ExtMapBuffer[bar1]=0;bar=bar3+1;}
Иначе, не все горбы устраняются

Переход на новые рельсы: пользовательские индикаторы в MQL5 Переход на новые рельсы: пользовательские индикаторы в MQL5

Я не буду перечислять все новые возможности и особенности нового терминала и языка. Их действительно много, и некоторые новинки вполне достойны освещения в отдельной статье. Вы не увидите здесь кода, написанного по принципам объектно-ориентированного программирования — это слишком серьезная тема для того, чтобы просто быть упомянутой в контексте как дополнительная вкусность для кодописателей. В этой статье остановимся подробней на индикаторах, их строении, отображении, видах, а также особенностях их написания по сравнению с MQL4.

Вот мы и получили долгожданные MetaTrader 5 и MQL5 Вот мы и получили долгожданные MetaTrader 5 и MQL5

Это очень краткий обзор MetaTrader 5. Я не могу описать все новшества системы за столь короткий период времени - тестирование стартовало 09-09-2009. Это символическая дата, и я уверен, что это будет счастливым числом. Всего несколько дней у меня на руках бета-версия терминала MetaTrader 5 и MQL5. Я не успел опробовать все, что в нем есть нового, но то, что есть, уже впечатляет.

Работа с корзинами валютных пар на рынке Форекс Работа с корзинами валютных пар на рынке Форекс

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

Текстовые файлы для хранения входных параметров советников, индикаторов и скриптов Текстовые файлы для хранения входных параметров советников, индикаторов и скриптов

В статье рассмотрены вопросы хранения динамических объектов, массивов и других переменных в качестве свойств советников, индикаторов и скриптов в текстовых файлах. Они служат удобным дополнением к функционалу стандартных средств, предлагаемых языками MQL.