Download MetaTrader 5

Multiple Null Bar Re-Count in Some Indicators

4 October 2006, 15:27
Nikolay Kositsin
0
2 971


In this article, I would like to discuss the problem of multiple re-counting of the indicator value in the MetaTrader 4 Client Terminal when the null bar changes. Its core consits in that you can find in the web so many rather good indicators that can work normally only on historical data, but, when the trading platform is connected to the server, they give results that, to put it mildly, bear no relation to algorithms set on these indicators!

Usually, authors of such indicators, after having wrestled with the problem, finally find the easiest but not the wisest solution: They just make their indicators recalculate indicator's value for all bars of the chart at each launch of the int start() function in order to calculate the value on only one last, null bar!

At the first glance, all problems seem to be solved by this, but, in the reality, the problems just move to another area. Such indicator absorbs resources, and this results in that computer hampers and hangs!

To get outside of this problem, one should first of all think of the general idea of how the indicator is built. Every indicator is a function of some variables:


Indicator = Function (Variable1, Variable2, Variable3, .... etc.).

All indicators can be divided into two large groups:

  • Group 1 - indicators, in which all variables are determined only by security prices and by external variables; these variables can be calculated for any bar without concerning them with values of the same variables on preceding bars.

  • Group 2 - indicators, in which at least one variable depends on its own value obtained as a result of indicator calculation on preceding bars:

    VariableN(i) = FunctionN(VariableN(i+1)),

    where the left value was obtained on the i-th bar and the value in brackets in the right part was obtained on bar i+1. Or at least one variable depends on another variable, the value of which was obtained at calculation of the indicator on preceding bars:

    VariableN(i) = FunctionR(VariableX(i+1)),

    where, similarly, the left value was obtained on the i-th bar and the value in brackets in the right part was obtained on bar i+1.

We are interested in indicators from Group 2 since these are these indicators that create the problem above. Where indicator is multiply re-counted on the null bar, such variables that, logically, should have the same values for other bars start to be multiply re-counted for all bars, even if the indicator has been written in due form to re-count only null bar using the IndicatorCounted() function. Such an indicator proves to be absolutely useless for working on the real-time market. Having explained all this, let us go to the specific indicator:


//+------------------------------------------------------------------+
//|                                                           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()
  {
    // In this case, indicator's values are fully recalculated 
    // on all bars at every launch of the start() function
    for(int bar = Bars-1; bar >= 0; bar--)
    /*
          If the line named "for(int bar=Bars-1; bar>=0; bar--)" is replaced with the four lines below

          for recounting of the indicator on only last bars 
          at each launch of the start() function, this indicator will work properly 
          only on historical data:
 
         int  limit,counted_bars=IndicatorCounted();
         if(counted_bars>0) counted_bars--;
         limit=Bars-1-counted_bars;
         for(int bar=limit; bar>=0; bar--)
         */
      {
        // Variables e1,e2,e3,e4,e5,e6 are functions of themselves 
        // calculated on the preceding bar  
        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);
  }
//+------------------------------------------------------------------+


In this case, the most simple way to solve the problem would be replacing of the variables I mentioned in the indicator code with indicator arrays:

//+------------------------------------------------------------------+
//| 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[];
//---- Turning of variables into buffers
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);
//---- Writing of variables into indicator buffers
   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()
  {
//----+ check whether the amount of bars is sufficient for correct 
//      calculation of the indicator
   if(Bars - 1 < T3_Period)
       return(0);
//----+ Entering of integer variables and obtaining of bars already counted
   int MaxBar, limit, counted_bars = IndicatorCounted();
//---- check for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last counted bar must be re-counted
   if(counted_bars > 0) 
       counted_bars--;
//---- determining of the oldest bar number, starting from which
//     all bars will be re-counted
   MaxBar = Bars - 1 - T3_Period;
//---- determining of the oldest bar number, starting from which
//     only new bars will be re-counted
   limit = (Bars - 1 - counted_bars);
//---- initialization of null
   if(limit > MaxBar)
     {
       for(int bar = Bars - 1; bar >= limit; bar--)
           MapBuffer[bar] = 0.0;
       limit = MaxBar; 
     }
//+--- basic loop of indicator calculation
   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];
     } 
//+--- termination of the basic loop
   return(0);
  }
//+----------------------------------------------------------------+

The problem has been solved, but, in this case, it is a particular solution of this specific problem: there can be much more variables, and this is why by no means always all variables can be written as indicator buffers. In such situations, the most general solution of the problem could be the following actions. To make a normal multiple recalculation of an indicator, we need only those variables values that are obtained after calculation of the indicator on the second bar, so it is better and more comprehensive to re-count not only the last, but also the last but one bar!



Therefore, values of these variables at the end of the loop on the second bar must be memorized in some memory variables that are not local in any indicator program block, i.e. declared at the beginning of the indicator code. These memory variables can be made statistical and then introduced into the int start() function at the beginning of the text. And at every launch of the start() function before the indicator calculation loop, if the amount of counted bars is not equal to zero (if (IndicatorCounted()!=0), assign values from these memory variables to the actual indicator's variables. After that, the uncounted bars can be calculated on the same code as all bars from the beginning! It is sometimes better to memorize these variables not at the end of the loop on the second bar, but at the beginning on the first bar. This is how we will act in our further examples! Below is the same indicator written considering these ideas:



//+-------------------------------------------------------------------------+
//|                                                           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;
//---- introduction of variables to save variables 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;
//---- initialization complete
   return(0);
  }
//+---------------------------------------------------------------------------+
//| T3 iteration function                                                     |
//+---------------------------------------------------------------------------+
int start()
  {
//----+ check whether the amount of bars is enough for correct 
//      indicator calculation
   if(Bars-1 < T3_Period)
       return(0);
//----+ introduction of integer variables and getting of bars already counted
   int MaxBar, limit, counted_bars = IndicatorCounted();
//---- check for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last counted bar must be re-counted
   if(counted_bars > 0) 
       counted_bars--;
//---- determining of the oldest bar number, starting from which 
//     all bars will be re-counted
   MaxBar = Bars - 1 - T3_Period;
//---- determining of the oldest bar number, starting from which 
//     only new bars will be re-counted
   limit = (Bars - 1 - counted_bars);
//---- initialization of null
   if(limit > MaxBar)
     {
       for(int bar = Bars - 1; bar >= MaxBar; bar--)
           MapBuffer[bar] = 0.0;
       limit = MaxBar; 
     }
//+--- before the basic loop of indicator calculation, restore values
//     of variables as they were after counting on the second bar
//+--- restore values of the variables +=======+
   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);
     }
//+--- +==========================================+
//+--- Basic loop to calculate the indicator
   for(bar = limit; bar >= 0; bar--)
     {
       //+--- Memorize values of variables as they were after 
       //     the second bar
       //+--- Save values of the variables +=============+ 
       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;
     } 
   //+--- terminate the basic loop
   return(0);
  }
//+-----------------------------------------------------------------+

Being compared to the previous one, this indicator needs much less information to restore variables in the memory. But it looks more complicated than the previous one!

The part of the code of the same indicator given below inside of the int start() function for the most advanced developers of expert advisors who combine an EA with indicators. In this code, the EA will always calculate the indicator value only on the null bar. It is natural that this variation of the code suits only to test expert on historical data. For expert advisor's online operation, one has to have another variation of the code from the indicator that we have considered in our last example. It is supposed that, if a new null bar has started to form, the indicator value on the first bar is completely counted and should not be re-counted. The same is applicable to variables that are memorized when the bar changes. It must be noted here that, if such a complicated code is not written for the expert, it will be impossible to understand what such an expert will count and on what basis it will trade! If, instead of this code, to use the proved variation with the complete re-count of the indicator on every tick on all bars only to get the value on the last, null bar, we can wait for results during a month!

int start()
  {
//----+ check whether the amount of bars is enough for correct 
//      indicator calculation
   if(Bars-1 < T3_Period)
       return(0);
//---- determine the oldest bar number, starting from which 
//     all bars will be re-counted
   int MaxBar = Bars - 1 - T3_Period;
//---- initialization of null
   if(MaxBar = 0)
       for(int bar = Bars - 1; bar > 0; bar--)
           MapBuffer[bar] = 0.0;
//+--- before basic indicator calculation, restore values 
//     of variables as they were after calculation on the first bar
//+--- restoring values of variables +=======+
   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);
         }
//+--- +==============================================+
//+--- Memorize values of variables as they were after 
//     the first bar
//+--- Saving of values of variables +================+ 
   if(Tnew0 != time2)
     {
       time2 = Tnew0;
       E1 = e1; 
       E2 = e2; 
       E3 = e3; 
       E4 = e4; 
       E5 = e5; 
       E6 = e6;
     }
//+---+============================================+
 
//+--- indicator calculation (calculation is made always 
//     only on the null bar)
   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;
//----+ ------------------------------------------------+
//----+ The code of your expert must be placed here     |
//----+ ------------------------------------------------+
   return(0);
  }
//+----------------------------------------------------------------+


Of course, the code becomes some more complicated!

Below is the exemplary indicator, in which, for it to work correctly, only one logical variable must be restored at every launch of the int start() function. But, without this almost imperceivable change, indicator will not work properly:


//+------------------------------------------------------------------+
//|                                                   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 [];
//---- 
double VALUE1, VALUE2, Swing = 1, OLDSwing;
//---- Introduction of variables for multiple re-count of bar 
int time2, SWING;
//+---------------------------------------------------------------------+
//| 3LineBreak initialization function                                  |
//+---------------------------------------------------------------------+ 
int init()
  {   
//---- Chart is performed as a hystogram 
   SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexStyle(1, DRAW_HISTOGRAM);
//---- 2 indicator buffers are used for counting.
   SetIndexBuffer(0, HighBuffer);
   SetIndexBuffer(1, LowBuffer );
//---- setting of indicator values that will not be visible on the chart
   SetIndexEmptyValue(0, 0);
   SetIndexEmptyValue(1, 0);
//---- names for data windows and labels for subwindows.
   IndicatorShortName("3LineBreak");
   SetIndexLabel   (0, "3LineBreak");
//---- setting of the bar number, starting from which the indicator 
//     will be drawn  
   SetIndexDrawBegin(0, Lines_Break);
   SetIndexDrawBegin(1, Lines_Break);
//---- termination of the initialization
   return(0);
  }
//+------------------------------------------------------------------+
//| 3LineBreak iteration function                                    |
//+------------------------------------------------------------------+ 
int start()
  {
//----+ Introduction of integer variables and getting of bars already counted
   int MaxBar, limit, counted_bars = IndicatorCounted();
//---- check for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last counted bar must be re-counted )
   if(counted_bars > 0) 
       counted_bars--;
//---- determining of the oldest bar number, starting from which 
//     all bars will be re-counted
   MaxBar = Bars - 1 - Lines_Break;
//---- determining of the oldest bar number, starting from which 
//     only new bars will be re-counted
   limit = (Bars - 1 - counted_bars);
//---- initialization of null
   if(limit > MaxBar)
     {
       for(int bar = limit; bar > MaxBar; bar--)
         { 
           HighBuffer[bar] = 0.0; 
           LowBuffer[bar] = 0.0; 
         }
       limit=MaxBar;
     }
//----
//+--- restoring of values of variables +================+
   int Tnew = Time[limit+1];
   if(limit < MaxBar)
   if(Tnew == time2)
       Swing = SWING; 
   else
     {
       if(Tnew > time2)
           Print("ERROR01");
       else 
           Print("ERROR02");
       return(-1);  
     }
//+--- +==================================================+
//+--- basic loop of indicator calculation
   for(bar = limit; bar >= 0; bar--)
     {
       //+--- Saving of values of variables +=============+ 
       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 == 1 &&  Low [bar] < VALUE2) 
           Swing = -1;
       if(OLDSwing == -1 &&  High[bar] > VALUE1) 
           Swing = 1;
       //----
       if(Swing == 1)
         { 
           HighBuffer[bar] = High[bar]; 
           LowBuffer [bar] = Low [bar]; 
         }
       if(Swing == -1)
         { 
           LowBuffer[bar] = High[bar]; 
           HighBuffer[bar] = Low[bar]; 
         }   
     }
//+--- termination of the basic loop
   return(0);
  }

Situation below is quite the same:


//+------------------------------------------------------------------+
//|                                                  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;
//---- termination of the initialization
   return(0);
  }
//+------------------------------------------------------------------+
//| BrainTrend1 iteration function                                   |
//+------------------------------------------------------------------+
int start()
  {
//---- check whether the amount of bars is enough to calculate
   if(Bars < 11)
       return(0);
//---- Introduction of statistical memory variables for multiple 
//     recalculation of the null bar 
   static int MEMORY, time2;
//----+ Introduction of integer variables and getting of counted bars
   int limit, MaxBar,bar, counted_bars = IndicatorCounted();
//---- check for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last counted bar must be re-counted 
   if(counted_bars > 0) 
       counted_bars--;
//---- determining of the oldest bar number, starting from which 
//     all bars will be re-counted
   MaxBar = Bars - 1 - 10;
//---- determining of the oldest bar number, starting from which 
//     only new bars will be re-counted
   limit = Bars - counted_bars - 1; 
   if(limit > MaxBar)
   limit = MaxBar;
   Comment("BrainTrading Inc. System 7.0");
//+--- restoring of values of variables +================+
   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)
     {
       //+--- Saving of values of variables           +====+ 
       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--;
     } 
//+--- termination of the basic loop
   return(0);
  }

For the indicator below to work correctly, it is necessary to restore not only normal variables, but also the buffer:



//+------------------------------------------------------------------+
//|                                                  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;
//---- Introduction of variables for multiple re-counting of the null bar 
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;  
//---- change of the buffer size to the required size
   ArrayResize(Values, artp);
//---- similar change of the memory buffer size in the first measuring
//     to the required size
   ArrayResize(VALUES, artp);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| BrainTrend2 iteration function                                   |
//+------------------------------------------------------------------+
int start()
  {
//---- check whether the amount of bars is enough to calculate
   if(Bars < 11)
       return(0);
//----+ Introduction of integer variables and getting of bars already counted
   int limit, MaxBar, bar, J, counted_bars = IndicatorCounted();
//---- check for possible errors
   if(counted_bars < 0)
      return(-1);
//---- the last counted bar must be re-counted 
   if(counted_bars > 0) 
       counted_bars--;
//---- determining of the oldest bar number, starting from which 
//     all bars will be re-counted
   MaxBar = Bars - 3;
//---- determining of the oldest bar number, starting from which 
//     only new bars will be re-counted
   limit = (Bars - 1 - counted_bars);
//---- initialization of null
   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;        
         }
     } 
//----
//+--- restoring of values of variables +================+
   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);  
         }
//+--- +==================================================+
//+--- Basic loop of the indicator calculation 
   bar = limit;
   while(bar >= 0)      
     {  
       //+--- Saving values of variables +================+ 
       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--;
     }  
//+--- termination of the basic loop
   return(0);
  }

Well, and finally, the well-beloved indicator ZigZag that, as we can see from analysis, is re-counted on all bars, too, and, therefore, represents the problem in question! I suppose it to be even unnecessary to put th code of this indicator in this present article since it can always be taken from the "indicators" folder of the MetaTrader 4 Client Terminal.

Of course, in this indicator, the code for more saving use of computer resources written considering all the above ideas would look rather catchy, awesome and cumbersome. In this situation, we can act differently. If, after recalculation of the indicator, we register coordinates and values of the last and the next to last inflection of the ZigZag indicator broken line, we will be able in future, at the next launch of the int start() function quite correctly re-count the non-counted values of the indicator from the nearest of these two coordinates. And the incomplete count of the last two peaks can be taken from memory. It must be added that this indicator does not work quite correctly and periodically creates absolutely nonnormal hills to do away with, and it would be highly desirable to combine thios task with the previous one. Since at least three peaks of the ZigZag indicator are always required to remove hills from the chart, we will start calculation of the indicator exactly from the туче to last from the beginning of the indicator's broken line inflection. this is how the code should be changed, in such a case:


//+------------------------------------------------------------------+
//|                                                       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()
  {
    //+ check whether the amount of bars is sufficient for correct
    //  calculation of the indicator
    if(Bars - 1 < ExtDepth)
        return(0);
    //+ Introduction of integer memory variables to re-count the indicator 
    //  on uncounted bars only
    static int time2, time3, time4;  
    //+ Introduction of floating-point variables to re-count 
    //  the indicator on uncounted bars only
    static  double ZigZag2, ZigZag3, ZigZag4;
    //+ Introduction of integer variables to re-count the indicator only
    //  on uncounted bars and getting of indicators already counted
    int MaxBar, limit, supr2_bar, supr3_bar, supr4_bar;
    int counted_bars = IndicatorCounted();
    // check for possible errors
    if(counted_bars < 0)
        return(-1);
    // the last counted bar must be re-counted
    if(counted_bars > 0) 
        counted_bars--;
    //----+ Introduction of variables    
    int shift, back, lasthighpos, lastlowpos;
    double val, res, TempBuffer[1];
    double curlow, curhigh, lasthigh, lastlow;
    // determining of the oldest bar number, starting from which

    // all bars will be fully re-counted
    MaxBar = Bars - ExtDepth; 
    // determining of the start bar number in the loop, starting from 
    // which new bars will be re-counted
    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("Start bar was not found,",
            " the indicator will be re-counted on all bars" );
          }
      }
    // initialization of null
    if(limit >= MaxBar) 
      {
        for(shift = Bars - 1; shift >= MaxBar; shift--)
            ZigZagBuffer[shift] = 0.0; 
        limit = MaxBar; 
      } 
    // change of the temporary buffer size
    if(ArrayResize(TempBuffer, Limit + ExtBackstep + 1)!=
       limit + ExtBackstep + 1)
        return(-1);
    //+ start of the first large loop
    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;
      }
    //+ end of the first large loop 
    // final cutting 
    lasthigh = -1; 
    lasthighpos = -1;
    lastlow = -1; 
    lastlowpos = -1;
    //----+ start of the second large loop
    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;
          }
      }
    //+ end of the second large loop
    //+ start of the third loop
    for(shift = limit; shift >= 0; shift--)
      {
        res = TempBuffer[shift];
        if(res != 0.0) 
            ZigZagBuffer[shift] = res;
      }
    //+ end of the third loop
    //+ Restoring of values of the indicator buffer that 
    //  could be lost 
    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;
      }
    //+ correction of hills 
    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;      
          }
      } 
    //+ memorizing of the last three inflections of the ZigZag and 
    //  the indicator values at these points 
    time2 = Time[bar2];
    time3 = Time[bar3];
    time4 = Time[bar4];
    ZigZag2 = vel2;  
    ZigZag3 = vel3; 
    ZigZag4 = vel4; 
    //---- completion of calculating the indicator values
    return(0);
  }
 //---+ +----------------------------------------------------------+


As it stands now, ZigZag is much less voracious of precious computer resources that, from the previous experience, are unfotunately always insufficient no matter how many of them are available! I changed the name of the indicator buffer for the more transparent "ZigZagBuffer". I took the second buffer with temporary data off the indicator buffers since there is no necessity to have it there and changed its name for "TempBuffer". And in three loops of indicator calculation, I introduced variable "limit" as the start bar number, from which re-count of only uncounted bars will start.

It is natural that every indicator is unique. And it is hardly possible to create one approach that would work equally well on all indicators. But I suppose that general ideas of such an approach are quite clear:

1. The most difficult is to determine all variables that have values accumulated on previous ticks. It must be taken into consideration that it is sometimes necessary to memorize some variables and bar numbers, the bar can change on the next ticks and these saved values will not have any sense then. In such a case, the time of bar opening must be memorized (MEMORY_bar_time = Time[lastlowBar]). Then the current state of the bar can be restored using this memorized time (iBarShift(NULL,0,MEMORY_bar_time, TRUE)). It can happen that there are several loops of indicator calculation and every loop contains variables to be memorized, the same variable in different loops having to be memorized separately at the end on the second and the first bar or at the beginning of the loop on the first and null bar.
2. It is desirable to introduce variables with the names that correspond with the initial variables. It is better to declare these variables as non-local at the beginning of the indicator code. These variables can often be declared as static ones in the indicator code itself immediately after the start() operator.
3. The code of restoring of variables must be introduced before all loops of the indicator calculation.
4. In every loop containing actual variables, the code of memorizing these variables should be added.


At the end, I just have to add that all indicators used as examples in this article were taken from open sources at various forums in internet. I gave them here only as examples to correct errors they contin and am not responsible for distribution, copying, recompiling thereof, or for any copyright violations possibly occuring due to somebody's placing thereof on public websites.

Nikolay Kositsin

Translated from Russian by MetaQuotes Software Corp.
Original article: http://articles.mql4.com/ru/articles/1411

Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/1411

Attached files |
NullBarReCount.zip (12.86 KB)
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.

How to create an indicator of non-standard charts for MetaTrader Market How to create an indicator of non-standard charts for MetaTrader Market

Through offline charts, programming in MQL4, and reasonable willingness, you can get a variety of chart types: "Point & Figure", "Renko", "Kagi", "Range bars", equivolume charts, etc. In this article, we will show how this can be achieved without using DLL, and therefore such "two-for-one" indicators can be published and purchased from the Market.