El Indicador ZigZag: Nuevo Enfoque y Soluciones

Sergey Pavlov | 28 mayo, 2014

Introducción

Cualquier trader seguramente sabe que el indicador ZigZag está diseñado para el análisis de movimientos de precio de una amplitud especificada o mayor. Una línea ZigZag es una línea rota cuyos nodos se encuentran en puntos altos y bajos del gráfico de precios.

Hay muchas variaciones de este indicador: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16. Y sin embargo, un buen número de creadores de programas MQL5 optan por crear su propio ZigZag 'ideal'. Las principales desventajas del indicador ZigZag son los retrasos, las marcaciones incorrectas de nuevos cuestionables (barra externa) y un rendimiento o no satisfactorio.

En mi opinión, la implementación más elegante de ZigZag la propuso Yuri Kulikov (Yurich). Además, hay algunos artículos de MQL4 muy buenos, tales como "Layman's Notes: ZigZag..." (“Notas de Layman: ZigZag…”) y "Show Must Go On, or Once Again about ZigZag" (“El Espectáculo Debe Continuar, o de Nuevo Sobre ZigZag”). El tema parece haber sido explorado extensivamente, y hay un buen número de publicaciones disponibles. Sin embargo, hay algo en este tema que resulta atrayente. Ahora también ha captado mi atención, en particular la posibilidad de crear un indicador ZigZag avanzado.

Este artículo describe un método para crear un ZigZag avanzado usando el indicador Envelopes. Suponemos que podemos obtener una determinada combinación de parámetros centrada para una serie de Envelopes.

 

Un Método para Crear un Indicador ZigZag Avanzado

Nos pondremos un objetivo: encontrar coordenadas de dos modos, el actual y el nodo pronosticado (Fig. 1). El nodo actual es un nodo que no se ha completado todavía cuyas coordenadas todavía se están buscando o ajustando. Además, siempre se encuentra en la barra actual (cero). Aunque se encuentra el futuro, un nodo pronosticado muestra el nivel calculado del próximo nodo de ZigZag.

Predicción de nuevos nodos de ZigZag: el nodo actual y el próximo nodo.

Fig. 1. Predicción de nuevos nodos de ZigZag: el nodo actual y el próximo nodo.

El objetivo ya está configurado y ya tenemos una idea de cómo usar la Media Móvil (MA, por sus siglas en inglés) Envelopes como base para crear un indicador avanzado (Fig. 2). Buscaremos un Envelopes cuya desviación de nodos ZigZag sea mínima. Parece bastante lógico que los Envelopes para los picos y las caídas de ZigZag se busquen por separado.

Indicadores ZigZag y Media Móvil Envelopes.

Fig. 2. Indicadores ZigZag y Media Móvil Envelopes.

Para incrementar el significado estadístico del pronóstico, en lugar de usar solo un indicador Envelopes o incluso 10 de ellos, deberíamos usar un conjunto de 100 o más indicadores con diferentes datos de entrada. Serán diferentes en el período medio de la línea del indicador principal y el precio usado (High para picos y Low para caídas). Presentaremos las siguientes notas y fórmulas:

Tenemos dos conjuntos de indicadores: uno para picos y el otro para caídas (con unos 100 indicadores en cada uno). Calcularemos la desviación de los nodos de ZigZag de la línea principal del indicador Envelopes para cada uno de los indicadores en el conjunto y encontraremos la media aritmética de desviaciones para cada indicador del conjunto usando las fórmulas mencionadas arriba. La siguiente figura demuestra un diagrama de desviaciones respecto a los nodos identificados ZZ de la línea principal ENV para un indicador.

Diagrama de desviaciones de nodos ZZ de ENV.

Fig. 3. Diagrama de desviaciones de nodos ZZ de ENV.

La media aritmética de las desviaciones se usará para determinar el nivel en el cual la línea principal del indicador Envelopes se debería mover para dibujar las bandas de envoltura. De modo que necesitaremos la media aritmética de desviaciones de los picos de ZigZag para dibujar la línea superior, y la media aritmética de desviaciones de las caídas para dibujar la línea inferior del indicador Envelopes.

Usaremos las líneas superior e inferior de Envelopes para encontrar puntos característicos y predecir los nodos de ZigZag. De nuevo, nos interesa una serie de envolturas que consista en un conjunto de los indicadores Envelopes. La media aritmética de desviaciones de nodos ZigZag de la línea principal de una envoltura específica se calcula para cada indicador. Tras dibujar las líneas resultantes (las líneas superior e inferior) del conjunto en el gráfico, podremos ver lo siguiente:

Las líneas de Envelopes en el plano.

Fig. 4. Las líneas de Envelopes en el plano.

Si asumimos que cada línea se encuentra en un plano separado, mientras que todas juntas crean una superficie, la figura de arriba solo muestra la proyección de cada indicador en el plano de gráfico de precio. Una imagen en 3D de estas líneas tendría más o menos el siguiente aspecto:

Las líneas de Envelopes en 3D.

Fig. 5. Las líneas de Envelopes en 3D.

Revisemos rápidamente nuestra geometría. Imagine que el conjunto de líneas del indicador Envelopes es una superficie en 3D. Tome un plano perpendicular al gráfico de precio y corte la superficie en la barra actual (cero).

Como resultado, obtendremos una sección cruzada de la superficie representando una curva (las figuras de arriba demuestran un caso especial donde la curva es una línea recta). Para realizar el pronóstico, basta con tener las coordenadas de cada punto de la curva que después se usarán en cálculos.

Necesitaremos las siguientes características de sección cruzada: punto máximo y mínimo, así como centro de gravedad de la sección cruzada (la media aritmética de todos los valores de punto). Los puntos característicos obtenidos se proyectarán en la barra actual (cero), con los datos relevantes almacenados en el historial. Estos puntos característicos servirán como la base para los nodos ZigZag actuales y los siguientes.

Puesto que la búsqueda para de las bandas de Envelopes se lleva a cabo por separado para picos y caídas, obtendremos como resultado dos secciones cruzadas: una para picos y otro para caídas.

Para obtener el pronóstico, usaremos el punto característico más cercano. Por ejemplo, al buscar un pico, tomaremos los puntos característicos de la sección cruzada resultantes de la intersección de la superficie de las líneas superiores del indicador Envelopes con un plano cortante. Inversamente, para encontrar una caída, tomaremos dos puntos característicos de la sección cruzada resultantes de la intersección de la superficie de las líneas superiores del indicador Envelopes con un plano cortante.

 

Simular el Nuevo Indicador

Ahora que ya hemos definido el método, creemos el indicador. Primero encontraremos los últimos nodos del indicador ZigZag y los dibujaremos en el gráfico. Para ello, usaremos la clase AdvancedZigZag escrita para la tarea que tenemos entre manos:

//+------------------------------------------------------------------+
//|                                               AdvancedZigZag.mqh |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/es/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/es/users/DC2008"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                 GetExtremums.mqh |
//+------------------------------------------------------------------+
#include <GetExtremums.mqh>   // author of the code Yurich
#property copyright "Copyright 2012, Yurich"
#property link      "https://www.mql5.com/es/users/Yurich"
//+------------------------------------------------------------------+
//| ZigZag node structure                                            |
//+------------------------------------------------------------------+
struct MqlZigZag
  {
   double            price;   // Node coordinate
   datetime          t;       // Time
  };
//+------------------------------------------------------------------+
//| The AdvancedZigZag class                                         |
//+------------------------------------------------------------------+
class AdvancedZigZag
  {
private:
   MqlRates          rt[];
   dextremum         zz[];
   int               history;
   double            amplitude;
public:
   dextremum         zHL[];
   MqlZigZag         zzH[],zzL[];
   int               Count(const double range);
   int               Read(const int nodes);
                     AdvancedZigZag(const int bars);
                    ~AdvancedZigZag();
  };
//+------------------------------------------------------------------+
//| Class constructor                                                |
//+------------------------------------------------------------------+
AdvancedZigZag::AdvancedZigZag(const int bars)
  {
   history=bars;
   amplitude=0;
  }
//+------------------------------------------------------------------+
//| The Read method of the class                                     |
//+------------------------------------------------------------------+
int AdvancedZigZag::Read(const int nodes)
  {
   CopyRates(NULL,0,TimeCurrent(),history,rt);
   int cnt=GetExtremums(amplitude,rt,zHL,nodes);
   return(cnt);
  }
//+------------------------------------------------------------------+
//| The Count method of the class                                    |
//+------------------------------------------------------------------+
int AdvancedZigZag::Count(const double range)
  {
   amplitude=range;
   CopyRates(NULL,0,TimeCurrent(),history,rt);
   int cnt=GetExtremums(amplitude,rt,zz);
   ArrayResize(zzH,cnt);
   ArrayResize(zzL,cnt);
   int h=0;
   int l=0;
   for(int i=0; i<cnt; i++)
     {
      if(zz[i].type>0)
        {
         zzH[h]=(MqlZigZag)zz[i];
         h++;
        }
      else
        {
         zzL[l]=(MqlZigZag)zz[i];
         l++;
        }
     }
   ArrayResize(zzH,h);
   ArrayResize(zzL,l);
   return(cnt);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
AdvancedZigZag::~AdvancedZigZag()
  {
  }

Hay dos métodos en total:

La biblioteca GetExtremums (de Yury Kulikov) también será necesaria para buscar nodos.

Pongamos a prueba el indicador en un Asesor Experto. ¿Por qué un Asesor Experto y no un indicador? Por supuesto, es una cuestión de gustos, pero parece ser que es más eficiente de esta manera. Las prestaciones gráficas del Asesor Experto son sin duda más débiles, pero ganamos en rendimiento puesto que los indicadores con el mismo símbolo operan en una sola corriente, mientras que cada Asesor Experto opera en su propia corriente separada. Echamos un vistazo al código:

//+------------------------------------------------------------------+
//|                                                   two_Comets.mq5 |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/es/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/es/users/DC2008"
#property version   "1.00"
#include <AdvancedZigZag.mqh>
//--- Depth of history for the indicator calculation
input int      depth_stories=5000;  // Depth stories for calculating the indicator [bars]
//--- Minimum ZigZag amplitude value
input int      amplitude=100;        // The minimum value of the amplitude of the indicator [points]
//--- Declaring the class
AdvancedZigZag Azz(depth_stories);
//---
#define NUMBER_MA   227
#define START_MA    5
//--- macros
#define SIZE(i)                     (double)i*0.3<1?1:(int)(i*0.25)
#define ObjF1                       ObjectSetString(0,name,OBJPROP_FONT,"Wingdings")
#define ObjF2                       ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER)
#define ObjF3(T)                    ObjectSetInteger(0,name,OBJPROP_TIME,T)
#define ObjF4(P)                    ObjectSetDouble(0,name,OBJPROP_PRICE,P)
#define ObjF5(size)                 ObjectSetInteger(0,name,OBJPROP_FONTSIZE,size)
#define ObjF6(code)                 ObjectSetString(0,name,OBJPROP_TEXT,CharToString(code))
#define ObjF7(clr)                  ObjectSetInteger(0,name,OBJPROP_COLOR,clr)
#define ObjF8                       ObjectSetInteger(0,name,OBJPROP_COLOR,clrMagenta)
#define ObjF9                       ObjectSetInteger(0,name,OBJPROP_WIDTH,3)
#define ObjF10                      ObjectSetInteger(0,name,OBJPROP_BACK,true) 
#define ObjFont                     ObjF1;ObjF2;
#define ObjCoordinates(T,P)         ObjF3(T);ObjF4(P);
#define ObjProperty(size,code,clr)  ObjF5(size);ObjF6(code);ObjF7(clr);
#define ObjZZ                       ObjF8;ObjF9;ObjF10;
//---
double      MA[1],sumHi[NUMBER_MA],sumLo[NUMBER_MA];
int         handle_MA_H[NUMBER_MA],handle_MA_L[NUMBER_MA];
datetime    t[1];
int         H,L;
int         t_min,t_max;
int         err=-1;
double      sumH[2],maxH[2],minH[2];
double      sumL[2],maxL[2],minL[2];
string      name;
int         count;
int         shift;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   shift=PeriodSeconds()/30;
//--- calculation of ZigZag nodes using historical data
   Azz.Count(amplitude*Point());
   H=ArraySize(Azz.zzH);
   L=ArraySize(Azz.zzL);
   if(H<30 || L<30)
     {
      Print("Not enough data to calculate ZigZag nodes: "+
            "increase the depth of history; "+
            "or decrease the amplitude value.");
      return(-1);
     }
//---
   for(int i=0; i<NUMBER_MA; i++)
     {
      handle_MA_H[i]=iMA(NULL,0,i+START_MA,0,MODE_SMA,PRICE_HIGH);
      handle_MA_L[i]=iMA(NULL,0,i+START_MA,0,MODE_SMA,PRICE_LOW);
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0,-1,-1);
   for(int i=0; i<NUMBER_MA; i++)
     {
      IndicatorRelease(handle_MA_H[i]);
      IndicatorRelease(handle_MA_L[i]);
     }
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+

void OnTick()
  {
//--- get the current bar's opening time value
   CopyTime(NULL,0,0,1,t);
//--- ZigZag: last 7 nodes
   count=Azz.Read(7);
   for(int i=1; i<count; i++)
     {
      name="ZZ"+(string)i;
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,10);
      ObjectSetInteger(0,name,OBJPROP_BACK,true);
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[i-1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[i-1].time);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,Azz.zHL[i].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,Azz.zHL[i].time);
     }
//--- check for integrity of preliminary calculations
   if(err<0)
     {
      //--- calculate the sums of deviations of the nodes from MA for ZigZag peaks
      ArrayInitialize(sumHi,0.0);
      for(int j=H-1; j>=0; j--)
        {
         for(int i=0; i<NUMBER_MA; i++)
           {
            err=CopyBuffer(handle_MA_H[i],0,Azz.zzH[j].t,1,MA);
            if(err<0) return;
            sumHi[i]+=Azz.zzH[j].price-MA[0];
           }
        }
      //--- calculate the sums of deviations of the nodes from MA for ZigZag troughs
      ArrayInitialize(sumLo,0.0);
      for(int j=L-1; j>=0; j--)
        {
         for(int i=0; i<NUMBER_MA; i++)
           {
            err=CopyBuffer(handle_MA_L[i],0,Azz.zzL[j].t,1,MA);
            if(err<0) return;
            sumLo[i]+=MA[0]-Azz.zzL[j].price;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Debemos aclarar unas cuantas cosas aquí:

De este modo, el indicador resultante dibuja los últimos siete nodos ZigZag y calcula las coordenadas de todos los demás nodos en una parte específica del historial (Fig. 6). El cálculo solo se realiza una vez, y seguiremos usando los datos calculados. Por supuesto, puede implementarlo de tal forma que permita la actualización regular de los datos, pero en este artículo nos limitaremos a un solo pase.

El indicador ZigZag (7 nodos).

Fig. 6. El indicador ZigZag (7 nodos).

Además, dibujaremos las secciones cruzadas de las superficies de los indicadores Envelopes. Para ello, añadiremos lo siguiente al método OnTick():

//--- PEAKS
   sumH[0]=0.0;
   maxH[0]=0.0;
   minH[0]=0.0;
   for(int i=0; i<NUMBER_MA; i++)
     {
      CopyBuffer(handle_MA_H[i],0,t[0],1,MA);
      double envelope=MA[0]+sumHi[i]/H;
      if(i==0 || envelope<minH[0])
        {
         minH[0]=envelope;
         t_min=SIZE(i);
        }
      if(envelope>maxH[0])
        {
         maxH[0]=envelope;
         t_max=SIZE(i);
        }
      sumH[0]+=envelope;
      name="H"+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjFont
      ObjCoordinates(t[0]-(NUMBER_MA-i*2)*shift,envelope)
      ObjProperty(SIZE(i),158,clrBlue)
     }
//--- TROUGHS
   sumL[0]=0.0;
   maxL[0]=0.0;
   minL[0]=0.0;
   for(int i=0; i<NUMBER_MA; i++)
     {
      CopyBuffer(handle_MA_L[i],0,t[0],1,MA);
      double envelope=MA[0]-sumLo[i]/L;
      if(i==0 || envelope<minL[0])
        {
         minL[0]=envelope;
         t_min=SIZE(i);
        }
      if(envelope>maxL[0])
        {
         maxL[0]=envelope;
         t_max=SIZE(i);
        }
      sumL[0]+=envelope;
      name="L"+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjFont
      ObjCoordinates(t[0]+(NUMBER_MA-i*2)*shift,envelope)
      ObjProperty(SIZE(i),158,clrGold)
     }
Nota para programadores principiantes: los operadores al final de los picos (Peaks) y las caídas (Troughs) no tienen ';' al final de la cadena de caracteres. No se trata de un error tipográfico. Se trata de macros (vea la sección de datos donde están declarados): ¡son muy útiles! Le recomiendo que los use en sus programas.

Para discernir los puntos de sección cruzada de la superficie formada por las líneas de las envolturas, los puntos varían en tamaños: cuanto mayor sea el período medio de la línea principal de los indicadores Envelopes, mayores serán los puntos (Fig. 7). Además, las acciones cruzadas rotan alrededor de un eje vertical pasando a través de la barra actual (cero) en diferentes direcciones: los picos en un ángulo de 90° a la derecha, y las caídas en un ángulo de 90° a la izquierda.

Ahora se pueden ver en el plano de gráfico de precios. Inicialmente, se encontraban horizontalmente en el plano cortante (Fig. 5) y no se podían ver. Solo nos los podíamos imaginar, sin tener idea alguna sobre su forma. Las líneas de la sección cruzada ha resultado tener una forma muy peculiar. Esto se hace también por motivos de conveniencia en el análisis gráfico. Visualmente, las secciones cruzadas se parecen a dos cometas volando:

Sección cruzada del grupo de indicadores Envelopes.

Fig. 7. Sección cruzada del grupo de indicadores Envelopes.

Procedamos al cálculo de las características de la sección cruzada: el máximo y el mínimo, así como centro de gravedad (la media aritmética). Los valores resultantes se mostrarán como puntos en la barra actual, con el tamaño de punto correspondiente con la característica relevante. Además, los guardaremos en el historial para seguir con su análisis. Así, añadiremos lo siguiente al código ya existente:

//--- PEAKS

...

//--- midi
   string str=(string)t[0];
   name="Hmidi"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],sumH[0]/NUMBER_MA)
   ObjProperty(10,119,clrBlue)
//--- max
   name="Hmax"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],maxH[0])
   ObjProperty(t_max,158,clrBlue)
//--- min
   name="Hmin"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],minH[0])
   ObjProperty(t_min,158,clrBlue)

...

//--- TROUGHS

...

//--- midi
   name="Lmidi"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],sumL[0]/NUMBER_MA)
   ObjProperty(10,119,clrGold)
//--- max
   name="Lmax"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],maxL[0])
   ObjProperty(t_max,158,clrGold)
//--- min
   name="Lmin"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],minL[0])
   ObjProperty(t_min,158,clrGold)

Ahora veamos el aspecto que tiene al representarlo gráficamente:

Características de sección cruzada

Fig. 8. Características de sección cruzada: el máximo y el mínimo, así como centro de gravedad dibujado con picos y caídas por separado.

Solo necesitamos añadir el toque final encontrando y dibujando nodos ZigZag avanzados. Reforzaremos el código añadiendo lo siguiente:

//--- ZigZag: advanced nodes
   if(Azz.zHL[0].type>0) // peak
     {
      ObjectDelete(0,"MIN");
      ObjectDelete(0,"MINfuture");
      name="MAX";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[1].time);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]);
      double price=minH[0];
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
      if(Azz.zHL[0].value>minH[0])
        {
         price=sumH[0]/NUMBER_MA;
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      if(Azz.zHL[0].value>sumH[0]/NUMBER_MA)
        {
         price=maxH[0];
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      //--- into the future
      name="MAXfuture";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,price);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,t[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,maxL[0]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]+NUMBER_MA*shift);
      if(price<maxL[0])
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,sumL[0]/NUMBER_MA);
      if(price<sumL[0]/NUMBER_MA)
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,minL[0]);
     }
   if(Azz.zHL[0].type<0) // trough
     {
      ObjectDelete(0,"MAX");
      ObjectDelete(0,"MAXfuture");
      name="MIN";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[1].time);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]);
      double price=maxL[0];
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
      if(Azz.zHL[0].value<maxL[0])
        {
         price=sumL[0]/NUMBER_MA;
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      if(Azz.zHL[0].value<sumL[0]/NUMBER_MA)
        {
         price=minL[0];
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      //--- into the future
      name="MINfuture";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,price);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,t[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,minH[0]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]+NUMBER_MA*shift);
      if(price>minH[0])
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,sumH[0]/NUMBER_MA);
      if(price>sumH[0]/NUMBER_MA)
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,maxH[0]);
     }

De este modo hemos conseguido el nuevo indicador ZigZag avanzado que pronostica la posición de nuevos nodos (Fig. 9). Los nodos mismos se encuentran en los puntos característicos de sección cruzada: el máximo, el mínimo y el centro de gravedad. El título funcional del indicador es "Two Comets" (“Dos cometas”).

Note que el tiempo requerido para completar el siguiente nodo, que está en el futuro, sigue siendo desconocido. Básicamente, solo podemos predecir una coordenada de nodo: el precio.

Nodos ZigZag pronosticados.

Fig. 9. El indicador ZigZag avanzado pronostica los nodos: el actual y el siguiente.

 

Análisis de los Resultados y Recomendaciones para Desarrolladores

Las observaciones del indicador nos muestran que:

  1. Las desviaciones de las coordenadas de nodo ZigZag de los nodos pronosticados se encuentran dentro de la región de tolerancia. El gran número de nodos permanece en la sombra de la sección cruzada correspondiente. Ciertamente, esto es solo un examen cualitativo. En los próximos artículos obtendremos resultados más precisos.
  2. ¡Las acciones cruzadas de las líneas de envoltura demuestran el impulso del comportamiento del mercado y el precio esperado! Y preste atención a la cola de la cometa que está compuesta de puntos con el período de media más pequeño (el más pequeño en tamaño). Está dirigido hacia la dirección del precio. La cola de la cometa se dobla de las formas más complicadas, y cuanto más girada esté en la dirección opuesta, mayor será la posibilidad de ver el cambio de tendencia. Simplemente observe el comportamiento del indicador en intervalos cronológicos distintos con diferentes amplitudes. ¡Es extremadamente interesante!
  3. Los puntos característicos de la sección cruzada forman líneas que pueden demostrar gran resistencia al movimiento de precios. Por tanto, se pueden considerar líneas de soporte y resistencia.
  4. Cuando los puntos del centro de gravedad de la sección cruzada se adelantan a ella (como en el caso de los picos en la Fig. 9), esto es una indicación de la presencia de una tendencia ascendente.

¡Como resultado, obtendremos un indicador muy interesante que se puede poner en práctica en una estrategia de trading!

 

Conclusión