Português
preview
Del básico al intermedio: Indicador (II)

Del básico al intermedio: Indicador (II)

MetaTrader 5Ejemplos |
37 0
CODE X
CODE X

En el artículo anterior, "Del básico al intermedio: Indicador (I)", empezamos a hablar, de manera práctica, de cómo puedes crear un indicador muy simple y sencillo utilizando muy poco conocimiento. Aunque muchos piensen que se necesita una gran cantidad de código y conocimientos para conseguir resultados, quedó bastante claro que incluso un principiante, con algo de estudio y esfuerzo, puede crear algo relativamente sencillo y funcional. Y de una manera bastante elegante, ya que gran parte del trabajo se está trasladando a MetaTrader 5. A nosotros solo nos corresponde implementar de manera adecuada la respuesta a un evento. Así, MetaTrader 5 sabrá qué hacer con el resto de la información que estamos tratando durante los eventos que puedan estar ocurriendo.

Pues bien, tal vez estés pensando: «Pero ese código que logramos crear no se parece a ningún indicador que haya visto antes». Aunque estamos consiguiendo trazar algo en el gráfico, no es exactamente lo que esperaba. Esperaba poder hacer muchas más cosas.

Vale, mi querido lector, tal vez te parezca que todo esto no tiene buena pinta. Pero debes recordar que el conocimiento no se adquiere de la noche a la mañana. Necesita ser cultivado. El objetivo de estos artículos es precisamente ese: cultivar y difundir conocimiento. No se trata de decir: «Haz esto, pues solo funcionará de esta forma», como estoy mostrando. Así que veamos hasta dónde llegamos en el artículo anterior.


Cómo implementar una media móvil de X períodos

Bien, en el artículo anterior, vimos cómo trazar información en el gráfico. Lo hicimos de una forma sencilla de entender, completamente funcional y bastante didáctica. No obstante, el objetivo del artículo era mostrar precisamente que existe una forma de hacer un mejor uso de los recursos disponibles en MetaTrader 5, por lo que no profundicé mucho en ciertos criterios o posibilidades.

Sin embargo, ese tipo de cosa que se mostró en el artículo anterior se podría lograr de varias otras maneras. No obstante, creo que muchos querrían ver cómo se podría implementar un sistema de medias móviles más habitual. Es decir, lo que resultaría interesante es poder crear medias móviles que se asemejen a las que vemos, como la famosa media móvil exponencial de nueve períodos, conocida precisamente por ser un modelo operativo muy rentable, aunque difícil de seguir a pesar de su extrema simplicidad.

Vale, entonces, querido lector, puedes estar pensando cómo convertir aquel código aburrido, implementado en el artículo anterior, en otro más interesante. Esto tal vez sea mucho más difícil de lograr. Ahora creo que te verás obligado a crear algo con muchas más líneas, no esa cosa que se mostró en el artículo anterior.

Pues bien, acepto el reto: crear un indicador de medias móviles con la menor cantidad posible de código. Cuanto menos código, mejor. Pero, aunque esto suponga un reto para muchos, a mí me parece algo aburrido y monótono. Antes de ver cómo podemos hacerlo, revisemos el código disponible en el anexo del artículo anterior. Este puede verse a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buffer[];
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
15. 
16.    return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21.    for (int c = prev_calculated; c < rates_total; c++)
22.       gl_buffer[c] = price[c];
23. 
24.    return rates_total;
25. };
26. //+------------------------------------------------------------------+

Código 01

Creo que ya habrás estudiado y practicado bastante con ese código 01, ya que es muy sencillo y divertido, y nos permite entender cosas que de otro modo serían difíciles de comprender. Pero vayamos al desafío propuesto: ¿cómo podemos hacer que este código sea capaz de calcular y trazar una media móvil? Para cumplir este objetivo, primero debemos tomar algunas decisiones. No son necesarias, pero es bueno para practicar cómo pensar en un código que queremos implementar.

La primera decisión que debemos tomar es qué tipo de cálculo queremos utilizar. Sí, existen diferencias entre los cálculos de media móvil. Hay medias ponderadas y no ponderadas. Esta ponderación puede ser en términos de volumen, precio o tiempo. Entenderlo y saber cómo elegirlo es importante. En el caso de una media móvil exponencial, esta termina siendo ponderada por el tiempo o por el número de períodos presentes en ella. Una VWAP sería una media móvil ponderada por el volumen en el precio, ¿o sería al contrario? Bueno, ¿quién sabe? De cualquier manera, el tiempo no se tendría en cuenta. En el caso de una media móvil aritmética, no tenemos esta ponderación y el valor se calcula teniendo en cuenta solo un dato y no su variación con respecto a otros datos. Para simplificar, llamamos a las medias «media móvil exponencial» y «media móvil simple».

Es precisamente la relación de un dato con otros lo que genera la ponderación. No obstante, no vamos a entrar en detalles matemáticos aburridos, ya que no es necesario en este caso. Sin embargo, necesitamos conocer, al menos, la fórmula del tipo de cálculo que vamos a implementar. De lo contrario, ¿cómo podríamos implementar algo sin saber cómo se calcula? Como la media móvil aritmética es muy simple de calcular —ya que solo hay que ir sumando y restando valores dentro de un determinado número de períodos—, vamos a ver un caso un poco más complicado: la media exponencial, que muchos creen que lo es.

La fórmula que vamos a adoptar se muestra justo abajo.

Imagen 01

En esta imagen 01, se muestra la expresión para calcular la media exponencial con cualquier número de períodos. En esta fórmula, P representa el valor de la cotización actual. El valor M representa el valor anterior de la media exponencial. N representa el número de períodos que se utilizarán en la media. Con esta información, ya podemos pasar a la fase de implementación del cálculo. Antes, aclararé un pequeño detalle que mencioné hace poco: la media aritmética se calcula sumando un X número de valores y dividiendo el resultado por ese X utilizado.

Pero, cuando vayamos a calcular X + 1, es decir, el siguiente período, todo lo que necesitaremos hacer es restar el valor de la primera entrada de la suma y sumar el valor de la nueva entrada. El resultado lo dividimos entre X. Por eso dije que la media aritmética es aburrida y sin gracia, ya que solo necesitamos sumar y restar. Sin embargo, se cree que la media exponencial requiere una cantidad enorme de cálculos. Sin embargo, te darás cuenta de que es casi lo mismo que la media aritmética.

De acuerdo, entonces veamos qué significa esta expresión que aparece en la imagen 01: para calcular la MME actual (media móvil exponencial), necesitamos el valor anterior de la media. Hmm... esto parece un problema, ya que cuando iniciamos el cálculo no tenemos aún ese valor. Por eso, necesitamos un pequeño recurso matemático. El recurso es simplemente hacer que M sea igual a cero. Ahora sí podemos implementar el cálculo. Para ello, basta con mirar el código 01 y ver dónde debemos modificarlo. En este caso, solo es necesario modificar la línea 22. Sin embargo, para que no haya malos entendidos y puedas comprender que no es necesario complicar el código, voy a mostrar el código completo. Puedes verlo justo a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buffer[];
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
15. 
16.    return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21.    for (int c = prev_calculated; c < rates_total; c++)
22.       gl_buffer[c] = (price[c] - (c ? gl_buffer[c - 1] : 0)) * (2. / (1.0 + 9)) + (c ? gl_buffer[c - 1] : 0);
23. 
24.    return rates_total;
25. };
26. //+------------------------------------------------------------------+

Código 02

Observa que lo único que modificamos en el código fue la línea 22. NO SE MODIFICÓ NADA MÁS. Sin embargo, cuando ejecutamos el código, vemos lo que se muestra a continuación.

Imagen 02

Quizás te estés preguntando: «Pero ¿realmente estamos viendo una media móvil exponencial de nueve períodos en esta imagen 02?» Es difícil creer que para colocar una media móvil exponencial se necesite tan poco. Para comprobarlo, vamos a utilizar un indicador que viene por defecto con MetaTrader 5 y que nos permitirá verificar si estamos haciendo las cosas de la manera correcta. Se puede observar en la siguiente animación:

Animación 01

En esta animación podemos ver, al fondo, la media de la imagen 02 y, en primer plano, el indicador de verificación. Fíjate en los valores que estamos utilizando. Según estos valores, el indicador se colocará en el gráfico como se ve en la animación. Es perfecto. De hecho, el código 02 está calculando una media exponencial de nueve períodos. Pero, ¿cómo es posible?

Bien, si has estudiado y practicado los conceptos mostrados en los artículos, te darás cuenta de que simplemente estamos utilizando la expresión que se muestra en la imagen 01. Sin embargo, como se ha dicho, es necesario realizar un pequeño ajuste, ya que necesitamos poner a cero el primer valor de la cadena de cálculos. Por eso, la expresión puede resultar un poco confusa a primera vista. No obstante, si lo prefieres, puedes cambiar el código de la línea 22 por otro un poco menos compacto. Así, el manejador del evento Calculate quedará como se muestra a continuación.

                   .
                   .
                   .
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21. #define def_N     9
22.    
23.    for (int c = prev_calculated; c < rates_total; c++)
24.    {
25.       double M = (c ? gl_buffer[c - 1] : 0);
26.    
27.       gl_buffer[c] = (price[c] - M) * (2. / (1.0 + def_N)) + M;
28.    }
29. 
30.    return rates_total;
31. 
32. #undef def_N
33. };
34. //+------------------------------------------------------------------+

Fragmento 01

En este fragmento 01 se hace lo mismo que en el código 02, solo que con unas cuantas líneas más. Perfecto, pero antes de continuar, debo advertirte, querido lector, de que hay un pequeño problema en este código. No obstante, no voy a explicar cuál es ese problema. Te dejaré con la curiosidad. Así, sin duda, vas a intentar entenderlo y podrás formarte una opinión más clara sobre ciertos aspectos de la programación, como, por ejemplo, que no siempre debemos hacer las cosas de una forma determinada solo porque una expresión matemática nos diga que se haga así.

Para que el problema deje de existir, necesitamos cambiar el código por lo que se muestra justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buffer[];
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
15. 
16.    return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
22.       gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * 2. / (1 + 9)) + gl_buffer[c - 1] : price[c] * 2. / (1 + 9));
23. 
24.    return rates_total;
25. };
26. //+------------------------------------------------------------------+

Código 03

En cualquier caso, no te preocupes, porque en el anexo tendrás ambos códigos para que puedas experimentar e intentar entender qué problema es ese que, al mirar el código, parece no tener sentido. De cualquier manera, ahora creo que podemos hablar de otro asunto. Seguro que estás pensando: «Amigo, ¿y si quiero utilizar una media móvil exponencial con un número diferente de períodos? ¿Tendré que compilar el código cada vez que quiera usar un valor de período diferente?». Y esto no parece muy prometedor. De hecho, mi amigo lector, este tipo de implementación que hemos visto hasta ahora solo atiende a un modelo muy específico. Sin embargo, sin entender cada punto implementado hasta ahora, resulta difícil comprender los nuevos puntos que van surgiendo.

Pero, debido a todo lo explicado hasta ahora, creo que ya estás listo para pasar al siguiente nivel de implementación. Pero, para no mezclarlo todo, vamos a cambiar de tema.


Interactuar con el usuario

Muy, pero muy raramente, alguien querrá implementar un código como el mostrado en el tema anterior, precisamente debido al hecho de que no podemos cambiar el número de períodos del cálculo. Es verdad que existen casos, como el cálculo de una VWAP, en los que la media que se traza es atemporal. Sin embargo, este sería un caso bastante específico, lo que no le quita mérito ni la necesidad de explicar y entender lo que se verá en este tema.

A continuación, veremos cómo podemos permitir que el usuario cambie algunos parámetros de cálculo sin necesidad de volver a compilar el código. Para lograrlo, debemos indicar al compilador MQL5 que queremos permitir que el usuario o otras aplicaciones informen de los valores internos utilizados en nuestro código. Es decir, tendremos un tipo de constante que, para el usuario, será una variable. Sin embargo, desde la perspectiva de nuestra aplicación, lo que vamos a crear se considerará una constante. Sí, mi querido lector, es posible realizar estos cambios directamente mediante otras aplicaciones, como veremos más adelante, aunque, en principio, dicho recurso tenga como objetivo permitir una interacción más directa con el usuario.

Muy bien, para empezar, tenemos que ajustar el código 03. Esto es porque, tal y como está, terminará haciendo las cosas un tanto confusas. Así, solo por motivos didácticos y no de rendimiento, vamos a cambiar el código por el que se muestra justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buffer[];
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
15. 
16.    return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21.    double k = 2. / (1 + 9);
22. 
23.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
24.       gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price[c] * k);
25. 
26.    return rates_total;
27. };
28. //+------------------------------------------------------------------+

Código 04

Observa que el cambio es bastante sencillo, ya que solo aparece la línea 21, que crea una constante para ajustar el cálculo de la media exponencial. Ahora viene la cuestión: el valor que necesitamos cambiar aquí es precisamente el nueve que podemos ver en la línea 21 del código 04. Para ello, basta con añadir una nueva línea a este código 04. De este modo, podremos indicar cuál es el período que deseamos utilizar en el cálculo de la media. Este cambio se puede ver justo a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. input uchar user01 = 9;
11. //+----------------+
12. double   gl_buffer[];
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
17. 
18.    return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
22. {
23.    double k = 2. / (1 + user01);
24. 
25.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
26.       gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price[c] * k);
27. 
28.    return rates_total;
29. };
30. //+------------------------------------------------------------------+

Código 05

Eso es todo. Ahora, el usuario puede indicar cuál será el período de la media móvil exponencial que se va a utilizar. Sin embargo, aún queda un pequeño detalle por explicar. Cuando vayas a colocar este indicador en el gráfico, verás la imagen que se muestra a continuación.

Imagen 03

Fíjate en que estoy marcando una nueva pestaña en esta imagen. Esta pestaña no existía antes, pero se creó precisamente por la línea diez del código 05. Es decir, este es el punto de interacción con el usuario, que le permite ajustar y configurar algunas constantes que vamos a utilizar en el código. Hasta aquí todo bien, todo muy lindo y maravilloso. Sin embargo, al seleccionar la nueva pestaña, nos encontramos con la imagen que se ve justo abajo.

Imagen 04

Y este es el problema. Fíjate en la información que estoy resaltando en la imagen 04. Bien, para nosotros, que estamos desarrollando la aplicación, sabemos muy bien qué va a hacer este valor. Sin embargo, a medida que añadimos más valores, empieza a volverse un tanto confuso y hasta quien implementó el código tiene dificultades para saber qué representa cada valor. Y es aquí donde necesitamos recurrir a otro pequeño detalle. Este detalle se transmitirá al compilador de una forma bastante inusual. Al menos a mí, me pareció un tanto extraño al principio, pero acabé acostumbrándome.

Como se puede observar en la imagen 04, la cadena de caracteres destacada es la misma que da nombre a la constante vista en la línea diez del código 05. Y sí, querido lector, en la línea diez no estamos declarando una variable, sino una constante. En casos simples, podemos poner un nombre representativo para nuestro código y para el usuario de la aplicación. Sin embargo, esto no siempre se aplica, ya que, en la mayoría de los casos, mostrar un pequeño texto al usuario es lo más adecuado. Entonces, ¿cómo podemos colocar un texto en ese punto destacado de la imagen 04?

Esta es la parte inusual. Para hacerlo, necesitamos añadir un comentario de tipo línea. Como este tipo de comentario debe colocarse obligatoriamente después de la declaración de la constante, tenemos que hacer algo parecido a lo que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. input uchar user01 = 9;             //Exponential average periods
11. //+----------------+
                   .
                   .
                   .

Código 06

Observa atentamente este código 06, especialmente la línea 10. Lo que se ha añadido a esta línea es lo que llamamos un comentario de línea. Cuando hacemos esto en una constante que será mostrada en la pestaña de la imagen 04, le estamos diciendo al compilador que utilice ese texto como una cadena que se mostrará al usuario. Para dejar esto mucho más claro y para que realmente comprendas lo que se ha dicho, observa lo que sucede cuando vemos la misma pestaña que se muestra en la imagen 04, pero utilizando lo que se muestra en este código 06. Esto se puede observar en la imagen de abajo.

Imagen 05

Con esta imagen 05, cualquier persona sabrá qué tipo de cosa se está ajustando en la aplicación, lo que facilita mucho su uso.

¿Vieron qué fácil es crear un indicador? Hemos necesitado muy poco para que las cosas funcionen y se adapten perfectamente a un determinado tipo de interés. Aún quedan algunas cosas por hacer para que un indicador pueda ser más útil e integrarse mejor en MetaTrader 5 y poder utilizarlo para otros propósitos. Sin embargo, iremos viendo estas cosas poco a poco para que puedas comprender cómo y por qué usar esta o aquella función de la biblioteca estándar de MQL5 dentro de un indicador. Como puedes ver, necesitamos pocas funciones para que el indicador funcione. No obstante, aún no es un indicador realmente versátil.

Muy bien, esta fue la primera parte. Ahora veremos cómo utilizar la segunda versión de OnCalculate. Esta primera versión atiende a ciertos criterios, pero es posible que necesitemos más información. Por esta razón, necesitamos la otra versión. Pero, para no mezclarlo todo, veremos esto en otro tema.


Cómo usar la segunda versión de OnCalculate

En artículos anteriores, mencionamos que la función OnCalculate es la única función de la biblioteca estándar de MQL5 que está sobrecargada. Esto se debe a que, en algunos casos, podemos usar una versión y, en otros, necesitaremos usar la otra. Sin embargo, la elección de una u otra versión no solo afecta a lo que se muestra en la interfaz de usuario, sino también a la forma en que tú, mi querido lector, deberás plantearte la implementación. Sin la ayuda de MetaTrader 5, debemos tener más cuidado y prestar más atención durante la implementación. Esto se debe a que, a diferencia de la primera versión, esta segunda cuenta con más datos que se pueden utilizar y, dependiendo de cómo se utilicen, se obtendrá un tipo de resultado u otro.

Debido a la naturaleza de lo que se puede hacer al utilizar esta segunda versión de OnCalculate, por ahora solo mostraré cómo sería el mismo código del indicador, pero en esta versión sobrecargada. Es importante que entiendas que existe una diferencia entre usar una u otra versión. Pero las diferencias las creas tú, como programador. Así, el mismo código 06, visto en el tema anterior, quedaría como se muestra a continuación, si se usa la segunda versión de OnCalculate.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. input uchar user01 = 9;             //Exponential average periods
11. //+----------------+
12. double   gl_buffer[];
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
17. 
18.    return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
22. {
23.    double k = 2. / (1 + user01);
24. 
25.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
26.       gl_buffer[c] = (c ? ((Close[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : Close[c] * k);
27. 
28.    return rates_total;
29. };
30. //+------------------------------------------------------------------+

Código 07

Ahora, presta atención a un detalle, querido lector. A diferencia de lo que ocurría antes, cuando podíamos seleccionar qué tipo de información se usaría para trazar la media, en este código 07 ya no será posible hacerlo. La razón es que estamos «fijando» el cálculo sobre uno de los valores, en este caso, el valor de cierre de la barra. Toda la información restante será ignorada, ya que no se utilizará. Pero no es solo esto lo que ha cambiado. ¿Recuerdas que se dijo que la interfaz de usuario también cambiaría? Cuando vayas a colocar este indicador, visto en el código 07, en el gráfico, notarás la ausencia de una de las pestañas, como se puede ver en la imagen de abajo.

Imagen 06

En esta imagen 06, se puede observar que ya no tenemos una de las pestañas que había antes. El motivo es que el cálculo del indicador está fijado en un tipo de valor determinado. Por tanto, el usuario no podrá modificar el origen como se hacía antes. Sin embargo, esto no impide utilizar los demás tipos de cálculo. Podemos dar al usuario la opción de elegir el tipo de entrada que se utilizará en el cálculo. Para ello, bastará con añadir nuevas entradas input.

Esta parte es muy interesante. Sin embargo, como es bastante simple, vamos a aprovechar el resto del artículo para explicar cómo hacerlo. Así, ya podrás comenzar a crear diversas soluciones de forma interesante y creativa.

Para dar acceso al usuario a otros tipos de valores de entrada, podemos utilizar varios métodos diferentes. Mi preferido es el uso de enumeradores. Los enumeradores son fáciles de implementar, simples de entender y modificar, por eso me gustan. Básicamente, necesitamos una pequeña secuencia de pasos para que todo quede bastante agradable y sea intuitivo, tanto para ti, que estarás implementándolo, como para el usuario que va a utilizar tu aplicación.

l primer paso es crear el enumerador. Vamos a crear uno muy sencillo, solo para mostrar cómo se hace. Por tanto, el código 07 se modificará por lo que se muestra a continuación.

                   .
                   .
                   .
09. //+----------------+
10. enum Enum_TypeInput {
11.             HIGH,
12.             OPEN,
13.             CLOSE,
14.             LOW
15.                     };
16. //+----------------+
17. input uchar user01 = 9;             //Exponential average periods
18. input uchar user02 = HIGH;          //Data type for calculation
19. //+----------------+
                   .
                   .
                   .

Código 08

Al ejecutar el código 07 con estos cambios vistos en el código 08, tú vas a ver lo que se muestra en la imagen justo abajo.

Imagen 07

¡Hombre! Vaya, pero no es esto lo que yo quería hacer. Lo que quería era mostrar al usuario los datos o tipos definidos en la enumeración de la línea diez para que pudiera elegir en función de este tipo de enumeración. ¿Por qué no funcionó? Bien, mi querido lector, el motivo es el tipo de dato esperado en la línea 18. Observa lo siguiente: has declarado la enumeración, y esto está bien; el problema es que la enumeración se está convirtiendo en valores enteros. El compilador no entendió lo que querías hacer. Para corregirlo, basta con cambiar el tipo de valor esperado por la constante. También vamos a cambiar el tipo HIGH por CLOSE, que sería el más común en la mayoría de los casos. Así, el código 08 se modificará al que se muestra justo abajo.

                   .
                   .
                   .
16. //+----------------+
17. input uchar             user01 = 9;             //Exponential average periods
18. input Enum_TypeInput    user02 = CLOSE;         //Data type for calculation
19. //+----------------+
                   .
                   .
                   .

Código 09

Tras aplicar esta modificación del código 09 al 08, podemos ver el siguiente resultado al ejecutar el código en el terminal, justo abajo.

Imagen 08

Mira que funciona. Pero no solo esto. Como el compilador ha entendido lo que deseas hacer, también puedes seleccionar elementos utilizando el mismo texto que se encuentra en la enumeración declarada en la línea diez del código 08. Sin embargo, presta atención, querido lector: a diferencia de lo que ocurría antes, cuando teníamos la asistencia de MetaTrader 5, ahora estamos por nuestra cuenta. Es decir, no basta con colocar las cosas como se ha visto arriba para que todo funcione de inmediato. Necesitamos modificar el manejador del evento. En este caso, pasamos al siguiente paso, que consiste en hacer que nuestro código utilice la selección realizada por el usuario. Para ello, basta con modificar el código como se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. enum Enum_TypeInput {
11.             HIGH,
12.             OPEN,
13.             CLOSE,
14.             LOW
15.                     };
16. //+----------------+
17. input uchar             user01 = 9;             //Exponential average periods
18. input Enum_TypeInput    user02 = CLOSE;         //Data type for calculation
19. //+----------------+
20. double   gl_buffer[];
21. //+------------------------------------------------------------------+
22. int OnInit()
23. {
24.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
25. 
26.    return INIT_SUCCEEDED;
27. };
28. //+------------------------------------------------------------------+
29. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
30. {
31.    double   k = 2. / (1 + user01),
32.             price = 0;
33. 
34.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
35.    {
36.         switch (user02)
37.         {
38.             case HIGH   :
39.                 price = High[c];
40.                 break;
41.             case OPEN   :
42.                 price = Open[c];
43.                 break;
44.             case CLOSE  :
45.                 price = Close[c];
46.                 break;
47.             case LOW    :
48.                 price = Low[c];
49.                 break;
50.         }
51.         gl_buffer[c] = (c ? ((price - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price * k);
52.    }
53. 
54.    return rates_total;
55. };
56. //+------------------------------------------------------------------+

Código 10

En este código, podemos observar que en la línea 32 he añadido una nueva variable. Su objetivo es permitirnos controlar las cosas de forma más sencilla. En la línea 36, añadimos un comando switch para capturar el valor que se colocará en la variable price. Al hacer esto, no es necesario realizar muchos cambios en el código de cálculo, ya que solo es preciso ajustar la línea de cálculo para usar la variable price, tal y como se puede observar en la línea 51. Genial, ¿no crees, querido lector? Pero podemos hacer una última cosa antes de finalizar este artículo.

Observa lo siguiente: en la enumeración declarada en la línea 10, hay algunos valores que son claros y se pueden utilizar directamente en la interfaz. Sin embargo, es posible que prefieras utilizar un código diferente. Entonces, ¿cómo podríamos dejar las cosas claras tanto para futuros usuarios como para ti? De este modo, la información sobre qué tipo de valor se utilizará en el cálculo podrá comprenderse fácilmente. Bien, para hacer esto, hacemos algo parecido a lo que hacemos en las líneas de entrada. Es decir, añadiremos un comentario de línea. Así llegamos al código que se muestra justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. enum Enum_TypeInput {
11.             en_HIGH,           //Use maximum prices
12.             en_OPEN,           //Use opening prices
13.             en_CLOSE,          //Use closing prices
14.             en_LOW             //Use minimum prices
15.                     };
16. //+----------------+
17. input uchar             user01 = 9;             //Exponential average periods
18. input Enum_TypeInput    user02 = en_CLOSE;      //Data type for calculation
19. //+----------------+
20. double   gl_buffer[];
21. //+------------------------------------------------------------------+
22. int OnInit()
23. {
24.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
25. 
26.    return INIT_SUCCEEDED;
27. };
28. //+------------------------------------------------------------------+
29. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
30. {
31.    double   k = 2. / (1 + user01),
32.             price = 0;
33. 
34.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
35.    {
36.         switch (user02)
37.         {
38.             case en_HIGH   :
39.                 price = High[c];
40.                 break;
41.             case en_OPEN   :
42.                 price = Open[c];
43.                 break;
44.             case en_CLOSE  :
45.                 price = Close[c];
46.                 break;
47.             case en_LOW    :
48.                 price = Low[c];
49.                 break;
50.         }
51.         gl_buffer[c] = (c ? ((price - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price * k);
52.    }
53. 
54.    return rates_total;
55. };
56. //+------------------------------------------------------------------+

Código 11

Fíjate en cómo quedó el código al final. Este es el código que tendrás en el anexo para poder estudiar y practicar. Al ejecutarlo, obtendremos en el terminal de MetaTrader 5 lo que se muestra justo abajo.

Imagen 09

Como se ha dicho, acabamos de ocultar al usuario toda la información relacionada con la estructura interna del código. Para el usuario, no habrá cambiado nada. Sin embargo, para ti, como programador, este código es un poco más difícil de implementar, ya que tenemos muchas reglas y menos ayuda de MetaTrader 5 que antes. Pero, como cada caso es un caso, ahora ya sabes cómo implementar un indicador sencillo que, sin embargo, te puede enseñar mucho sobre otras cosas.


Consideraciones finales

En este artículo, mostré cómo puedes implementar un indicador simple de manera relativamente sencilla siguiendo una serie de pasos. No mostré cosas como el desplazamiento de la línea de trazado, pero como es muy simple, basta con añadir una constante más al código y utilizarla en la función de evento OnCalculate, así que no voy a mostrar cómo hacerlo, quedando este tipo de cosas como tarea para quienes quieran practicar e intentar entender más sobre indicadores.

En el próximo artículo vamos a abordar otra cosa también relacionada con indicadores. Entonces, hasta entonces.

Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15802

Archivos adjuntos |
Anexo.zip (3.04 KB)
Redes generativas antagónicas (GAN) para datos sintéticos en modelos financieros (Parte 1): Introducción a las GAN y los datos sintéticos en modelos financieros Redes generativas antagónicas (GAN) para datos sintéticos en modelos financieros (Parte 1): Introducción a las GAN y los datos sintéticos en modelos financieros
Este artículo presenta a los operadores bursátiles las redes generativas antagónicas (Generative Adversarial Networks, GAN) para generar datos financieros sintéticos, abordando las limitaciones de datos en el entrenamiento de modelos. Este artículo presenta a los operadores bursátiles las redes generativas antagónicas (GAN) para generar datos financieros sintéticos, abordando las limitaciones de datos en el entrenamiento de modelos.
Del básico al intermedio: Eventos (II) Del básico al intermedio: Eventos (II)
En este artículo veremos que no siempre es necesario implementar las cosas de una u otra manera. Existen formas alternativas de hacer las cosas. Comprender los conceptos explicados en artículos anteriores es primordial para entender adecuadamente el contenido de este artículo. El contenido expuesto aquí tiene como objetivo único y exclusivo la didáctica. En ningún caso debe considerarse una aplicación final, en la que el objetivo no sea el estudio de los conceptos aquí mostrados.
De lo básico a intermedio: Indicador (III) De lo básico a intermedio: Indicador (III)
En este artículo, veremos cómo declarar diversos indicadores de representación gráfica, como DRAW_COLOR_LINE y DRAW_FILLING. Además, por supuesto, aprenderemos a trazar múltiples indicadores de forma sencilla, práctica y rápida. Esto puede cambiar realmente tu forma de ver MetaTrader 5 y el mercado en general.
Creación de un Panel de Administración de Operaciones en MQL5 (Parte V): Panel de Gestión de Operaciones (II) Creación de un Panel de Administración de Operaciones en MQL5 (Parte V): Panel de Gestión de Operaciones (II)
En este artículo, mejoraremos el Panel de Gestión Comercial de nuestro Panel de Administración multifuncional. Hoy introduciremos una potente función de ayuda que simplificará el código, mejorando su legibilidad, su mantenimiento y su eficiencia. También demostraremos cómo integrar sin problemas botones adicionales y mejorar la interfaz para gestionar una gama más amplia de tareas de negociación. Ya sea para gestionar posiciones, ajustar órdenes o simplificar las interacciones de los usuarios, esta guía le ayudará a desarrollar un panel de gestión de operaciones sólido y sencillo de usar.