
La implementación del análisis automático de las Ondas de Elliott en MQL5
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: | |
---|---|
![]() Fig. 1 Impulso |
|
2. Cuñas: | |
![]() Fig. 2 Cuña |
|
3. Diagonales: | |
![]() Fig. 3 Diagonal |
|
Las ondas correctivas se clasifican como: | |
4. Zigzags: | |
![]() Fig. 4 Zigzag |
|
5. Planas: | |
![]() Fig. 5 Plana |
|
6. Doble zigzag: | |
![]() Fig. 6 Doble zigzag |
|
7. Triple zigzag: | |
![]() Fig. 7 Triple zigzag |
|
8. Tres dobles: | |
![]() Fig. 8 Doble tres |
|
9. Tres triples: | |
![]() Fig. 9 Triple tres |
|
10. Triángulos contractos: | |
![]() Fig. 10 Triángulo contracto |
|
11. Triángulos expansivos: | |
![]() Fig. 11 Triángulo expansivo |
|
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:
- 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.
- 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
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.
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:
- Ondas no iniciadas:
- 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);
- 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);
- 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);
- 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);
- Las ondas con la quinta onda no iniciada: 5< (por ejemplo, Impulso con la onda no iniciada 5; número de puntos necesarios - 1);
- 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);
- Ondas inacabadas:
- 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);
- 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);
- 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);
- Las ondas con la segunda onda inacabada: 1-2> (por ejemplo, Zigzag con la onda inacabada B; número de puntos necesarios -2);
- Las ondas con la primera onda inacabada: 1> (por ejemplo, Plana con la onda inacabada A; número de puntos necesarios -1);
- Ondas no iniciadas e inacabadas:
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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:
- La onda con la primera onda no iniciada A - A<-B-C;
- La onda con la primera onda no iniciada W y la segunda onda inacabada X - W<-X>;
- Las ondas acabadas B y C;
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:
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".
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;
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.
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.
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.
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.
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.
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.
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).
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
La función de limpieza del árbol de las ondas con el nodo superior Node:
void ClearTree(TNode *Node)
Función del limpieza del array ClearNodeInfoArray:
void ClearNodeInfoArray()
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
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
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[];
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.
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
//+------------------------------------------------------------------+ //| 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++; } }
//+------------------------------------------------------------------+ //| 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); }
//+------------------------------------------------------------------+ //| 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); }
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); }
//+------------------------------------------------------------------+ //| 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:
- "Iniciar análisis" - se pone en marcha el análisis automático de las ondas,
- "Mostrar resultados" - las etiquetas de las ondas se visualizan en el gráfico,
- "Limpiar gráfico" - se limpia la memoria y se eliminan las etiquetas de las ondas del gráfico,
- "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:
- 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.
- 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.
- 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.
- 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).
- 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.
- 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.
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.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/260





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso