La implementación del análisis automático de las Ondas de Elliott en MQL5

MRoVas | 25 marzo, 2014


Introducción

Uno de los métodos más populares del análisis del mercado es el análisis de las ondas. Sin embargo, este proceso es bastante complejo lo que comporta el uso de herramientas adicionales. Una de estas herramientas es el marcador  automático.

En este artículo se describe el proceso de creación del analizador automático de las Ondas de Elliott en el lenguaje MQL5. Se supone que el lector de este artículo ya está familiarizado con la teoría de las ondas. Si no es así, tiene que consultar las fuentes correspondientes.


1. Principio de las ondas de Elliott

Las ondas de Elliott es un modelo teórico del comportamiento del mercado desarrollado por Ralph Elliott según el cual el movimiento de los precios en el mercado está sujeto a la psicología de la gente y representa el proceso cíclico del cambio de las ondas de impulso por las ondas correctivas, y viceversa.

Las ondas de impulso representan una secuencia de cinco fluctuaciones del precio, y las ondas correctivas representan una secuencia de tres o cinco fluctuaciones del precio. Las ondas de impulso pueden ser de los siguientes tipos (según su forma, estructura, así como según las reglas que pueden ser aplicadas a ellas):

1. Impulsos:
Impulso
Fig. 1 Impulso
  • El final de la segunda onda nunca se extenderá más allá del punto de inicio de la primera onda;
  • La tercera onda siempre se extiende más allá de la cresta de la primera;
  • El final de la cuarta onda nunca sobrepasa la cresta de la primera onda;
  • La tercera onda nunca puede ser la más corta de todas las ondas existentes;
  • La tercera onda siempre es un impulso;
  • La primera onda puede ser un impulso, o bien una cuña (leading diagonal - diagonal de inicio);
  • La quinta onda puede ser un impulso o una diagonal;
  • La segunda onda puede tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La cuarta onda puede tomar forma de cualquier onda correctiva;
2. Cuñas:
Cuña
Fig. 2 Cuña
  • El final de la segunda onda nunca se extenderá más allá del punto de inicio de la primera onda;
  • La tercera onda siempre se extiende más allá de la cresta de la primera;
  • El final de la cuarta onda siempre va más allá de la cresta de la primera onda, pero nunca sobrepasa el inicio de la tercera onda;
  • La tercera onda nunca puede ser la más corta de todas las ondas existentes;
  • La tercera onda siempre es un impulso;
  • La primera onda puede ser un impulso, o bien una cuña (leading diagonal - diagonal de inicio);
  • La quinta onda puede ser un impulso o una diagonal;
  • La segunda onda puede tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La cuarta onda puede tomar forma de cualquier onda correctiva;
3. Diagonales:
Diagonal
Fig. 3 Diagonal
  • El final de la segunda onda nunca se extenderá más allá del punto de inicio de la primera onda;
  • La tercera onda siempre se extiende más allá de la cresta de la primera;
  • El final de la cuarta onda, por regla general, va más allá de la cresta de la primera onda, pero nunca sobrepasa el inicio de la tercera onda;
  • La tercera onda nunca puede ser la más corta de todas las ondas existentes;
  • La primera, segunda y tercera ondas pueden tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La cuarta y la quinta ondas pueden tomar forma de cualquier onda correctiva;
Las ondas correctivas se clasifican como:
4. Zigzags:
Zigzag
Fig. 4 Zigzag
  • La onda A puede tomar forma del impulso o cuña;
  • La onda C puede tomar forma del impulso o diagonal;
  • La onda B puede tomar forma de cualquier onda correctiva;
  • La onda C se extiende más allá de la cresta de la onda A;
  • El final de la onda B no sobrepasa el inicio de la onda A;
5. Planas:
Plana
Fig. 5 Plana
  • La onda A puede tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La onda B puede tomar forma de cualquier onda correctiva;
  • La onda C puede tomar forma del impulso o diagonal;
6. Doble zigzag:
Doble zigzag
Fig. 6 Doble zigzag
  • La onda W y la onda Y toman forma del zigzag;
  • La onda X puede tomar forma de cualquier onda correctiva;
  • La onda Y se extiende más allá de la cresta de la onda W;
  • El final de la onda X no sobrepasa el inicio de la onda W;
7. Triple zigzag:
Triple zigzag
Fig. 7 Triple zigzag
  • La onda W, la onda Y y la onda Z toman forma del zigzag;
  • La onda X puede tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La onda XX puede tomar forma de cualquier onda correctiva;
  • La onda Y se extiende más allá de la cresta de la onda W;
  • La onda Z se extiende más allá dela cresta de la onda Y;
  • El final de la onda X no sobrepasa el inicio de la onda W;
  • El final de la onda XX no sobrepasa el inicio de la onda Y;
8. Tres dobles:
Doble tres
Fig. 8 Doble tres
  • La onda W toma forma de cualquier onda correctiva, a excepción del triángulo;
  • La onda X y la onda Y toman forma de cualquier onda correctiva;
9. Tres triples:
Triple tres
Fig. 9 Triple tres
  • La onda W, la onda X y la onda Y pueden tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La onda XX y la onda Z pueden tomar forma de cualquier onda correctiva;
10. Triángulos contractos:
Triángulo contracto
Fig. 10 Triángulo contracto
  • La onda C nunca sale de los límites de precio de la onda B;
  • La onda D nunca sale de los límites de precio de la onda C;
  • La onda E nunca sale de los límites de precio de la onda D;
  • La onda A, la onda B y la onda C pueden tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La onda D y la onda E pueden tomar forma de cualquier onda correctiva;
11. Triángulos expansivos:
Triángulo expansivo
Fig. 11 Triángulo expansivo
  • La onda C siempre es más larga que la onda B;
  • La onda D siempre es más larga que la onda C;
  • La onda A, la onda B y la onda C pueden tomar forma de cualquier onda correctiva, a excepción del triángulo;
  • La onda D y la onda E pueden tomar forma de cualquier onda correctiva;

Los modelos de las ondas y las reglas arriba mencionadas corresponden sólo a la noción clásica sobre el análisis de las ondas.

Además, existe su concepto moderno que ha sido formado durante el estudio del mercado Forex. Por ejemplo, ha sido encontrado un nuevo modelo del triángulo oblicuo (deslizante), han sido identificadas los impulsos con el triángulo en la segunda onda, etc.

Como se puede ver en las figuras 1-11, cada onda de impulso o correctiva se compone de las mismas ondas correctivas o de impulso (líneas trazadas), pero de menor grado. Se trata de así llamada la fractalidad (ondas anidadas) de las ondas de Elliott: las ondas del mayor grado se componen de las ondas del menor grado, las que a su vez se componen de las ondas aún más pequeñas, y así sucesivamente.

Con eso podemos terminar la introducción breve en el principio ondular de Elliott y pasar al tema del etiquetado automático de las ondas.


2. Algoritmo del etiquetado automático de las Ondas de Elliott

Como seguramente ya haya entendido, el análisis de las ondas de Elliott es un proceso complejo y polifacético. Por eso desde el principio la gente ha empezado a buscar y aplicar las herramientas que ayudan facilitarlo.

Una de estas herramientas ha sido el mecanismo del etiquetado automático de las Ondas de Elliott.

Podemos distinguir dos principios  del etiquetado automático:

  1. De acuerdo con el concepto de la fractalidad de las ondas, el análisis se hace "de arriba abajo", a partir de las ondas mayores hacia las menores.
  2. El análisis se lleva a cabo utilizando el método del repaso directo de posibles opciones.

El diagrama de bloques del algoritmo del análisis automático de las ondas de Elliott se muestra en la figura 12.

Fig. 12 El diagrama de bloques del algoritmo del análisis automático de las ondas de Elliott
Fig. 12 El diagrama de bloques del algoritmo del análisis automático de las ondas de Elliott

Vamos a examinar este algoritmo detalladamente utilizando el ejemplo del etiquetado automático del Impulso (véase la fig. 13).

En la primera fase, en el período de tiempo necesario del gráfico de precios, utilizando el "Zigzag", se señala el número de puntos necesario para el etiquetado. El número de puntos depende del tipo de la onda a analizar. Por ejemplo, para el análisis del Impulso hacen falta seis puntos: 5 crestas y un punto de inicio. Si estuviéramos analizando el Zigzag, entonces el número de puntos necesarios ya sería cuatro: tres crestas y un punto de inicio.

Si el "Zigzag" ha identificado seis puntos en el gráfico de precios, inmediatamente podemos realizar el etiquetado del Impulso: primer punto - punto del inicio de la onda 1, segundo punto - cresta de la onda 1, tercer punto - cresta de la onda 2, cuarto punto - cresta de la onda 3, quinto punto - cresta de la onda 4, y sexto punto - cresta de la onda 5.

Sin embargo, en la Fig. 13 el "Zigzag" ha identificado 8 puntos. En este caso, para estos puntos habrá que repasar todas las opciones posibles del etiquetado de la onda. Tampoco es tan complicado calcularlos, serán cinco (están remarcados con el rojo). Y cada opción del etiquetado tendrá que ser comprobada de acuerdo con las reglas.

Opciones del etiquetado
Fig. 13 Opciones del etiquetado del Impulso

Una vez comprobadas las reglas, si según todos los parámetros la onda marcada es un Impulso, de la misma manera se lleva a cabo el análisis de las subondas.

Lo mismo se refiere al análisis de las demás ondas de impulso y ondas correctivas.

 

3. Tipos de ondas para el etiquetado automático

Y como ya hemos dicho antes, el análisis se realizará de arriba abajo enviando al programa las órdenes de búsqueda de cualquier onda en el intervalo establecido. No obstante, en el mayor intervalo resulta imposible determinar el estado de la onda, su inicio y su final. Llamaremos esta onda la no iniciada y la inacabada.

Podemos clasificar todas las ondas en los grupos siguientes:

  1. Ondas no iniciadas:
    1. Las ondas con la primera onda no iniciada: 1<-2-3-4-5 (por ejemplo, Impulso con la onda no iniciada 1; número de puntos necesarios - 5) y 1<-2-3 (por ejemplo, Zigzag con la onda no iniciada A; número de puntos necesarios - 3);
    2. Las ondas con la segunda onda no iniciada: 2<-3-4-5 (por ejemplo, Diagonal con la onda no iniciada 2; número de puntos necesarios - 4) y 2<-3 (por ejemplo, Plana con la onda no iniciada B; número de puntos necesarios -2);
    3. Las ondas con la tercera onda no iniciada: 3<-4-5 (por ejemplo, Zigzag triple con la onda no iniciada Y; número de puntos necesarios - 3);
    4. Las ondas con la cuarta onda no iniciada: 4<-5 (por ejemplo, Triángulo con la onda no iniciada D; número de puntos necesarios -2);
    5. Las ondas con la quinta onda no iniciada: 5< (por ejemplo, Impulso con la onda no iniciada 5; número de puntos necesarios - 1);
    6. Las ondas con la tercera onda no iniciada: 3< (por ejemplo, Doble tres con la onda no iniciada Z; número de puntos necesarios - 1);
  2. Ondas inacabadas:
    1. Las ondas con la quinta onda inacabada: 1-2-3-4-5> (por ejemplo, Impulso con la onda inacabada 5; número de puntos necesarios - 5);
    2. Las ondas con la cuarta onda inacabada: 1-2-3-4> (por ejemplo, Zigzag triple con la onda inacabada XX; número de puntos necesarios - 4);
    3. Las ondas con la tercera onda inacabada: 1-2-3> (por ejemplo, Cuña con la onda inacabada 3; número de puntos necesarios -3);
    4. Las ondas con la segunda onda inacabada: 1-2> (por ejemplo, Zigzag con la onda inacabada B; número de puntos necesarios -2);
    5. Las ondas con la primera onda inacabada: 1> (por ejemplo, Plana con la onda inacabada A; número de puntos necesarios -1);
  3. Ondas no iniciadas e inacabadas:
    1. Las ondas con la primera onda no iniciada y la segunda onda inacabada: 1<-2> (por ejemplo, Zigzag con la onda no iniciada A y la onda inacabada B; número de puntos necesarios - 1);
    2. Las ondas con la segunda onda no iniciada y la tercera onda inacabada: 2<-3> (por ejemplo, Zigzag con la onda no iniciada B y la onda inacabada C; número de puntos necesarios - 1);
    3. Las ondas con la tercera onda no iniciada y la cuarta onda inacabada: 3<-4> (por ejemplo, Impulso con la onda no iniciada 3 y la onda inacabada 4; número de puntos necesarios - 1);
    4. Las ondas con la cuarta onda no iniciada y la quinta onda inacabada: 4<-5> (por ejemplo, Impulso con la onda no iniciada 4 y la onda inacabada 5; número de puntos necesarios - 1);
    5. Las ondas con la primera onda no iniciada y la tercera onda inacabada: 1<-2-3> (por ejemplo, Triple tres con la onda no iniciada W y la onda inacabada  Y; número de puntos necesarios - 2);
    6. Las ondas con la segunda onda no iniciada y la cuarta onda inacabada: 2<-3-4> (por ejemplo, Cuña con la onda no iniciada 2 y la onda inacabada 4; número de puntos necesarios - 2);
    7. Las ondas con la tercera onda no iniciada y la quinta onda inacabada: 3<-4-5> (por ejemplo, Diagonal con la onda no iniciada 3 y la onda inacabada 5; número de puntos necesarios - 2);
    8. Las ondas con la primera onda no iniciada y la cuarta onda inacabada: 1<-2-3-4> (por ejemplo, Triple  tres con la onda no iniciada W y la onda inacabada  XX; número de puntos necesarios - 3);
    9. Las ondas con la segunda onda no iniciada y la quinta onda inacabada: 2<-3-4-5> (por ejemplo, Impulso con la onda no iniciada 2 y la onda inacabada 5; número de puntos necesarios - 3);
    10. Las ondas con la primera onda no iniciada y la segunda onda inacabada:1<-2-3-4-5> (por ejemplo, Zigzag triple con la onda no iniciada W y la onda inacabada  Z; número de puntos necesarios - 4);
  4. Las ondas acabadas 1-2-3-4-5 (número de puntos necesarios - 6) y 1-2-3 (número de puntos necesarios - 4).

El signo "<" que sigue el número de la onda quiere decir que no se ha iniciado. El signo ">" que sigue el número de la onda quiere decir que no se ha acabado.

En la figura 14 podemos ver las siguientes ondas:

  1. La onda con la primera onda no iniciada AA<-B-C;
  2. La onda con la primera onda no iniciada W y la segunda onda inacabada XW<-X>;
  3. Las ondas acabadas B y C;

Ondas no iniciadas e inacabadas 
Fig. 14 Ondas no iniciadas e inacabadas


4. Descripción de las estructuras de datos del analizador automático de las ondas de Elliott

Para escribir el analizador automático de las ondas de Elliott, vamos a necesitar las siguientes estructuras de datos:

4.1. La estructura de la descripción de las ondas analizadas en el programa:

// La estructura de la descripción de las ondas analizadas en el programa
struct TWaveDescription
  {
   string            NameWave;    // nombre de la onda
   int               NumWave;     // número de subondas en la onda
   string            Subwaves[6]; // nombres de las posibles subondas en la onda
  };

4.2. La clase para el almacenamiento de los parámetros de una onda específica: 

// La clase para almacenar los parámetros de la onda
class TWave
  {
public:
   string            Name;            // nombre de la onda
   string            Formula;         // fórmula de la onda (1-2-3-4-5, <1-2-3 и т.п.)
   int               Level;           // nivel (grado) de la onda
   double            ValueVertex[6]; // valores de las crestas de la onda
   int               IndexVertex[6]; // índices de las crestas de la onda
  };

4.3. La clase para almacenar los valores de las crestas e índices de las crestas del zigzag:

// La clase para almacenar los valores de las crestas e índices de las crestas del zigzag
class TZigzag:public CObject
  {
public:
   CArrayInt        *IndexVertex;    // índices de las crestas del zigzag
   CArrayDouble     *ValueVertex;    // valores de las crestas del zigzag
  };

4.4. La clase para representar el árbol de ondas:

// La clase para representar el árbol de ondas
class TNode:public CObject
  {
public:
   CArrayObj        *Child;    // los hijos de este nodo del árbol
   TWave            *Wave;      // la onda guardada en el nodo del árbol
   string            Text;       // el texto del nodo del árbol
   TNode            *Add(string Text,TWave *Wave=NULL) // la función de adición del nodo en el árbol
     {
      TNode *Node=new TNode;
      Node.Child=new CArrayObj;
      Node.Text =Text;
      Node.Wave=Wave;
      Child.Add(Node);
      return(Node);
     }
  };

4.5. La estructura para almacenar los puntos encontrados en el zigzag:

// La estructura para almacenar los puntos encontrados en el zigzag
struct TPoints
  {
   double            ValuePoints[];  // valores de puntos encontrados
   int               IndexPoints[];  // índices de puntos encontrados
   int               NumPoints;       // número de puntos encontrados
  };

4.6. La clase para almacenar los parámetros de la sección del gráfico ya analizada:

// La clase para almacenar los parámetros de la sección ya analizada, correspondiente al nodo del árbol de ondas
class TNodeInfo:CObject
  {
public:
   int               IndexStart,IndexFinish;  // el rango de la sección ya analizada
   double            ValueStart,ValueFinish;  // los valores extremos de la sección ya analizada
   string            Subwaves;                  // nombre de la onda o del grupo de ondas
   TNode            *Node;                      // nodo que indica en el rango del gráfico ya analizado
  };

4.7. La clase para almacenar la marcación de las ondas antes de colocarlas en el gráfico:

// La clase para almacenar la marcación de las ondas antes de colocarlas en el gráfico
class TLabel:public CObject
  {
public:
   double            Value;  // valor de la cresta
   int               Level;  // nivel de la onda
   string            Text;    // marcación de la cresta
  };

 

5. Descripción de las funciones del analizador automático de las ondas de Elliott

Para escribir el analizador automático de las ondas de Elliott, vamos a necesitar las siguientes funciones:

5.1. Zigzag

La función de búsqueda de los extremos del "Zigzag":

int Zigzag(int H,int Start,int Finish,CArrayInt *IndexVertex,CArrayDouble *ValueVertex)

El elemento clave en el analizador automático de las ondas de Elliott es el "Zigzag" por el que van a construirse las ondas. El cálculo del "Zigzag" por cualquier parámetro tiene que ser muy rápido.

En nuestro analizador vamos a utilizar el "Zigzag" cogido desde el artículo "Cómo escribir los zigzags rápidos y no redibujables".

La función Zigzag calcula el "Zigzag" con el parámetro H en el intervalo de Start hasta Finish y registra los índices y valores de las crestas encontrados en los arrays IndexVertex y ValueVertex, respectivamente, cuyas direcciones se pasan a esta función.

La función Zigzag devuelve el número de crestas encontradas del "Zigzag". 

5.2. FillZigZagArray

La función del repaso del "Zigzag" y almacenamiento de sus parámetros: 

void FillZigzagArray(int Start,int Finish)

Como ha sido mostrado antes, tendremos que buscar la cantidad necesaria de puntos en el gráfico de precios para poder etiquetar la onda. Por eso necesitaremos un array de crestas de los "Zigzags" con diferentes parámetros, el que luego vamos a repasar para buscar estos puntos. 

La función FillZigzagArray calcula los "Zigzags" en el intervalo del gráfico de Start hasta Finish con todos los posibles valores del parámetro H (hasta que el número de las crestas del "Zigzag" sea igual a o menor de dos), guarda la información sobre las crestas encontradas en los objetos de la clase TZigzag y registra estos objetos en el array global ZigzagArray, cuya declaración es como sigue:

CArrayObj ZigzagArray;

5.3. FindPoints

La función de búsqueda en el intervalo especificado de la cantidad de puntos necesaria en el gráfico de precios: 

bool FindPoints(int NumPoints,int IndexStart,int IndexFinish,double ValueStart,double ValueFinish,TPoints &Points)

La función FindPoints busca no menos de NumPoints puntos en el gráfico de precios, en el intervalo de búsqueda necesario, de IndexStart hasta IndexFinish, con los valores necesarios del primer y del último puntos ValueStart y ValueFinish, y los guarda (es decir, los puntos) en la estructura Points, la referencia a la cual se pasa a esta función.

La función FindPoints devuelve true si la cantidad de puntos necesaria ha sido encontrada, de lo contrario devuelve false.

5.4. NotStartedAndNotFinishedWaves

La función del análisis de las ondas no iniciadas e inacabadas:

void NotStartedAndNotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

La función NotStartedAndNotFinishedWaves analiza todas las ondas del tercer grupo de ondas: las ondas no iniciadas e inacabadas. La función analiza NumWave onda (con el nivel de onda Level) de la onda con el nombre ParentWave.Name, que puede tomar forma Subwaves de las ondas (forma del Zigzag, Plana, Doble Zigzag, y/o etc.). La onda analizada NumWave va a guardarse en el nodo del árbol de ondas, el hijo del nodo Node.

Por ejemplo, si ParentWave.Name="Impulso", NumWave=5, Subwaves="Impulso, Diagonal," y Level=2, entonces se puede decir que la función NotStartedAndNotFinishedWaves va a analizar la quinta onda del Impulso que tiene el nivel 2 y puede tomar forma del Impulso o Diagonal.

Como ejemplo mostraremos el diagrama de bloques del algoritmo del análisis de la onda no iniciada e inacabada 1<-2-3> en la función NotStartedAndNotFinishedWaves:

  <img style="vertical-align: middle;" alt="Fig. 15 El diagrama de bloques del análisis de la onda con la formula "1"" title="Fig. 15 El diagrama de bloques del análisis de la onda con la formula "1"" src="http://p.mql5.com/data/temp/7300/d97x04fpa7_fig15.gif" height="1746" width="750">
Fig. 15 El diagrama de bloques del análisis de la onda con la formula "1<-2-3>"

Durante el trabajo de la función NotStartedAndNotFinishedWaves se llaman las funciones NotStartedWaves, NotFinishedWaves y FinishedWaves.

5.5. NotStartedWaves

La función del análisis de las ondas no iniciadas: 

void NotStartedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

La función NotStartedWaves analiza todas las ondas del primer grupo de ondas: las ondas no iniciadas. La función analiza NumWave onda (con el nivel de onda Level) de la onda con el nombre ParentWave.Name, que puede tomar forma Subwaves de las ondas. La onda analizada NumWave va a guardarse en el nodo del árbol de ondas, el hijo del nodo Node.

Durante el trabajo de la función NotStartedWaves se invocan las funciones NotStartedWaves y FinishedWaves.

Todas las ondas se analizan de la misma manera como se muestra en el diagrama de bloques en la Fig. 15. 

5.6. NotFinishedWaves

La función del análisis de las ondas inacabadas: 

void NotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

La función NotFinishedWaves analiza todas las ondas del segundo grupo de ondas: las ondas inacabadas. La función analiza NumWave onda (con el nivel de onda Level) de la onda con el nombreParentWave.Name, que puede tomar forma Subwaves de las ondas. La onda analizada NumWave va a guardarse en el nodo del árbol de ondas, el hijo del nodo Node.

Durante el trabajo de la función NotFinishedWaves se invocan las funciones NotFinishedWaves y FinishedWaves.   

Todas las ondas se analizan de la misma manera como se muestra en el diagrama de bloques en la Fig. 15.

5.7. FinishedWaves

La función del análisis de las ondas acabadas: 

void FinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

La función FinishedWaves analiza todas las ondas del cuarto grupo de ondas: las ondas acabadas. La función analiza NumWave onda (con el nivel de onda Level) de la onda con el nombreParentWave.Name, que puede tomar forma Subwaves de las ondas. La onda analizada NumWave va a guardarse en el nodo del árbol de ondas, el hijo del nodo Node.

Durante el trabajo de la función FinishedWaves se invoca la función FinishedWaves.   

Todas las ondas se analizan de la misma manera como se muestra en el diagrama de bloques en la Fig. 15.

5.8. FindWaveInWaveDescription

La función de búsqueda de la onda en la estructura WaveDescription:

int FindWaveInWaveDescription(string NameWave)

La función FindWaveInWaveDescription busca la onda en el array de estructuras WaveDescription por el nombre de la onda NameWave, que se pasa como parámetro, y devuelve el número del índice que corresponde a esta onda.

El array de estructuras WaveDescription:

TWaveDescription WaveDescription[]=
  {
     {
      "Impulso",5,
        {
         "",
         "Impulso,Cuña",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Impulso,",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Impulso,diagonal,"
        }
     }
      ,
     {
      "Cuña",5,
        {
         "",
         "Impulso,Cuña",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Impulso,",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Impulso,diagonal,"
        }
     }
      ,
     {
      "Diagonal",5,
        {
         "",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo"
        }
     }
      ,
     {
      "Zigzag",3,
        {
         "",
         "Impulso,Cuña",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Impulso,diagonal,",
         "",
         ""
        }
     }
      ,
     {
      "Plana",3,
        {
         "",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Impulso,diagonal,",
         "",
         ""
        }
     }
      ,
     {
      "Doble zigzag",3,
        {
         "",
         "Zigzag,",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,",
         "",
         ""
        }
     }
      ,
     {
      "Triple zigzag",5,
        {
         "",
         "Zigzag,",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,"
        }
     }
      ,
     {
      "Doble tres",3,
        {
         "",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "",
         ""
        }
     }
      ,
     {
      "Triple tres",5,
        {
         "",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo"
        }
     }
      ,
     {
      "Triángulo contracto",5,
        {
         "",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo"
        }
     }
      ,
     {
      "Triángulo expansivo",5,
        {
         "",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo",
         "Zigzag,Plana,Zigzag doble,Zigzag triple,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo"
        }
     }
  };

La función FindWaveInWaveDescription se utiliza en las funciones del análisis de ondas NotStartedAndNotFinishedWaves, NotStartedWaves, NotFinishedWaves y FinishedWaves. 

5.9. Already

La función que comprueba si esta sección del gráfico ya ha sido analizada: 

bool Already(TWave *Wave,int NumWave,TNode *Node,string Subwaves)

Debido a que el análisis automático de las ondas de Elliott se realiza utilizando el método del repaso (recuento), puede surgir la situación cuando esta sección del gráfico ya ha sido analizada con el fin de la presencia de alguna onda o grupo de ondas. Para saberlo, hay que guardar la referencia al nodo en el árbol de ondas de la onda ya analizada, y sólo después de eso entregar esta referencia. Todo eso ocurre en la función Already.

La función Already busca en el array global NodeInfoArray (en el que se guardan los objetos de la clase TNodeInfo) la sección del gráfico correspondiente a la onda NumWave de la onda con el nombre Wave.Name, que tiene la forma Subwaves de las ondas, y registra en Node la dirección del nodo de la sección del gráfico ya etiquetada. Si esta sección no existe, se crea y se llena nuevo objeto de la clase TNodeInfo y se registra en el array NodeInfoArray.

La función devuelve true si la sección del gráfico ya ha sido analizada, de lo contrario devuelve false. 

El array NodeInfoArray se declara así: 

CArrayObj NodeInfoArray;

5.10. Funciones de comprobación de correspondencia de la onda a las reglas

Incluyen las funciones VertexAAboveB, WaveAMoreWaveB y la función WaveRules de la cual se invocan las dos primeras funciones. Durante la comprobación hay que recordar que las ondas pueden ser no iniciadas e (o) inacabadas, y, por ejemplo, para las ondas con la formula "1<-2-3>" resulta imposible determinar si se ha solapado la cuarta onda la zona de la primera porque la cuarta onda todavía no existe.

5.10.1. WaveRules

Función de comprobación de correspondencia de la onda a las reglas:

bool WaveRules(TWave *Wave)

La función WaveRules devuelve true si la onda con el nombre Wave.Name es "correcta", de lo contrario, devuelve false. Durante su trabajo la función WaveRules invoca las funciones VertexAAboveVertexB y WaveAMoreWaveB.

5.10.2. VertexAAboveVertexB

Función de comprobación de superación de una cresta respecto a la otra: 

int VertexAAboveVertexB(int A,int B,bool InternalPoints)

La función VertexAAboveVertexB devuelve el número >=0 si la cresta de la onda A ha superado la cresta de la onda B, de lo contrario, devuelve -1. Si InternalPoints=true, entonces se toman en consideración los puntos internos de las ondas (los valores máximos y (o) mínimos de las ondas).

5.10.3. WaveAMoreWaveB

Función de comprobación de superación de la longitud de una onda respecto a la longitud de la otra:

int WaveAMoreWaveB(int A,int B)

La función WaveAMoreWaveB devuelve el número >=0 si la onda A es más larga que la onda B, de lo contrario, devuelve -1.

11. Función de limpieza de memoria

5.11.1. ClearTree

La función de limpieza del árbol de las ondas con el nodo superior Node: 

void ClearTree(TNode *Node)

5.11.2. ClearNodeInfoArray

Función del limpieza del array ClearNodeInfoArray: 

void ClearNodeInfoArray()

5.11.3. ClearZigzagArray

Función del limpieza del array ZigzagArray:

void ClearZigzagArray()

5.12. Función para recorrer el árbol de ondas y mostrar los resultados del análisis en el gráfico

Tras finalizar el análisis automático de las ondas de Elliott, tenemos el árbol de ondas.

En la imagen de abajo se muestra su ejemplo:

 Fig. 16 Ejemplo del árbol de ondas
Fig. 16 Ejemplo del árbol de ondas 

Ahora para mostrar los resultados del análisis en el gráfico, necesitamos recorrer este árbol. Como vemos en la Figura 16, hay varias opciones del recorrido (porque hay varias opciones de las ondas), y cada opción del recorrido lleva consigo diferente etiquetado.

Podemos distinguir dos tipos de nodos del árbol.

El primer tipo - nodos con los nombres de las ondas ("Impulso", "Zigzag", etc.). El segundo tipo - nodos con los números de las ondas ("1", "1<", etc.). Toda la información sobre los parámetros de la onda se encuentra en el primer tipo de nodos. Por eso, durante la visita de estos nodos, vamos a registra la información sobre la onda para mostrarla luego en el gráfico.

Para simplificar, vamos a recorrer el árbol visitando sólo las primeras variantes de las ondas.

El ejemplo del recorrido se muestra en la Fig. 17 y está marcado con la línea roja.

Fig. 17 Ejemplo del recorrido del árbol de ondas
Fig. 17 Ejemplo del recorrido del árbol de ondas

5.12.1. FillLabelArray

Función del recorrido del árbol de ondas:

void FillLabelArray(TNode *Node)

La función FillLabelArray recorre el árbol de ondas con la raíz Node, visitando sólo las primeras variantes de las ondas en el árbol, y llena el array global LabelArray. Cada índice de este array va a guardar la referencia al array de crestas (array de objetos de clase TLabel) que tienen este índice en el gráfico.

El array LabelArray se determina de la siguiente manera: 

CArrayObj *LabelArray[];

5.12.2. CreateLabels

Función para mostrar los resultados del análisis en el gráfico: 

void CreateLabels()

La función CreateLabels crea los objetos gráficos "Texto" que corresponden a las etiquetas de las ondas en el gráfico. Las etiquetas de las ondas se crean a base del array LabelArray.

5.12.3. CorrectLabel

La función de actualización (correcciones) de las crestas de las ondas en el gráfico: 

void CorrectLabel()

La función CorrectLabel corrige las etiquetas de las ondas en el gráfico a la hora de desplazarlo y (o) reducir.

 

6. Implementación de las funciones del etiquetado automático de las Ondas de Elliott 

6.1. La función Zigzag:

//+------------------------------------------------------------------+
//| La función Zigzag                                                |
//+------------------------------------------------------------------+
int Zigzag(int H,int Start,int Finish,CArrayInt *IndexVertex,CArrayDouble *ValueVertex)
  {
   bool Up=true;
   double dH=H*Point();
   int j=0;
   int TempMaxBar = Start;
   int TempMinBar = Start;
   double TempMax = rates[Start].high;
   double TempMin = rates[Start].low;
   for(int i=Start+1;i<=Finish;i++)
     {
      // procesamiento del caso del segmento ascendente
      if(Up==true)
        {
         // comprobamos si se ha cambiado el máximo actual
         if(rates[i].high>TempMax)
           {
            // si es así, corregimos las variables correspondientes
            TempMax=rates[i].high;
            TempMaxBar=i;
           }
         else if(rates[i].low<TempMax-dH)
           {
            // de los contrario, si el nivel retrasado ha sido roto, fijamos el máximo
            ValueVertex.Add(TempMax);
            IndexVertex.Add(TempMaxBar);
            j++;
            // corregimos las variables correspondientes
            Up=false;
            TempMin=rates[i].low;
            TempMinBar=i;
           }
        }
      else
        {
         // procesamiento del caso del segmento descendente
         // comprobamos si se ha cambiado el mínimo actual
         if(rates[i].low<TempMin)
           {
            // si es así, corregimos las variables correspondientes
            TempMin=rates[i].low;
            TempMinBar=i;
           }
         else if(rates[i].high>TempMin+dH)
           {
            // de los contrario, si el nivel retrasado ha sido roto, fijamos el mínimo
            ValueVertex.Add(TempMin);
            IndexVertex.Add(TempMinBar);
            j++;
            // corregimos las variables correspondientes
            Up=true;
            TempMax=rates[i].high;
            TempMaxBar=i;
           }
        }
     }
   // devolvemos la cantidad de las crestas del zigzag
   return(j);
  }

6.2. La función FillZigzagArray:

CArrayObj ZigzagArray; // declaramos el array dinámico global ZigzagArray
//+------------------------------------------------------------------+
//| La función FillZigzagArray                                       |
//| se repasan los valores del parámetro H del zigzag                |
//| и se llena el array ZigzagArray                                  |
//+------------------------------------------------------------------+
void FillZigzagArray(int Start,int Finish)
  {
   CArrayInt *IndexVertex=new CArrayInt;         // creamos el array dinámico de los índices de las crestas del zigzag
   CArrayDouble *ValueVertex=new CArrayDouble;   // creamos el array dinámico de los valores de las crestas del zigzag
   TZigzag *Zigzag;                                 // declaramos la clase para almacenar los índices y valores de las crestas del zigzag
   int H=1;
   int j=0;
   int n=Zigzag(H,Start,Finish,IndexVertex,ValueVertex);//encontramos las crestas del zigzag con el parámetro H=1
   if(n>0)
     {
      // guardamos las crestas del zigzag en el array ZigzagArray
      Zigzag=new TZigzag; // creamos el objeto para almacenar los índices encontrados y las crestas del zigzag, 
                             // lo llenamos y guardamos en el array ZigzagArray
      Zigzag.IndexVertex=IndexVertex;
      Zigzag.ValueVertex=ValueVertex;
      ZigzagArray.Add(Zigzag);
      j++;
     }
   H++;
   // repasamos en ciclo el parámetro H del zigzag
   while(true)
     {
      IndexVertex=new CArrayInt;                            // creamos el array dinámico de los índices de las crestas del zigzag
      ValueVertex=new CArrayDouble;                        // creamos el array dinámico de los valores de las crestas del zigzag
      n=Zigzag(H,Start,Finish,IndexVertex,ValueVertex); // encontramos las crestas del zigzag
      if(n>0)
        {
         Zigzag=ZigzagArray.At(j-1);
         CArrayInt *PrevIndexVertex=Zigzag.IndexVertex; // obtenemos el array de los índices del zigzag anterior
         bool b=false;
         // comprobamos en el ciclo si hay alguna diferencia entre el zigzag actual y el anterior
         for(int i=0; i<=n-1;i++)
           {
            if(PrevIndexVertex.At(i)!=IndexVertex.At(i))
              {
               // si hay diferencia, guardamos las crestas del zigzag en el array ZigzagArray
               Zigzag=new TZigzag;
               Zigzag.IndexVertex=IndexVertex;
               Zigzag.ValueVertex=ValueVertex;
               ZigzagArray.Add(Zigzag);
               j++;
               b=true;
               break;
              }
           }
         if(b==false)
           {
            // de lo contrario, si no hay diferencia, liberamos la memoria
            delete IndexVertex;
            delete ValueVertex;
           }
        }
      // buscamos las crestas del zigzag hasta que sean menos o igual a dos
      if(n<=2)
         break;
      H++;
     }
  }

6.3. La función FindPoints:

//+------------------------------------------------------------------+
//| La función FindPoints                                            |
//| Llena los arrays ValuePoints                                     |
//| y IndexPoints de la estructura Points                            |
//+------------------------------------------------------------------+
bool FindPoints(int NumPoints,int IndexStart,int IndexFinish,double ValueStart,double ValueFinish,TPoints &Points)
  {
   int n=0;
   // se repasa en el ciclo el array ZigzagArray
   for(int i=ZigzagArray.Total()-1; i>=0;i--)
     {
      TZigzag *Zigzag=ZigzagArray.At(i);             // el zigzag i obtenido en el array ZigzagArray
      CArrayInt *IndexVertex=Zigzag.IndexVertex;    // obtenemos el array de los índices de las crestas del zigzag i
      CArrayDouble *ValueVertex=Zigzag.ValueVertex; // obtenemos el array de los valores de las crestas del zigzag i
      int Index1=-1,Index2=-1;
      // buscamos el índice del array IndexVertex correspondiente al primer punto
      for(int j=0;j<IndexVertex.Total();j++)
        {
         if(IndexVertex.At(j)>=IndexStart)
           {
            Index1=j;
            break;
           }
        }
      // buscamos el índice del array IndexVertex correspondiente al último punto
      for(int j=IndexVertex.Total()-1;j>=0;j--)
        {
         if(IndexVertex.At(j)<=IndexFinish)
           {
            Index2=j;
            break;
           }
        }
      // si el primer y el último punto han sido encontrados
      if((Index1!=-1) && (Index2!=-1))
        {
         n=Index2-Index1+1; // descubrimos cuántos puntos hemos encontrado
        }
      // si hemos encontrado el número de puntos necesario (igual o superior)
      if(n>=NumPoints)
        {
         // comprobamos que la primera y la última cresta coincidan con los valores necesarios de las crestas
         if(((ValueStart!=0) && (ValueVertex.At(Index1)!=ValueStart)) || 
            ((ValueFinish!=0) && (ValueVertex.At(Index1+n-1)!=ValueFinish)))continue;
         // llenamos la estructura Points pasada como parámetros
         Points.NumPoints=n;
         ArrayResize(Points.ValuePoints, n);
         ArrayResize(Points.IndexPoints, n);
         int k=0;
         // llenamos los arrays ValuePoints y IndexPoints de la estructura Points
         for(int j=Index1; j<Index1+n;j++)
           {
            Points.ValuePoints[k]=ValueVertex.At(j);
            Points.IndexPoints[k]=IndexVertex.At(j);
            k++;
           }
         return(true);
        };
     };
   return(false);
  };

6.4. La función NotStartedAndNotFinishedWaves:

//+------------------------------------------------------------------+
//| La función NotStartedAndNotFinishedWaves                         |
//+------------------------------------------------------------------+
void NotStartedAndNotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v1,v2,v3,v4,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,pos=0,start=0;
   // escribimos en el array ListNameWave las ondas a analizar
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(pos!=StringLen(Subwaves)-1)
     {
      pos=StringFind(Subwaves,",",start);
      NameWave=StringSubstr(Subwaves,start,pos-start);
      ListNameWave[i++]=NameWave;
      start=pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // buscamos como mínimo dos puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(2,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas no iniciadas e inacabadas con la formula "1<-2-3>"
   v1=0;
   while(v1<=Points.NumPoints-2)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para
              // saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if((WaveDescription[IndexWave].NumWave==5) || (WaveDescription[IndexWave].NumWave==3))
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="1<-2-3>";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = 0;
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = IndexStart;
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = IndexFinish;
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // creamos la primera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la segunda subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // ciclos del repaso de las ondas no iniciadas e inacabadas con la formula "2<-3-4>"
   v2=0;
   while(v2<=Points.NumPoints-2)
     {
      v3=v2+1;
      while(v3<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==5)
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="2<-3-4>";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = 0;
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = 0;
               Wave.IndexVertex[1] = IndexStart;
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = IndexFinish;
               Wave.IndexVertex[5] = 0;
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=2;
                  // creamos la segunda subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  //creamos la cuarta subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v3=v3+2;
        }
      v2=v2+2;
     }
   // ciclos del repaso de las ondas no iniciadas e inacabadas con la formula "3<-4-5>"
   v3=0;
   while(v3<=Points.NumPoints-2)
     {
      v4=v3+1;
      while(v4<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para
              // saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==5)
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="3<-4-5>";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = 0;
               Wave.ValueVertex[2] = 0;
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = Points.ValuePoints[v4];
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = 0;
               Wave.IndexVertex[1] = 0;
               Wave.IndexVertex[2] = IndexStart;
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = Points.IndexPoints[v4];
               Wave.IndexVertex[5] = IndexFinish;
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=3;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la cuarta subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la quinta subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v4=v4+2;
        }
      v3=v3+2;
     }
   // buscamos como mínimo tres puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(3,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // ciclos del repaso de las ondas no iniciadas e inacabadas con la formula "1<-2-3-4>"
   v1=0;
   while(v1<=Points.NumPoints-3)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-2)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-1)
           {
            int j=0;
            while(j<=i-1)
              {
               // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
               NameWave=ListNameWave[j++];
               // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="1<-2-3-4>";
                  Wave.ValueVertex[0] = 0;
                  Wave.ValueVertex[1] = Points.ValuePoints[v1];
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = 0;
                  Wave.ValueVertex[5] = 0;
                  Wave.IndexVertex[0] = IndexStart;
                  Wave.IndexVertex[1] = Points.IndexPoints[v1];
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = IndexFinish;
                  Wave.IndexVertex[5] = 0;
                  // comprobamos la correspondencia de la onda a las reglas
                  if(WaveRules(Wave)==true)
                    {
                     // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                     ParentNode=Node.Add(NameWave,Wave);
                     I=1;
                     // creamos la primera subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la segunda subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la tercera
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la cuarta subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                    // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                  else delete Wave;
                 }
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // ciclos del repaso de las ondas no iniciadas e inacabadas con la formula "2<-3-4-5>"
   v2=0;
   while(v2<=Points.NumPoints-3)
     {
      v3=v2+1;
      while(v3<=Points.NumPoints-2)
        {
         v4=v3+1;
         while(v4<=Points.NumPoints-1)
           {
            int j=0;
            while(j<=i-1)
              {
               // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
               NameWave=ListNameWave[j++];
               // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="2<-3-4-5>";
                  Wave.ValueVertex[0] = 0;
                  Wave.ValueVertex[1] = 0;
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = Points.ValuePoints[v4];
                  Wave.ValueVertex[5] = 0;
                  Wave.IndexVertex[0] = 0;
                  Wave.IndexVertex[1] = IndexStart;
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = Points.IndexPoints[v4];
                  Wave.IndexVertex[5] = IndexFinish;
                  // comprobamos la correspondencia de la onda a las reglas
                  if(WaveRules(Wave)==true)
                    {
                     // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                     ParentNode=Node.Add(NameWave,Wave);
                     I=2;
                     // creamos la segunda subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la tercera
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     //creamos la cuarta subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la quinta subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                  else delete Wave;
                 }
              }
            v4=v4+2;
           }
         v3=v3+2;
        }
      v2=v2+2;
     }
   // buscamos como mínimo cuatro puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // ciclos del repaso de las ondas no iniciadas e inacabadas con la formula "1<-2-3-4-5>"
   v1=0;
   while(v1<=Points.NumPoints-4)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-3)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-2)
           {
            v4=v3+1;
            while(v4<=Points.NumPoints-1)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
                  NameWave=ListNameWave[j++];
                  // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1<-2-3-4-5>";
                     Wave.ValueVertex[0] = 0;
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = 0;
                     Wave.IndexVertex[0] = IndexStart;
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = IndexFinish;
                      // comprobamos la correspondencia de la onda a las reglas
                     if(WaveRules(Wave)==true)
                       {
                        // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // creamos la primera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la segunda subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la tercera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la cuarta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la quinta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                      // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                     else delete Wave;
                    }
                 }
               v4=v4+2;
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // buscamos como mínimo un punto en el gráfico de precios y lo escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(1,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclo del repaso de las ondas no iniciadas e inacabadas con la formula "1<-2>"
   v1=0;
   while(v1<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5 || WaveDescription[IndexWave].NumWave==3)
           {
              // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="1<-2>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = Points.ValuePoints[v1];
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = IndexStart;
            Wave.IndexVertex[1] = Points.IndexPoints[v1];
            Wave.IndexVertex[2] = IndexFinish;
            Wave.IndexVertex[3] = 0;
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=1;
               // creamos la primera subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la segunda subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
            else delete Wave;
           }
        }
      v1=v1+1;
     }
   // ciclo del repaso de las ondas no iniciadas e inacabadas con la formula "2<-3>"
   v2=0;
   while(v2<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5 || WaveDescription[IndexWave].NumWave==3)
           {
              // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="2<-3>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = Points.ValuePoints[v2];
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = IndexStart;
            Wave.IndexVertex[2] = Points.IndexPoints[v2];
            Wave.IndexVertex[3] = IndexFinish;
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=2;
               // creamos la segunda subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la tercera subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
            else delete Wave;
           }
        }
      v2=v2+1;
     }
   // ciclo del repaso de las ondas no iniciadas e inacabadas con la formula "3<-4>"
   v3=0;
   while(v3<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5)
           {
              // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="3<-4>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = Points.ValuePoints[v3];
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = 0;
            Wave.IndexVertex[2] = IndexStart;
            Wave.IndexVertex[3] = Points.IndexPoints[v3];
            Wave.IndexVertex[4] = IndexFinish;
            Wave.IndexVertex[5] = 0;
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=3;
               // creamos la tercera subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la cuarta subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
            else delete Wave;
           }
        }
      v3=v3+1;
     }
   // ciclo del repaso de las ondas no iniciadas e inacabadas con la formula "4<-5>"
   v4=0;
   while(v4<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5)
           {
             // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="4<-5>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = Points.ValuePoints[v4];
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = 0;
            Wave.IndexVertex[2] = 0;
            Wave.IndexVertex[3] = IndexStart;
            Wave.IndexVertex[4] = Points.IndexPoints[v4];
            Wave.IndexVertex[5] = IndexFinish;
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=4;
               // creamos la cuarta subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la quinta subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            //иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v4=v4+1;
     }
  }

6.5. La función NotStartedWaves:

//+------------------------------------------------------------------+
//| La función NotStartedWaves                                       |
//+------------------------------------------------------------------+
void NotStartedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v1,v2,v3,v4,v5,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,Pos=0,Start=0;
   // escribimos en el array ListNameWave las ondas a analizar
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(Pos!=StringLen(Subwaves)-1)
     {
      Pos=StringFind(Subwaves,",",Start);
      NameWave=StringSubstr(Subwaves,Start,Pos-Start);
      ListNameWave[i++]=NameWave;
      Start=Pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // buscamos como mínimo dos puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(2,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas no iniciadas con la formula "4<-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=0)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5)
           {
              // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="4<-5";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = Points.ValuePoints[v4];
            Wave.ValueVertex[5] = Points.ValuePoints[v5];
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = 0;
            Wave.IndexVertex[2] = 0;
            Wave.IndexVertex[3] = IndexStart;
            Wave.IndexVertex[4] = Points.IndexPoints[v4];
            Wave.IndexVertex[5] = Points.IndexPoints[v5];
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=4;
               // creamos la cuarta subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la quinta subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
            else delete Wave;
           }
        }
      v4=v4-2;
     }
   // ciclos del repaso de las ondas no iniciadas con la formula "2<-3"
   v3=Points.NumPoints-1;
   v2=v3-1;
   while(v2>=0)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==3)
           {
              // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="2<-3";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = Points.ValuePoints[v2];
            Wave.ValueVertex[3] = Points.ValuePoints[v3];
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = IndexStart;
            Wave.IndexVertex[2] = Points.IndexPoints[v2];
            Wave.IndexVertex[3] = Points.IndexPoints[v3];
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=2;
               // creamos la segunda subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la tercera subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
            else delete Wave;
           }
        }
      v2=v2-2;
     }
   // buscamos como mínimo tres puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(3,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas no iniciadas con la formula "3<-4-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=1)
     {
      v3=v4-1;
      while(v3>=0)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==5)
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="3<-4-5";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = 0;
               Wave.ValueVertex[2] = 0;
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = Points.ValuePoints[v4];
               Wave.ValueVertex[5] = Points.ValuePoints[v5];
               Wave.IndexVertex[0] = 0;
               Wave.IndexVertex[1] = 0;
               Wave.IndexVertex[2] = IndexStart;
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = Points.IndexPoints[v4];
               Wave.IndexVertex[5] = Points.IndexPoints[v5];
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=3;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la cuarta subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la quinta subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v3=v3-2;
        }
      v4=v4-2;
     }
   // ciclos del repaso de las ondas no iniciadas con la formula "1<-2-3"
   v3=Points.NumPoints-1;
   v2=v3-1;
   while(v2>=1)
     {
      v1=v2-1;
      while(v1>=0)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==3)
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="1<-2-3";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = IndexStart;
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // creamos la primera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la segunda subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v1=v1-2;
        }
      v2=v2-2;
     }
   // buscamos como mínimo cuatro puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas no iniciadas con la formula "2<-3-4-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=2)
     {
      v3=v4-1;
      while(v3>=1)
        {
         v2=v3-1;
         while(v2>=0)
           {
            int j=0;
            while(j<=i-1)
              {
               // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
               NameWave=ListNameWave[j++];
               // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="2<-3-4-5";
                  Wave.ValueVertex[0] = 0;
                  Wave.ValueVertex[1] = 0;
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = Points.ValuePoints[v4];
                  Wave.ValueVertex[5] = Points.ValuePoints[v5];
                  Wave.IndexVertex[0] = 0;
                  Wave.IndexVertex[1] = IndexStart;
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = Points.IndexPoints[v4];
                  Wave.IndexVertex[5] = Points.IndexPoints[v5];
                  // comprobamos la correspondencia de la onda a las reglas
                  if(WaveRules(Wave)==true)
                    {
                     // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                     ParentNode=Node.Add(NameWave,Wave);
                     I=2;
                     // creamos la segunda subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la tercera
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la cuarta subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la quinta subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                  else delete Wave;
                 }
              }
            v2=v2-2;
           }
         v3=v3-2;
        }
      v4=v4-2;
     }
   // buscamos como mínimo cinco puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(5,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas no iniciadas con la formula "1<-2-3-4-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=3)
     {
      v3=v4-1;
      while(v3>=2)
        {
         v2=v3-1;
         while(v2>=1)
           {
            v1=v2-1;
            while(v1>=0)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
                  NameWave=ListNameWave[j++];
                  // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1<-2-3-4-5";
                     Wave.ValueVertex[0] = 0;
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = Points.ValuePoints[v5];
                     Wave.IndexVertex[0] = IndexStart;
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = Points.IndexPoints[v5];
                      // comprobamos la correspondencia de la onda a las reglas
                     if(WaveRules(Wave)==true)
                       {
                        // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // creamos la primera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la segunda subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la tercera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la cuarta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la quinta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                      // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                     else delete Wave;
                    }
                 }
               v1=v1-2;
              }
            v2=v2-2;
           }
         v3=v3-2;
        }
      v4=v4-2;
     }
  }

6.6. La función NotFinishedWaves:

//+------------------------------------------------------------------+
//| La función NotFinishedWaves                                      |
//+------------------------------------------------------------------+
void NotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v0,v1,v2,v3,v4,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,Pos=0,Start=0;
   // escribimos en el array ListNameWave las ondas a analizar
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(Pos!=StringLen(Subwaves)-1)
     {
      Pos=StringFind(Subwaves,",",Start);
      NameWave=StringSubstr(Subwaves,Start,Pos-Start);
      ListNameWave[i++]=NameWave;
      Start=Pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // buscamos como mínimo dos puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(2,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas inacabadas con la formula "1-2>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // sacamos por turno desde ListNameWave el nombre de la onda para el análisis
         NameWave=ListNameWave[j++];
         // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
         IndexWave=FindWaveInWaveDescription(NameWave);
         if((WaveDescription[IndexWave].NumWave==5) || (WaveDescription[IndexWave].NumWave==3))
           {
              // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="1-2>";
            Wave.ValueVertex[0] = Points.ValuePoints[v0];
            Wave.ValueVertex[1] = Points.ValuePoints[v1];
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = Points.IndexPoints[v0];
            Wave.IndexVertex[1] = Points.IndexPoints[v1];
            Wave.IndexVertex[2] = IndexFinish;
            Wave.IndexVertex[3] = 0;
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // comprobamos la correspondencia de la onda a las reglas
            if(WaveRules(Wave)==true)
              {
               // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
               ParentNode=Node.Add(NameWave,Wave);
               I=1;
               // creamos la primera subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // creamos la segunda subonda en el árbol de ondas
               ChildNode=ParentNode.Add(IntegerToString(I));
               // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
            else delete Wave;
           }
        }
      v1=v1+2;
     }
   // buscamos como mínimo tres puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(3,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas inacabadas con la formula "1-2-3>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-2)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if((WaveDescription[IndexWave].NumWave==5) || (WaveDescription[IndexWave].NumWave==3))
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="1-2-3>";
               Wave.ValueVertex[0] = Points.ValuePoints[v0];
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = 0;
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = Points.IndexPoints[v0];
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = IndexFinish;
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // creamos la primera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la segunda subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // buscamos como mínimo cuatro puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // ciclos del repaso de las ondas inacabadas con la formula "1-2-3-4>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-3)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-2)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-1)
           {
            int j=0;
            while(j<=i-1)
              {
               // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
               NameWave=ListNameWave[j++];
               // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="1-2-3-4>";
                  Wave.ValueVertex[0] = Points.ValuePoints[v0];
                  Wave.ValueVertex[1] = Points.ValuePoints[v1];
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = 0;
                  Wave.ValueVertex[5] = 0;
                  Wave.IndexVertex[0] = Points.IndexPoints[v0];
                  Wave.IndexVertex[1] = Points.IndexPoints[v1];
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = IndexFinish;
                  Wave.IndexVertex[5] = 0;
                  // comprobamos la correspondencia de la onda a las reglas
                  if(WaveRules(Wave)==true)
                    {
                     // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                     ParentNode=Node.Add(NameWave,Wave);
                     I=1;
                     // creamos la primera subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la segunda subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la tercera
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // creamos la cuarta subonda en el árbol de ondas
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                  else delete Wave;
                 }
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // buscamos como mínimo cinco puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(5,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas inacabadas con la formula "1-2-3-4-5>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-4)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-3)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-2)
           {
            v4=v3+1;
            while(v4<=Points.NumPoints-1)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
                  NameWave=ListNameWave[j++];
                  // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1-2-3-4-5>";
                     Wave.ValueVertex[0] = Points.ValuePoints[v0];
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = 0;
                     Wave.IndexVertex[0] = Points.IndexPoints[v0];
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = IndexFinish;
                      // comprobamos la correspondencia de la onda a las reglas
                     if(WaveRules(Wave)==true)
                       {
                        // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // creamos la primera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la segunda subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la tercera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la cuarta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la quinta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                      // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                     else delete Wave;
                    }
                 }
               v4=v4+2;
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
  }

6.7. La función FinishedWaves:

//+------------------------------------------------------------------+
//| La función FinishedWaves                                         |
//+------------------------------------------------------------------+
void FinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v0,v1,v2,v3,v4,v5,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,Pos=0,Start=0;
   // escribimos en el array ListNameWave las ondas a analizar
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(Pos!=StringLen(Subwaves)-1)
     {
      Pos=StringFind(Subwaves,",",Start);
      NameWave=StringSubstr(Subwaves,Start,Pos-Start);
      ListNameWave[i++]=NameWave;
      Start=Pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // buscamos como mínimo cuatro puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // ciclos del repaso de las ondas inacabadas con la formula "1-2-3"
   v0 = 0;
   v1 = 1;
   v3 = Points.NumPoints - 1;
   while(v1<=v3-2)
     {
      v2=v1+1;
      while(v2<=v3-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
            NameWave=ListNameWave[j++];
            // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==3)
              {
               // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
               Wave=new TWave;;
               Wave.Name=NameWave;
               Wave.Formula="1-2-3";
               Wave.Level=Level;
               Wave.ValueVertex[0] = Points.ValuePoints[v0];
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = Points.IndexPoints[v0];
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // comprobamos la correspondencia de la onda a las reglas
               if(WaveRules(Wave)==true)
                 {
                  // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // creamos la primera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(i));
                  // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la segunda subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(i));
                  // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // creamos la tercera subonda en el árbol de ondas
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
               else delete Wave;
              }
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // buscamos como mínimo seis puntos en el gráfico de precios y los escribimos en la estructura Points
   // si no han sido encontrados, salimos de la función
   if(FindPoints(6,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // ciclos del repaso de las ondas inacabadas con la formula "1-2-3-4-5"
   v0 = 0;
   v1 = 1;
   v5 = Points.NumPoints - 1;
   while(v1<=v5-4)
     {
      v2=v1+1;
      while(v2<=v5-3)
        {
         v3=v2+1;
         while(v3<=v5-2)
           {
            v4=v3+1;
            while(v4<=v5-1)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // sacamos por turno desde  ListNameWave el nombre de la onda para el análisis
                  NameWave=ListNameWave[j++];
                  // buscamos el índice de la onda en la estructura WaveDescription para saber la cantidad de sus subondas y sus nombres
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                    // creamos el objeto de la clase TWave y llenamos sus campos - parámetros de la onda a analizar
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1-2-3-4-5";
                     Wave.ValueVertex[0] = Points.ValuePoints[v0];
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = Points.ValuePoints[v5];
                     Wave.IndexVertex[0] = Points.IndexPoints[v0];
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = Points.IndexPoints[v5];
                      // comprobamos la correspondencia de la onda a las reglas
                     if(WaveRules(Wave)==true)
                       {
                        // si la onda ha pasado la comprobación de las reglas, la añadimos al árbol de ondas
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // creamos la primera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la primera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la segunda subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la segunda subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la tercera subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la tercera subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la cuarta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la cuarta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // creamos la quinta subonda en el árbol de ondas
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // si el intervalo del gráfico que corresponde a la quinta subonda no ha sido analizado todavía, lo analizamos
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                      // de lo contrario, si la onda no ha pasado las reglas, liberamos la memoria
                     else delete Wave;
                    }
                 }
               v4=v4+2;
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
  }

6.8. La función FindWaveInWaveDescription:

//+------------------------------------------------------------------+
//| La función FindWaveInWaveDescription                             |
//+------------------------------------------------------------------+
int FindWaveInWaveDescription(string NameWave)
  {
   for(int i=0;i<ArrayRange(WaveDescription,0);i++)
      if(WaveDescription[i].NameWave==NameWave)return(i);
   return(-1);
  }

6.9. La función Already:

//+------------------------------------------------------------------+
//| La función Already                                               |
//+------------------------------------------------------------------+
bool Already(TWave *Wave,int NumWave,TNode *Node,string Subwaves)
  {
   // obtenemos los parámetros necesarios de la onda o del grupo de ondas
   int IndexStart=Wave.IndexVertex[NumWave-1];
   int IndexFinish=Wave.IndexVertex[NumWave];
   double ValueStart = Wave.ValueVertex[NumWave - 1];
   double ValueFinish= Wave.ValueVertex[NumWave];
   // se repasa en el ciclo el array NodeInfoArray para buscar la sección del gráfico ya trazada
   for(int i=NodeInfoArray.Total()-1; i>=0;i--)
     {
      TNodeInfo *NodeInfo=NodeInfoArray.At(i);
      // si la sección necesaria ya ha sido trazada antes,
      if(NodeInfo.Subwaves==Subwaves && (NodeInfo.ValueStart==ValueStart) && 
         (NodeInfo.ValueFinish==ValueFinish) && (NodeInfo.IndexStart==IndexStart) &&
         (NodeInfo.IndexFinish==IndexFinish))
        {
         // agregamos los nodos hijos del nodo encontrado en los nodos hijos del nodo nuevo
         for(int j=0;j<NodeInfo.Node.Child.Total();j++)
            Node.Child.Add(NodeInfo.Node.Child.At(j));
         return(true); // salimos de la función
        }
     }
   // si la sección no ha sido trazada antes, escribimos sus datos en el array NodeInfoArray
   TNodeInfo *NodeInfo=new TNodeInfo;
   NodeInfo.IndexStart=IndexStart;
   NodeInfo.IndexFinish=IndexFinish;
   NodeInfo.ValueStart=ValueStart;
   NodeInfo.ValueFinish=ValueFinish;
   NodeInfo.Subwaves=Subwaves;
   NodeInfo.Node=Node;
   NodeInfoArray.Add(NodeInfo);
   return(false);
  }

6.10. La función WaveRules:

int IndexVertex[6];                         // índices de las crestas de la onda
double ValueVertex[6],Maximum[6],Minimum[6]; // los valores de las crestas de la onda, valores máximos y mínimos de la onda
string Trend;                                    // dirección de la tendencia - "Up" o "Down"
string Formula;                                  // formula de la onda - "1<2-3>" o "1-2-3>" etc.
int FixedVertex[6];                             // información sobre las crestas de la onda, están fijadas o no

//+------------------------------------------------------------------+
//| La función WaveRules                                             |
//+------------------------------------------------------------------+
bool WaveRules(TWave *Wave)
  {
   Formula=Wave.Formula;
   bool Result=false;
   // llenamos el array IndexVertex y ValueVertex - índices de las crestas y valores de las crestas de la onda
   for(int i=0;i<=5;i++)
     {
      IndexVertex[i]=Wave.IndexVertex[i];
      ValueVertex[i]=Wave.ValueVertex[i];
      FixedVertex[i]=-1;
     }
   // llenamos el array FixedVertex cuyos valores indican si la cresta es fija o no
   int Pos1=StringFind(Formula,"<");
   string Str;
   if(Pos1>0)
     {
      Str=ShortToString(StringGetCharacter(Formula,Pos1-1));
      FixedVertex[StringToInteger(Str)]=1;
      FixedVertex[StringToInteger(Str)-1]=0;
      Pos1=StringToInteger(Str)+1;
     }
   else Pos1=0;
   int Pos2=StringFind(Formula,">");
   if(Pos2>0)
     {
      Str=ShortToString(StringGetCharacter(Formula,Pos2-1));
      FixedVertex[StringToInteger(Str)]=0;
      Pos2=StringToInteger(Str)-1;
     }
   else
     {
      Pos2=StringLen(Formula);
      Str=ShortToString(StringGetCharacter(Formula,Pos2-1));
      Pos2=StringToInteger(Str);
     }
   for(int i=Pos1;i<=Pos2;i++)
      FixedVertex[i]=1;
   double High[],Low[];
   ArrayResize(High,ArrayRange(rates,0));
   ArrayResize(Low,ArrayRange(rates,0));
     // encontramos los máximos y los mínimos de la onda
     for(int i=1; i<=5; i++)
     {
      Maximum[i]=rates[IndexVertex[i]].high;
      Minimum[i]=rates[IndexVertex[i-1]].low;
      for(int j=IndexVertex[i-1];j<=IndexVertex[i];j++)
        {
         if(rates[j].high>Maximum[i])Maximum[i]=rates[j].high;
         if(rates[j].low<Minimum[i])Minimum[i]=rates[j].low;
        }
     }
   // averiguamos la tendencia
   if((FixedVertex[0]==1 && ValueVertex[0]==rates[IndexVertex[0]].low) ||
      (FixedVertex[1]==1 && ValueVertex[1]==rates[IndexVertex[1]].high) ||
      (FixedVertex[2]==1 && ValueVertex[2]==rates[IndexVertex[2]].low) ||
      (FixedVertex[3]==1 && ValueVertex[3]==rates[IndexVertex[3]].high) ||
      (FixedVertex[4]==1 && ValueVertex[4]==rates[IndexVertex[4]].low) ||
      (FixedVertex[5]==1 && ValueVertex[5]==rates[IndexVertex[5]].high))
      Trend="Up";
   else Trend="Down";
   // comprobamos la onda necesaria por las reglas
   if(Wave.Name=="Impulso")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(4,1,true)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0 &&
         (WaveAMoreWaveB(3,1)>=0 || WaveAMoreWaveB(3,5)>=0))
         Result=true;
     }
   else if(Wave.Name=="Cuña")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(4,2,true)>=0 &&
         VertexAAboveVertexB(1,4,false)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0&&
         (WaveAMoreWaveB(3,1)>=0 || WaveAMoreWaveB(3,5)>=0))
         Result=true;
     }
   else if(Wave.Name=="Diagonal")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(4,2,true)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0&&
         (WaveAMoreWaveB(3,1)>=0 || WaveAMoreWaveB(3,5)>=0))
         Result=true;
     }
   else if(Wave.Name=="Zigzag")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Plana")
     {
      if(VertexAAboveVertexB(1,0,false)>=0 &&
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0)
         Result=true;
     }
   else if(Wave.Name=="Doble zigzag")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Doble tres")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Triple zigzag")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(5,3,false) &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0)
         Result=true;
     }
   else if(Wave.Name=="Triple tres")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,false)>=0)
         Result=true;
     }
   else if(Wave.Name==«Triángulo contracto")
     {
      if(VertexAAboveVertexB(1,0,false)>=0 && VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>= 0&&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,false)>=0 &&
         WaveAMoreWaveB(2,3)>=0 && WaveAMoreWaveB(3,4)>=0 && WaveAMoreWaveB(4,5)>=0)
         Result=true;
     }
   else if(Wave.Name==Triángulo expansivo")
     {
      if(VertexAAboveVertexB(1,0,false)>=0 && VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>= 0&&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,false)>=0 &&
         WaveAMoreWaveB(3,2)>=0 && WaveAMoreWaveB(3,2)>=0)
         Result=true;
     }
   return(Result);
  }

6.11. La función VertexAAboveVertexB:

//+-------------------------------------------------------------------------------------+
//| La función VertexAAboveVertexB comprueba si la cresta A es más alta que la cresta B |
//| que se transfieren como parámetros de esta función                                  |
//| esta verificación se puede realizar sólo si las crestas A y B son fijas,            |
//| o si la cresta A no es fija y par, y la cresta B es fija,                           |
//| o la cresta A es fija y la cresta B no es fija y es impar,                          |
//| o la cresta A no es fija y es par, y la cresta B no es fija y es impar              |
//+-------------------------------------------------------------------------------------+
int VertexAAboveVertexB(int A,int B,bool InternalPoints)
  {
   double VA=0,VB=0,VC=0;
   int IA=0,IB=0;
   int Result=0;
   if(A>=B)
     {
      IA = A;
      IB = B;
     }
   else if(A<B)
     {
      IA = B;
      IB = A;
     }
   // si los puntos internos tienen que tomarse en consideración
   if(InternalPoints==true)
     {
      if((Trend=="Up") && ((IA%2==0) || ((IA-IB==1) && (IB%2==0))))
        {
         VA=Minimum[IA];
         IA=IA-IA%2;
        }
      else if((Trend=="Down") && ((IA%2==0) || ((IA-IB==1) && (IB%2==0))))
        {
         VA=Maximum[IA];
         IA=IA-IA%2;
        }
      else if((Trend=="Up") && ((IA%2==1) || ((IA-IB==1) && (IB%2==1))))
        {
         VA=Maximum[IA];
         IA=IA -(1-IA%2);
        }
      else if((Trend=="Down") && (IA%2==1) || ((IA-IB==1) && (IB%2==1)))
        {
         VA=Minimum[IA];
         IA=IA -(1-IA%2);
        }
      VB=ValueVertex[IB];
     }
   else
     {
      VA = ValueVertex[IA];
      VB = ValueVertex[IB];
     }
   if(A>B)
     {
      A = IA;
      B = IB;
     }
   else if(A<B)
     {
      A = IB;
      B = IA;
      VC = VA;
      VA = VB;
      VB = VC;
     }
   if(((FixedVertex[A]==1) && (FixedVertex[B]==1)) || 
      ((FixedVertex[A] == 0) &&(A % 2 == 0) && (FixedVertex[B] == 1)) ||
      ((FixedVertex[A] == 1) && (FixedVertex[B] == 0) && (B %2 == 1)) ||
      ((FixedVertex[A] == 0) & (A %2 == 0) && (FixedVertex[B] == 0) && (B % 2== 1)))
     {
      if(((Trend=="Up") && (VA>=VB)) || ((Trend=="Down") && (VA<=VB)))
         Result=1;
      else
         Result=-1;
     }
   return(Result);
  }

6.12. La función WaveAMoreWaveB:

//+-----------------------------------------------------------------------+
//| La función WaveAMoreWaveB comprueba si la onda A es más grande        |
//| que la onda B, que se transfieren como parámetros de esta función     |
//| esta verificación se puede realizar sólo si la onda A es completada,  |
//| y la onda B es completada o no completada o no iniciada               |
//+-----------------------------------------------------------------------+
int WaveAMoreWaveB(int A,int B)
  {
   int Result=0;
   double LengthWaveA=0,LengthWaveB=0;
   if(FixedVertex[A]==1 && FixedVertex[A-1]==1 && (FixedVertex[B]==1 || FixedVertex[B-1]==1))
     {
      LengthWaveA=MathAbs(ValueVertex[A]-ValueVertex[A-1]);
      if(FixedVertex[B]==1 && FixedVertex[B-1]==1) LengthWaveB=MathAbs(ValueVertex[B]-ValueVertex[B-1]);
      else if(FixedVertex[B]==1 && FixedVertex[B-1]==0)
        {
         if(Trend=="Up") LengthWaveB=MathAbs(ValueVertex[B]-Minimum[B]);
         else LengthWaveB=MathAbs(ValueVertex[B]-Maximum[B]);
        }
      else if(FixedVertex[B]==0 && FixedVertex[B-1]==1)
        {
         if(Trend=="Up")LengthWaveB=MathAbs(ValueVertex[B-1]-Minimum[B-1]);
         else LengthWaveB=MathAbs(ValueVertex[B-1]-Maximum[B-1]);
        }
      if(LengthWaveA>LengthWaveB) Result=1;
      else Result=-1;
     }
   return(Result);
  }

6.13. La función ClearTree:

//+------------------------------------------------------------------+
//| La función de limpieza del árbol de las ondas con                |
//| el nodo superior Node                                            |
//+------------------------------------------------------------------+
void ClearTree(TNode *Node)
  {
   if(CheckPointer(Node)!=POINTER_INVALID)
     {
      for(int i=0; i<Node.Child.Total();i++)
         ClearTree(Node.Child.At(i));
      delete Node.Child;
      if(CheckPointer(Node.Wave)!=POINTER_INVALID)delete Node.Wave;
      delete Node;
     }
  }

 6.14. La función ClearNodeInfoArray:

//+------------------------------------------------------------------+
//| La función de limpieza del array NodeInfoArray                   |
//+------------------------------------------------------------------+
void ClearNodeInfoArray()
  {
   for(int i=NodeInfoArray.Total()-1; i>=0;i--)
     {
      TNodeInfo *NodeInfo=NodeInfoArray.At(i);
      if(CheckPointer(NodeInfo.Node)!=POINTER_INVALID)delete NodeInfo.Node;
      delete NodeInfo;
     }
   NodeInfoArray.Clear();
  }

6.15. La función ClearZigzagArray:

//+------------------------------------------------------------------+
//| La función de limpieza del array ZigzagArray                     |
//+------------------------------------------------------------------+
void ClearZigzagArray()
  {
   for(int i=0;i<ZigzagArray.Total();i++)
     {
      TZigzag *Zigzag=ZigzagArray.At(i);
      delete Zigzag.IndexVertex;
      delete Zigzag.ValueVertex;
      delete Zigzag;
     }
   ZigzagArray.Clear();
  }

 6.16. La función FillLabelArray:

CArrayObj *LabelArray[];
int LevelMax=0;
//+------------------------------------------------------------------+
//| La función FillLabelArray                                        |
//+------------------------------------------------------------------+
void FillLabelArray(TNode *Node)
  {
   if(Node.Child.Total()>0)
     {
      // seleccionamos el primer nodo
      TNode *ChildNode=Node.Child.At(0);
      // obtenemos la estructura donde se almacena la información sobre la onda
      TWave *Wave=ChildNode.Wave;
      string Text;
      // si hay la primera cresta
      if(Wave.ValueVertex[1]>0)
        {
         // marcamos la cresta en función de la onda
         if(Wave.Name=="Impulso" || Wave.Name=="Cuña" || Wave.Name=="Diagonal")
            Text="1";
         else if(Wave.Name=="Zigzag" || Wave.Name=="Plana" || Wave.Name=="Triángulo expansivo" || 
            Wave.Name=="Triángulo contracto")
            Text="A";
         else if(Wave.Name=="Doble zigzag" || Wave.Name=="Doble tres" || 
            Wave.Name=="Triple zigzag" || Wave.Name=="Triple tres")
            Text="W";
         // obtenemos el array de crestas ArrayObj que tienen el índice Wave.IndexVertex[1] en el gráfico de precios 
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[1]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[1]]=ArrayObj;
           }
         // escribimos la información sobre las crestas con el índice Wave.IndexVertex[1] en el array ArrayObj
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[1];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[2]>0)
        {
         if(Wave.Name=="Impulso" || Wave.Name=="Cuña" || Wave.Name=="Diagonal")
            Text="2";
         else if(Wave.Name=="Zigzag" || Wave.Name=="Plana" || Wave.Name=="Triángulo expansivo" || 
            Wave.Name=="Triángulo contracto")
            Text="B";
         else if(Wave.Name=="Doble zigzag" || Wave.Name=="Doble tres" || 
            Wave.Name=="Triple zigzag" || Wave.Name=="Triple tres")
            Text="X";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[2]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[2]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[2];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[3]>0)
        {
         if(Wave.Name=="Impulso" || Wave.Name=="Cuña" || Wave.Name=="Diagonal")
            Text="3";
         else if(Wave.Name=="Zigzag" || Wave.Name=="Plana" || 
            Wave.Name=="Triángulo expansivo" || Wave.Name=="Triángulo contracto")
            Text="C";
         else if(Wave.Name=="Doble zigzag" || Wave.Name=="Doble tres" || 
            Wave.Name=="Triple zigzag" || Wave.Name=="Triple tres")
            Text="Y";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[3]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[3]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[3];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[4]>0)
        {
         if(Wave.Name=="Impulso" || Wave.Name=="Cuña" || Wave.Name=="Diagonal")
            Text="4";
         else if(Wave.Name=="Triángulo expansivo" || Wave.Name=="Triángulo contracto")
            Text="D";
         else if(Wave.Name=="Triple zigzag" || Wave.Name=="Triple tres")
            Text="XX";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[4]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[4]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[4];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[5]>0)
        {
         if(Wave.Name=="Impulso" || Wave.Name=="Cuña" || Wave.Name=="Diagonal")
            Text="5";
         else if(Wave.Name=="Triángulo expansivo" || Wave.Name=="Triángulo contracto")
            Text="E";
         else if(Wave.Name=="Triple zigzag" || Wave.Name=="Triple tres")
            Text="Z";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[5]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[5]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[5];
         ArrayObj.Add(Label);
        }
      // visitamos los nodos hijos del nodo actual
      for(int j=0;j<ChildNode.Child.Total();j++)
         FillLabelArray(ChildNode.Child.At(j));
     }
  }

 6.17. La función CreateLabels:

double PriceInPixels;
CArrayObj ObjTextArray; // declaramos el array donde van a guardarse los objetos gráficos "Texto"
//+------------------------------------------------------------------+
//| La función CreateLabels                                          |
//+------------------------------------------------------------------+
void CreateLabels()
  {
   double PriceMax =ChartGetDouble(0,CHART_PRICE_MAX,0);
   double PriceMin = ChartGetDouble(0,CHART_PRICE_MIN);
   int WindowHeight=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
   PriceInPixels=(PriceMax-PriceMin)/WindowHeight;
   int n=0;
   // repasamos el array LabelArray
   for(int i=0;i<ArrayRange(LabelArray,0);i++)
     {
      // si hay crestas con el mismo índice i
      if(CheckPointer(LabelArray[i])!=POINTER_INVALID)
        {
         // obtenemos las crestas con el mismo índice i
         CArrayObj *ArrayObj=LabelArray[i];
         // repasamos las crestas y mostramos en el gráfico
         for(int j=ArrayObj.Total()-1;j>=0;j--)
           {
            TLabel *Label=ArrayObj.At(j);
            int Level=LevelMax-Label.Level;
            string Text=Label.Text;
            double Value=Label.Value;
            color Color;
            int Size=8;
            if((Level/3)%2==0)
              {
               if(Text=="1") Text="i";
               else if(Text == "2") Text = "ii";
               else if(Text == "3") Text = "iii";
               else if(Text == "4") Text = "iv";
               else if(Text == "5") Text = "v";
               else if(Text == "A") Text = "a";
               else if(Text == "B") Text = "b";
               else if(Text == "C") Text = "c";
               else if(Text == "D") Text = "d";
               else if(Text == "E") Text = "e";
               else if(Text == "W") Text = "w";
               else if(Text=="X") Text="x";
               else if(Text == "XX") Text = "xx";
               else if(Text == "Y") Text = "y";
               else if(Text == "Z") Text = "z";
              }
            if(Level%3==2)
              {
               Color=Green;
               Text="["+Text+"]";
              }
            if(Level%3==1)
              {
               Color=Blue;
               Text="("+Text+")";
              }
            if(Level%3==0)
               Color=Red;
            int Anchor;
            if(Value==rates[i].high)
              {
               for(int k=ArrayObj.Total()-j-1;k>=0;k--)
                  Value=Value+15*PriceInPixels;
               Anchor=ANCHOR_UPPER;
              }
            else if(Value==rates[i].low)
              {
               for(int k=ArrayObj.Total()-j-1;k>=0;k--)
                  Value=Value-15*PriceInPixels;
               Anchor=ANCHOR_LOWER;
              }
            CChartObjectText *ObjText=new CChartObjectText;
            ObjText.Create(0,"wave"+IntegerToString(n),0,rates[i].time,Value);
            ObjText.Description(Text);
            ObjText.Color(Color);
            ObjText.SetInteger(OBJPROP_ANCHOR,Anchor);
            ObjText.FontSize(8);
            ObjText.Selectable(true);
            ObjTextArray.Add(ObjText);
            n++;
           }
        }
     }
   ChartRedraw();
  }

 6.18. La función CorrectLabel:

//+------------------------------------------------------------------+
//| La función CorrectLabel                                          |
//+------------------------------------------------------------------+
void CorrectLabel()
  {
   double PriceMax=ChartGetDouble(0,CHART_PRICE_MAX,0);
   double PriceMin = ChartGetDouble(0,CHART_PRICE_MIN);
   int WindowHeight=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
   double CurrentPriceInPixels=(PriceMax-PriceMin)/WindowHeight;
   // repasamos todos los objetos de texto (crestas de las ondas) y cambiamos su tamaño de precio
   for(int i=0;i<ObjTextArray.Total();i++)
     {
      CChartObjectText *ObjText=ObjTextArray.At(i);
      double PriceValue=ObjText.Price(0);
      datetime PriceTime=ObjText.Time(0);
      int j;
      for(j=0;j<ArrayRange(rates,0);j++)
        {
         if(rates[j].time==PriceTime)
            break;
        }
      double OffsetInPixels;
      if(rates[j].low>=PriceValue)
        {
         OffsetInPixels=(rates[j].low-PriceValue)/PriceInPixels;
         ObjText.Price(0,rates[j].low-OffsetInPixels*CurrentPriceInPixels);
        }
      else if(rates[j].high<=PriceValue)
        {
         OffsetInPixels=(PriceValue-rates[j].high)/PriceInPixels;
         ObjText.Price(0,rates[j].high+OffsetInPixels*CurrentPriceInPixels);
        }
     }
   PriceInPixels=CurrentPriceInPixels;
  }


7. Funciones de inicialización, deinicialización y procesamiento de eventos

En la función OnInit se crean los botones de manejo del analizador automático de las ondas de Elliott.

Se crean los siguientes botones:

  1. "Iniciar análisis" - se pone en marcha el análisis automático de las ondas,
  2. "Mostrar resultados" - las etiquetas de las ondas se visualizan en el gráfico,
  3. "Limpiar gráfico" - se limpia la memoria y se eliminan las etiquetas de las ondas del gráfico,
  4. "Corregir etiquetas" - se corrigen las etiquetas de las ondas en el gráfico.

El procesamiento de la pulsación sobre estos botones se lleva a cabo en la función de procesamiento de eventos OnChartEvent.

En la función OnDeinit se eliminan todos los objetos gráficos del gráfico, incluyendo los botones de administración, también se eliminan todos los objetos que se utilizan en el programa.

#include <Object.mqh>
#include <Arrays\List.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
#include <Elliott wave\Data structures.mqh>
#include <Elliott wave\Analysis functions.mqh>
#include <Elliott wave\Rules functions.mqh>
CChartObjectButton *ButtonStart,*ButtonShow,*ButtonClear,*ButtonCorrect;
int State;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   State=0;
   //creamos los botones de administración
   ButtonStart=new CChartObjectButton;
   ButtonStart.Create(0,"Iniciar análisis",0,0,0,150,20);
   ButtonStart.Description("Iniciar análisis");
   ButtonShow=new CChartObjectButton;
   ButtonShow.Create(0,"Mostrar resultados",0,150,0,150,20);
   ButtonShow.Description(Mostrar resultados");
   ButtonClear=new CChartObjectButton;
   ButtonClear.Create(0,"Limpiar gráfico",0,300,0,150,20);
   ButtonClear.Description("Limpiar gráfico");
   ButtonCorrect=new CChartObjectButton;
   ButtonCorrect.Create(0,"Corregir etiquetas",0,450,0,150,20);
   ButtonCorrect.Description("Corregir etiquetas");
   ChartRedraw();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //limpiamos el árbol de ondas
   ClearTree(FirstNode);
   //limpiamos el array NodeInfoArray
   ClearNodeInfoArray();
   //limpiamos el array ZigzagArray
   ClearZigzagArray();
   //limpiamos el array LabelArray
   for(int i=0;i<ArrayRange(LabelArray,0);i++)
     {
      CArrayObj *ArrayObj=LabelArray[i];
      if(CheckPointer(ArrayObj)!=POINTER_INVALID)
        {
         for(int j=0;j<ArrayObj.Total();j++)
           {
            TLabel *Label=ArrayObj.At(j);
            delete Label;
           }
         ArrayObj.Clear();
         delete ArrayObj;
        }
     }
   //quitamos todos los elementos gráficos del gráfico
   for(int i=ObjTextArray.Total()-1;i>=0;i--)
     {
      CChartObjectText *ObjText=ObjTextArray.At(i);
      delete ObjText;
     }
   ObjTextArray.Clear();
   delete ButtonStart;
   delete ButtonShow;
   delete ButtonClear;
   delete ButtonCorrect;
   ChartRedraw();
  }
MqlRates rates[];
TNode *FirstNode;
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Iniciar análisis" && State!=0)
      MessageBox("Primero pulse el botón \"Limpiar gráfico\"");
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Mostrar resultados" && State!=1)
      MessageBox("Primero pulse el botón \"Iniciar análisis\"");
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Limpiar gráfico && State!=2)
      MessageBox("Primero pulse el botón \"Mostrar resultados\"");
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Corregir etiquetas" && State!=2)
      MessageBox("Primero pulse el botón \"Mostrar resultados\"");
   //si se pulsa el botón Iniciar análisis"
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Iniciar análisis" && State==0)
     {
      //llenamos el array rates
      CopyRates(NULL,0,0,Bars(_Symbol,_Period),rates);
      //llenamos el array ZigzagArray
      FillZigzagArray(0,Bars(_Symbol,_Period)-1);
      //creamos el primer nodo
      TWave *Wave=new TWave;
      Wave.IndexVertex[0] = 0;
      Wave.IndexVertex[1] = Bars(_Symbol,_Period)-1;
      Wave.ValueVertex[0] = 0;
      Wave.ValueVertex[1] = 0;
      FirstNode=new TNode;
      FirstNode.Child=new CArrayObj;
      FirstNode.Wave=Wave;
      FirstNode.Text="Primer nodo";
      string NameWaves="Impulso,Cuña,Diagonal,Zigzag, Plana,Doble zigzag,Triple zigzag,Doble tres,Triple tres,Triángulo contracto,Triángulo expansivo";
      //llamamos a la función de búsqueda de las ondas no iniciadas e inacabadas
      NotStartedAndNotFinishedWaves(Wave,1,FirstNode,NameWaves,0);
      MessageBox(Análisis completado");
      State=1;
      ButtonStart.State(false);
      ChartRedraw();
     }
   //si se pulsa el botón "Mostrar resultados"
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Mostrar resultados" && State==1)
     {
      ArrayResize(LabelArray,ArrayRange(rates,0));
      //llenamos el array LabelArray
      FillLabelArray(FirstNode);
      //mostramos la marcación de ondas en el gráfico
      CreateLabels();
      State=2;
      ButtonShow.State(false);
      ChartRedraw();
     }
   //si se pulsa el botón "Limpiar gráfico"
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Limpiar gráfico" && State==2)
     {
      //limpiamos el árbol de ondas
      ClearTree(FirstNode);
      //limpiamos el array NodeInfoArray
      ClearNodeInfoArray();
      //limpiamos el array ZigzagArray
      ClearZigzagArray();
      //limpiamos el array LabelArray
      for(int i=0;i<ArrayRange(LabelArray,0);i++)
        {
         CArrayObj *ArrayObj=LabelArray[i];
         if(CheckPointer(ArrayObj)!=POINTER_INVALID)
           {
            for(int j=0;j<ArrayObj.Total();j++)
              {
               TLabel *Label=ArrayObj.At(j);
               delete Label;
              }
            ArrayObj.Clear();
            delete ArrayObj;
           }
        }
      //eliminamos el etiquetado del gráfico
      for(int i=ObjTextArray.Total()-1;i>=0;i--)
        {
         CChartObjectText *ObjText=ObjTextArray.At(i);
         ObjText.Delete();
        }
      ObjTextArray.Clear();
      State=0;
      ButtonClear.State(false);
      ChartRedraw();
     }
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==Corregir etiquetas" && State==2)
     {
      CorrectLabel();
      ButtonCorrect.State(false);
      ChartRedraw();
     }
  }

 Hemos considerado todas las funciones del análisis automático de las ondas de Elliott.

 

8. Medios para mejorar el programa

El programa del etiquetado automático de las Ondas de Elliott escrito en MQL5 tiene una serie de defectos:

  1. Un sistema imperfecto de comprobación de las reglas del etiquetado. Por ejemplo, cuando se realiza la verificación por reglas, no se toman en cuenta las relaciones de Fibonacci entre las ondas, tanto de tiempo, como de precio.
  2. Presencia de zonas no etiquetadas en el gráfico (lagunas en el etiquetado). Eso significa que no se puede construir una onda correcta según los puntos encontrados en el intervalo de tiempo establecido. La solución en esta situación puede ser el aumento del número de puntos para identificar una onda. Por ejemplo, para encontrar el impulso habrá que buscar 8 puntos y más, en vez de 6.
  3. Los resultados del etiquetado no visualizan ninguna información adicional: por ejemplo, no se construyen automáticamente los canales, no se calculan los objetivos, etc.
  4. En el artículo no está prevista la implementación del trabajo con el árbol de ondas (no se puede elegir un variante concreta del etiquetado), por eso en el gráfico se muestra sólo una de muchas opciones del etiquetado (la primera opción del etiquetado).
  5. Independientemente de que en el gráfico se visualice sólo una variante de las ondas, todas las demás opciones se encuentran en la memoria, ocupándola.
  6. El programa está orientado al etiquetado de los gráficos de mensuales hasta diarios, porque se observa un trabajo muy lento si hay una gran cantidad de barras (el etiquetado de un gráfico por hora puede tardar horas). El ejemplo del etiquetado de un gráfico EURUSD mensual se muestra en la Figura 18.

Etiquetado de ondas con analizador automático en MQL5
Fig. 18 Etiquetado de ondas con analizador automático en MQL5

Conclusión

En este artículo ha sido considerado el algoritmo del análisis automático de las ondas de Elliott. El presente algoritmo ha sido implementado en el lenguaje MQL5.

El programa tiene una serie de defectos mencionados antes y da motivos para su posterior resolución. Espero que este tema despierte interés en los aficionados a las ondas de Elliott, y dentro de poco aparezcan muchos programas que se encargan del análisis automático de las ondas.