Recuento múltiple de barras nulas en algunos indicadores

Nikolay Kositsin | 12 febrero, 2016

En este artículo, me gustaría hablar del problema del recuento múltiple del valor del indicador en la Terminal de Cliente de MetaTrader 4 cuando cambia la barra nula. Su esencia es que usted pueda encontrar en la web buenos indicadores que funcionen normalmente sólo en datos del historial pero que, cuando la plataforma de trading se conecte al servidor, dé resultados que, por decirlo suavemente, no tengan relación con los algoritmos establecidos en los indicadores.

Normalmente, los creadores de estos indicadores, tras pelearse con el problema, finalmente encuentran la solución más fácil, pero no la más inteligente: Simplemente hacen que los indicadores recalculen el valor del indicador de todas las barras del gráfico cada vez que se inicia la función int_start() para calcular el valor de una sola barra nula.

A simple vista parece que con este método se resuelven todos los problemas pero, en realidad, sólo se trasladan a otra área. Un indicador así absorbe los recursos, y provoca que el ordenador se ralentice y deje de responder.

Para solucionar este problema, lo primero que se debería hacer sería pensar en la idea general de cómo está construido el indicador. Cada indicador es una función de algunas variables:


Indicador = Función (Variable1, Variable2, Variable3, etc.)

Todos los indicadores se pueden dividir en dos grandes grupos:

  • Grupo 1 - indicadores en los que todas las variables están determinadas sólo por los precios de seguridad y variables externas; estas variables se pueden calcular por cualquier barra sin que se afecten con los valores de las mismas variables en barras anteriores.

  • Grupo 2 -. indicadores en los que al menos una variable depende del valor obtenido como el resultado del cálculo del indicador en la barra anterior:

    VariableN(i) = Función(VariableN(i+1)),

    donde el valor de la izquierda se obtuvo en la barra i y el valor en los paréntesis de la derecha se obtuvo en la barra +1. O al menos una variable depende de otra variable, de la que se obtuvo el valor en el cálculo del indicador de las barras anteriores.

    VariableN(i) = FunciónR(VariableX(i+1)),

    donde, de forma similar, el valor de la izquierda se obtuvo en la barra i y el valor en los paréntesis de la derecha se obtuvo en la barra i+1.

Los indicadores que nos interesan son los del Ggrupo 2, ya que son los que generan los problemas de los que se habla arriba. Cuando un indicador se recuenta en la barra inválida, esas variables que, lógicamente, deberían tener los mismos valores que las otras barras, empiezan a recontarse en todas las barras, incluso si el indicador se ha escrito como es debido para recontar sólo la barra inválida utilizando la función IndicatorCounted(). Este indicador ha demostrado no tener ninguna utilidad para trabajar en el mercado a tiempo real. Una vez explicado todo esto, vamos al indicador específico:

//+------------------------------------------------------------------+
//|                                                           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);
  }
//+------------------------------------------------------------------+

En este caso, la manera más simple de resolver el problema sería reemplazando las variables que he mencionado del código del indicador con selecciones del indicador:

//+------------------------------------------------------------------+
//| 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);
  }
//+----------------------------------------------------------------+

El problema se ha resuelto pero, en este caso, es una solución particular de este problema específico: puede haber muchas más variables, por ese motivo, de ningún modo las variables se pueden escribir siempre como un amortiguador de indicadores. En estas situaciones, la solución general para el problema podría ser la siguiente: Para hacer una recalculación normal de un indicador, necesitamos sólo los valores de las variables que se obtienen después de un cálculo del indicador en la segunda barra, por lo que es mejor, y más completo, recontar, además de la última, la penúltima barra.

Por lo tanto, los valores de estas variables al final del bucle en la segunda barra, deben guardarse en alguna memoria de variables que no sea local en ningún bloque de programa del indicador, es decir, que se declare al principio del código del indicador. Estas memorias de variables se pueden hacer estadísticamente y luego introducirlas en la función int start() al principio del texto. Y en cada inicio de la función start() antes del bucle de cálculo del indicador, si la cantidad de las barras contadas no es igual a cero (si (IndicatorCounted()!=0), asigne los valores de esa memoria de variables a las variables actuales del indicador. Después de eso, las barras que no se han contado se pueden calcular en el mismo código que todas las barras del principio. A veces es mejor memorizar estas variables al principio del bucle en la primera barra. Así es como lo haremos en los siguientes ejemplos. Por debajo del mismo indicador escrito consideramos estas 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);
  }
//+-----------------------------------------------------------------+

Comparándolo con el anterior, este indicador necesita mucha menos información para restaurar las variables en la memoria. Pero parece más complicado que el anterior.

La parte del código del mismo indicador que se da anteriormente dentro de la función int start(), está destinada a los desarrolladores de asesores expertos más avanzados que combinan un AE con indicadores. En este código, el AE siempre calculará el valor del indicador sólo en la barra nula. Es normal que esta variación del código encaje sólo para probar los expertos en el historial. Para el funcionamiento de los asesores expertos online, hay que tener otra variación del código del indicador que consideramos en el último ejemplo. Se supone que, si se empieza a formar otra barra inválida, el valor del indicador en la primera barra se cuenta por completo y no debería ser recontado. Esto mismo se aplica a las variables que se memorizan cuando cambia la barra. Hay que tener en cuenta que, si un código tan complicado no se escribe para el experto, sería imposible comprender que contaría un experto así y en qué bases hará el trading. Si, en lugar de este código, se usa la variación demostrada con el recuento completo del indicador en cada tick en todas las barras para obtener el valor de la última barra inválida, los resultados pueden tardar un mes.

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);
  }
//+----------------------------------------------------------------+


Evidentemente, el código se hace un poco más complicado.

Debajo está el indicador ejemplo, en el que, para que funcione correctamente, sólo se debe restaurar una variable lógica en cada inicio de la función inst start(). Pero sin este cambio casi imperceptible, el indicador no funcionará correctamente:

//+------------------------------------------------------------------+
//|                                                   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);
  }

La situación de abajo es casi la misma:

//+------------------------------------------------------------------+
//|                                                  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);
  }
Para que el indicador de abajo funcione correctamente, es necesario restaurar tanto las variables normales como el amortiguador.

//+------------------------------------------------------------------+
//|                                                  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);
  }


Y finalmente, el indicador ZigZag que, como podemos ver en el análisis, se recuenta también en todas las barras y, por lo tanto, representa el problema que nos ocupa. Supongo que no es necesario poner el código de este indicador en este artículo, ya que se puede obtener del archivo "indicadores" de la Terminal de Cliente de MetaTrader 4.

Por supuesto, en este indicador, el código para que el ordenador ahorre más recursos, teniendo en cuenta todas las ideas anteriores, parecería más bien sorprendente, increíble y complicado. En esta situación, podemos actuar de forma diferente. Si, después del cálculo del indicador, registramos las coordenadas y valores de la última y penúltima inflexión de la línea quebrada del indicador ZigZag, podremos, en un futuro, en el siguiente inicio de la función int start(), recontar correctamente los valores no contados del indicador desde la más cercana de estas dos coordenadas. Y se podrán obtener de la memoria el recuento incompleto de los últimos dos picos. Hay que añadir que este indicador no funciona tan bien y que puntualmente crea picos absolutamente anormales que hay que eliminar, y sería recomendable combinar esta tarea con la anterior. Ya que al menos tres picos del indicador ZigZag tienen que eliminar curvas del gráfico, empezaremos el cálculo del indicador exactamente desde el principio de la línea de inflexión del indicador. Dado el caso, así es como debería cambiarse el código:


//+------------------------------------------------------------------+
//|                                                       ZigZag.mq4 |
//|                      Copyright © 2005, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/           |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link      "https://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);
  }
 //---+ +----------------------------------------------------------+


Tal y como está puesto ahora, ZigZag requiere muchos más recursos del ordenador que, por experiencia, siempre son insuficientes, sin importar cuántos haya disponibles. Cambié el nombre del búfer del indicador por "BúferZigZag". Eliminé de los búfers del indicador el segundo amortiguador con datos temporales, ya que no es necesario tenerlo ahí, y le cambié el nombre por "BúferTemp". Y en tres bucles de cálculo del indicador, introduje la variable "limit" como el inicio del número de la barra, que empezaría el recuento de las barras que no se habían contado.

Es normal que cada indicador sea único. Y es poco probable crear una aproximación que funcionara igual de bien en todos los indicadores. Pero creo que las ideas generales de una aproximación así están bastante claras:

1. Lo más difícil es determinar todas las variables que tienen valores acumulados en los ticks anteriores. Hay que tener en cuenta que a veces hay que memorizar algunas variables y números de barras, la barra puede cambiar en los siguientes ticks, y los valores guardados no tendrán ningún sentido. En tal caso, hay que memorizar la hora en la que se abrió la barra (MEMORY_bar_time = Time [lastlowBar]). Luego se puede restaurar el estado actual de la barra utilizando esta hora memorizada (iBarShift(NULL,0,MEMORY_bar_time, TRUE)). Puede darse el caso de que haya varios bucles de cálculo del indicador, y cada uno de ellos contenga variables para memorizar; la misma variable en diferentes bucles tendrían que memorizarse por separado al final, en la segunda y primera barra, o al principio del bucle en la primera barra y en la barra inválida.
2. Es conveniente introducir variables con los nombres que corresponden a las iniciales de las variables. Es mejor declarar estas variables como no-locales al principio del código del indicador. Estas variables pueden declararse a menudo como estáticas en el mismo código del indicador, justo después del funcionamiento de start().
3. El código para restaurar las variables se debe introducir antes de todos los bucles de cálculo del indicador.
4. En cada bucle con variables actuales, debe añadirse el código de memorización de estas variables.


Para terminar, sólo tengo que añadir que todos los indicadores que se han utilizado como ejemplos en este artículo, se obtuvieron de fuentes abiertas en varios foros de internet. Sólo los he usado aquí como ejemplos para corregir errores y no soy responsable de su distribución, copia, recopilación; ni de ninguna violación de los derechos que pueda ocurrir si alguien los publica en páginas web públicas.