Del básico al intermedio: Indicador (V)
Introducción
En el artículo anterior Del básico al intermedio: Herencia, hablamos sobre cómo usar herencia en una programación estructurada. Como no quiero poner información sobre más información, para no dejarte completamente aturdido con tantas cosas por absorber, en este artículo vamos a trabajar en algo un poco más simple, o al menos en algo que tú podrás estudiar y practicar en paralelo con lo que se mostró en los artículos anteriores.
Como sé que ese tema puede ser algo complicado al inicio para comprenderlo correctamente, y ya que fueron seis artículos seguidos con material bastante denso, vamos a relajarnos y ver algo un poco más simple y práctico.
En los artículos sobre indicadores, siendo Del básico al intermedio: Indicador (IV) el último en esa línea, vimos cómo podríamos trazar algo directamente en el gráfico, usando una implementación completamente estática y muy simple, además, claro, de ser una forma muy directa de crear un indicador. Sin embargo, aunque el formato mostrado en aquellos artículos, en particular, haya demostrado ser el ideal para prácticamente casi cualquier indicador que tú puedas llegar a construir, no estamos, de hecho, limitados solo a eso. También tenemos la posibilidad de crear indicadores dinámicos, si es que podemos clasificarlos así.
Pero, para que tú, mi querido lector, puedas entender por qué considero aquel formato anterior como un formato estático, y en qué se diferenciaría de un formato de creación dinámica, necesito explicar algunas cosas. Pero, para hacerlo de manera adecuada, vayamos a un nuevo tema.
Diferencia entre un indicador estático y uno dinámico
Muy probablemente, muchos otros programadores no estén de acuerdo con lo que voy a decir. Pero eso es un mero detalle, ya que cada quien puede tener su opinión. Y una opinión no necesariamente invalida a otras, siempre que quede bien establecido y explicado el motivo por el cual se formó esta o aquella opinión.
Para entender por qué llamo indicadores estáticos a aquellos que vimos anteriormente, necesitamos entender su propio código. Es verdad que en su momento no entré en muchos detalles, incluso porque ese tipo de implementación es bastante simple de entender. Pero, para entender lo que veremos dentro de poco, primero necesitamos entender aquellos códigos anteriores.
Entonces, vamos a usar uno de esos códigos como ejemplo. Justo abajo, tú puedes ver uno que usaremos para iniciar la explicación sobre ciertas cosas.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_COLOR_CANDLES 05. #property indicator_color1 clrRed, clrRoyalBlue, clrGreen 06. //+----------------+ 07. #property indicator_buffers 5 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_Buff_High[], 11. gl_Buff_Open[], 12. gl_Buff_Close[], 13. gl_Buff_Low[], 14. gl_Buff_Color[]; 15. //+------------------------------------------------------------------+ 16. int OnInit() 17. { 18. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 19. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 20. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 21. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 22. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 23. 24. return INIT_SUCCEEDED; 25. }; 26. //+------------------------------------------------------------------+ 27. 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[]) 28. { 29. static double high = DBL_MIN, 30. low = DBL_MAX; 31. 32. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 33. { 34. gl_Buff_High[c] = High[c]; 35. gl_Buff_Open[c] = Open[c]; 36. gl_Buff_Close[c] = Close[c]; 37. gl_Buff_Low[c] = Low[c]; 38. 39. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 40. if ((c - 1) > 0) 41. { 42. high = (High[c - 1] > high ? High[c - 1] : high); 43. low = (Low[c - 1] < low ? Low[c - 1] : low); 44. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 45. if (gl_Buff_Color[c] != 1) 46. { 47. high = DBL_MIN; 48. low = DBL_MAX; 49. } 50. } 51. } 52. 53. return rates_total; 54. }; 55. //+------------------------------------------------------------------+
Código 01
Este código 01, cuando se ejecuta, transformará el gráfico en un indicador. En este caso, un indicador de inside bar, como tú puedes observar en la imagen de abajo.

Imagen 01
Bien, sabemos cómo funciona este código 01, al menos en su parte básica, precisamente porque, cuando fue presentado, se explicó cómo declarar las cosas. Sin embargo, este indicador, visto en el código 01, es, a mi entender, un indicador estático. Y el motivo está justamente en las declaraciones hechas en las líneas cuatro y cinco. Y, por favor, mi querido lector, no me interpretes mal. Digo que este indicador del código 01 es estático porque, una vez compilado, no podemos cambiar el propio indicador, ya que fue declarado usando directivas de compilación propias de MQL5, que son precisamente las líneas mencionadas.
Sin embargo, lo que realmente importa es si el indicador funciona o no, y si logra, o no, ejecutar la tarea para la cual fue diseñado. Como lo logra, y de forma bastante directa y sin demasiados rodeos, no hay problema en usar esta implementación estática. Aun así, podemos crear este mismo código, o mejor dicho, crear un indicador muy parecido, pero con propiedades dinámicas, al menos en lo que respecta a la forma de implementar y presentar las cosas.
¿Y cómo funcionaría un indicador dinámico? ¿Y cuál es la ventaja de implementar un indicador dinámico? Bien, vayamos por partes. Primero, un indicador dinámico no tendría, en su código, las líneas cuatro y cinco que se ven en el código 01. Esta es la primera y mayor diferencia. Y la ventaja sería, justamente, la ausencia de esas dos líneas. Puede que esto te parezca muy extraño, pero después tú entenderás mejor este tipo de cosa, conforme vayamos avanzando hacia modelos más elaborados.
Pero, precisamente por no tener esas líneas declaradas, podemos cambiar la forma en que el indicador presentaría la información sin necesidad de compilar el código para modificar esa presentación. Sin embargo, quizá la mayor ventaja esté en que no necesitamos crear un verdadero circuito lógico dentro del código del indicador para seleccionar cómo debería presentar la información en el gráfico.
Ahora presta atención, mi querido lector. Lo que haremos aquí no cambiará la forma en que el indicador del código 01 trabaja. Solo crearemos una forma de implementar las cosas mediante código. Es decir, parte de lo que se haría con directivas se hará mediante llamadas a la biblioteca estándar de MQL5. Entonces, lo primero será eliminar las líneas que convierten al indicador del código 01 en un indicador estático. Al quitar la parte estática, obtenemos lo que se ve en el código de abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_buffers 5 05. #property indicator_plots 1 06. //+----------------+ 07. double gl_Buff_High[], 08. gl_Buff_Open[], 09. gl_Buff_Close[], 10. gl_Buff_Low[], 11. gl_Buff_Color[]; 12. //+------------------------------------------------------------------+ 13. int OnInit() 14. { 15. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 16. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 17. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 18. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 19. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 20. 21. return INIT_SUCCEEDED; 22. }; 23. //+------------------------------------------------------------------+ 24. 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[]) 25. { 26. static double high = DBL_MIN, 27. low = DBL_MAX; 28. 29. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 30. { 31. gl_Buff_High[c] = High[c]; 32. gl_Buff_Open[c] = Open[c]; 33. gl_Buff_Close[c] = Close[c]; 34. gl_Buff_Low[c] = Low[c]; 35. 36. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 37. if ((c - 1) > 0) 38. { 39. high = (High[c - 1] > high ? High[c - 1] : high); 40. low = (Low[c - 1] < low ? Low[c - 1] : low); 41. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 42. if (gl_Buff_Color[c] != 1) 43. { 44. high = DBL_MIN; 45. low = DBL_MAX; 46. } 47. } 48. } 49. 50. return rates_total; 51. }; 52. //+------------------------------------------------------------------+
Código 02
Este código 02, aunque puede compilarse, NO FUNCIONA. Al menos, no hará nada en el gráfico en el que se coloque. Sin embargo, esto no es realmente un problema. Esto se debe a que eliminamos la parte que le indica a MetaTrader 5 cómo se usará, de hecho, el indicador. Así, todo lo que MetaTrader 5 sabe es que tenemos cuatro buffers de datos y un buffer de color, y que se hará solo un trazado. Pero MetaTrader 5 NO SABE CÓMO usar esos buffers, aunque estén ajustados para presentar alguna información. Por eso, no ocurre nada en el gráfico.
Bien, entonces, ¿cómo podemos resolver esta cuestión? ¿Cómo le decimos a MetaTrader 5 cómo usar los buffers que se declaran en este código 02? Bueno, mi querido lector, aquí está la parte interesante. Porque, en este momento, le diremos de forma dinámica a MetaTrader 5 cómo deben usarse los buffers. Y esto se consigue añadiendo algunas líneas nuevas al código, como tú puedes observar justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_buffers 5 05. #property indicator_plots 1 06. //+----------------+ 07. double gl_Buff_High[], 08. gl_Buff_Open[], 09. gl_Buff_Close[], 10. gl_Buff_Low[], 11. gl_Buff_Color[]; 12. //+------------------------------------------------------------------+ 13. int OnInit() 14. { 15. const color cor[] = {clrRed, clrRoyalBlue, clrGreen}; 16. 17. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 18. PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, cor.Size()); 19. for (uint c = 0; c < cor.Size(); c++) 20. PlotIndexSetInteger(c, PLOT_LINE_COLOR, c, cor[c]); 21. 22. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 23. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 24. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 25. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 26. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 27. 28. return INIT_SUCCEEDED; 29. }; 30. //+------------------------------------------------------------------+ . . .
Código 03
Como el resultado final es el mismo que se ve en la imagen 01, no la repetiré. Sin embargo, cuando tú uses este indicador dinámico, que vemos en este fragmento de código 03, las cosas funcionarán de una manera un poco distinta a la que tendrían al usar el indicador estático del código 01.
Pero, antes de hablar de eso, entendamos qué se hizo en este fragmento de código 03, donde solo se ve la parte que nos interesa. Recuerda que el código completo estará en el anexo. Observa lo siguiente, mi querido lector: en la línea 15, se define un array de colores constantes. La secuencia de colores es la misma que se usó en el código 01, en la línea cinco. Es importante que tú entiendas esto para comprender cómo el código funciona, de hecho. Ahora, cuando se ejecute la línea 17 en este código 03, tendremos el mismo tipo de efecto que se obtendría cuando el compilador viera la línea cuatro del código 01. Nota la diferencia sutil que existe aquí. A diferencia de lo que ocurría en el código 01, donde durante la compilación decíamos cómo serían las cosas, aquí no hacemos eso. Solo le decimos al compilador: crea el código y usa este valor. Pero esto tiene implicaciones fuertes, como se verá después.
Justo después, en la línea 18 del código 03, le decimos a MetaTrader 5 cuántos colores existen en el buffer de color. Detalle: el hecho de decir cuántos colores existen en esta línea 18 no nos dice qué valores tendrá cada color. Así que, si tú no los inicializas, MetaTrader 5 lo hará de alguna manera, y el patrón de colores puede quedar todo desordenado. Por eso usamos el bucle en la línea 19, para inicializar los colores mediante la llamada de la línea 20. Todo esto equivale a lo que sería la línea cinco del código 01.
Sin embargo, aun así, la equivalencia termina en este punto. Porque, cuando tú apliques este indicador del fragmento de código 03, te encontrarás con una situación, como mínimo, bastante inusitada. Esto puede observarse en la imagen de abajo.

Imagen 02
¡Epá!, espera un momento. ¿Dónde están los colores que se aplicarán en el indicador? Creo que, tanto tú estés bastante sorprendido con esto que se ve en la imagen 02, como el pobre usuario acabaría bastante confundido al ver esa imagen 02. Pero veamos qué pasa cuando aplicamos el indicador al gráfico. Esto puede observarse en la animación de abajo.

Animación 01
Qué cosa más siniestra. No lo entiendo. ¿Por qué, aunque no podamos ajustar los colores, como se observa en la imagen 02, aun así el indicador tuvo el mismo resultado que se ve en la imagen 01? Eso sí que es bastante extraño.
Bien, en el fondo, este comportamiento no es tan extraño. Esto se debe a que estamos definiendo las cosas de manera dinámica. Y, como MetaTrader 5 todavía no sabe cómo construir los colores, no puede usarlos, o mejor dicho, no puede presentarlos para que podamos ajustarlos, ya que aún no sabe cuántos colores ni cuáles colores se usarán. Esto, de hecho, es algo bastante interesante y que podremos explorar de distintas formas en el futuro. Por ahora, mantengámonos en lo básico de todo esto.
Teniendo en cuenta que el patrón de colores fue el mismo que vimos en la imagen 01, sabemos que el indicador funciona. Y también podemos mostrarle al usuario qué patrón definimos durante la fase de codificación. Pero, ¿acaso no podemos cambiar esto, permitiendo que el usuario pueda elegir los colores que desee? En cuanto a eso, mi querido lector, en realidad no hay ningún problema, ya que, una vez que el indicador esté en el gráfico, el usuario puede abrir sus propiedades para cambiarlos. En este caso, mira lo que ocurre en la animación 02.

Animación 02
Hum, intrigante. Ahora podemos ver qué colores se están definiendo para usarse en el indicador. Pero espera un momento. Tú dijiste que podemos cambiar los colores. Sin embargo, acabo de recordar algo. En la línea 15 del fragmento de código 03, estamos definiendo precisamente estos colores que se ven en la animación 02. Y, aun así, se definen como constantes. Ahora estoy confundido. Si se definen como constantes, ¿cómo podrá el usuario modificar los colores del patrón?
En cuanto a esto, mi querido lector, tenemos el beneficio de que MetaTrader 5 entiende lo que se está haciendo, ya que simplemente estamos cambiando algún argumento, o parámetro, del indicador. MetaTrader 5 no lo quitará por completo del gráfico. Lo mantendrá, por así decirlo, en el gráfico, cambiando solo lo que necesita alterarse. Esto puede verse en la animación de abajo.

Animación 03
Observa que, de hecho, funciona. Sin embargo, existe un pequeño problema con este tipo de enfoque. Cuando el usuario quite el indicador del gráfico, o mejor dicho, cuando le pida a MetaTrader 5 reconstruir el gráfico por cualquier motivo, esos valores declarados en la línea 15 volverán a usarse, haciendo que cualquier cambio de configuración hecho por el usuario se pierda y tenga que hacerse otra vez.
Un ejemplo de solicitud de reconstrucción del gráfico es el simple hecho de pedir cambiar el período del gráfico. Por más absurdo que parezca, para gran parte de los usuarios, el simple hecho de cambiar la configuración estando en un período, digamos, de 5 minutos, y, justo después, pedirle a MetaTrader 5 que use un período de 10 minutos, por ejemplo, hará que se vuelvan a usar las configuraciones definidas en el momento de creación del indicador, haciendo que las modificaciones se pierdan. Ya expliqué en otro artículo cómo solucionar o sortear este tipo de situación. Así que no lo repetiré aquí.
Bien, con esta demostración simple, creo que tú ya comprendiste la diferencia entre una implementación estática y una dinámica. Sin embargo, para quien quiera saber mejor hasta dónde podemos, o no, llegar, en la documentación de MQL5 existe una tabla que muestra este tipo de cosa. Mira en: Conexión entre las propiedades de indicadores y las funciones correspondientes, para saber qué directivas, o propiedades, pueden o no implementarse de forma dinámica.
Muy bien, mi amigo autor. Pero, entre nosotros, hasta el momento no he logrado entender dónde estaría la ventaja de usar este sistema dinámico. Aun así, no veo cómo esto podría serme útil de alguna forma. Bien, comprendo tu falta de entusiasmo con este tipo de implementación. En efecto, no parece gran cosa, sobre todo considerando algunas dificultades que termina por traernos muchas veces cuando usamos esta forma de implementar un indicador.
Sin embargo, puede ser que, en ciertos casos muy específicos y orientados a una implementación personal, tú incluso te intereses en usar este tipo de enfoque. Para mostrar un ejemplo de eso, veremos un caso bien específico de implementación. Esto se discutirá en el próximo tema.
Indicador Inside bar en diferentes tipos de gráfico
El indicador que vimos en el tema anterior está, básicamente, pensado para usarse en un gráfico de velas. Pero, como sabemos, MetaTrader 5 nos permite usar el gráfico de barras y el gráfico de línea, además, claro, del gráfico de velas. Si, por casualidad, el usuario cambia a otro tipo de presentación que no sea velas, obtendrá un comportamiento muy desagradable en el gráfico. Esto puede verse justo abajo.

Imagen 03
En esta imagen 03, tenemos el indicador de inside bar usado en un gráfico de barras. Nota que es completamente extraña la forma en que la información se traza, tal como se ve en la imagen siguiente.

Imagen 04
Aquí tenemos algo muy parecido al problema que se ve en la imagen 03. Sin embargo, en el caso de esta imagen 04, pedimos usar el gráfico de línea, y esto junto con el indicador de inside bar. Tal vez, para la mayoría, y en realidad, esta situación no tenga sentido, ya que, cuando usamos el gráfico de línea, buscamos obtener la información de solo, y únicamente, uno de los valores OHCL, y casi siempre lo que nos importa sería el valor de cierre. Pero ese no es el caso. La cuestión aquí es que, aunque pidamos ver el gráfico en otro formato, seguimos viendo velas presentadas. Y esto necesita corregirse.
Básicamente, hay dos maneras de hacer esto. Y digo dos porque existía una tercera, que implicaría crear un indicador para cada modo de gráfico. Pero eso haría que su uso fuera extremadamente desagradable y confuso para los usuarios. Así que vamos a centrarnos en las otras dos maneras. Una sería crear un circuito de ejecución, basando el código en lo que sería creado por el compilador. Y la otra sería crear una manipulación dinámica de la información que se va a trazar.
Para evitar molestias innecesarias, vamos a cambiar el código 03, visto en el tema anterior, a una versión en la que ya podamos definir los colores del indicador desde el inicio. Sin embargo, no vamos a definir el tipo de indicador que se trazará. Así, ese mismo fragmento de código 03 quedará como se muestra abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_color1 clrRed, clrRoyalBlue, clrGreen 05. //+----------------+ 06. #property indicator_buffers 5 07. #property indicator_plots 1 08. //+----------------+ 09. double gl_Buff_High[], 10. gl_Buff_Open[], 11. gl_Buff_Close[], 12. gl_Buff_Low[], 13. gl_Buff_Color[]; 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 18. 19. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 20. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 21. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 22. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 23. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 24. 25. return INIT_SUCCEEDED; 26. }; 27. //+------------------------------------------------------------------+ 28. 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[]) 29. { 30. static double high = DBL_MIN, 31. low = DBL_MAX; 32. 33. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 34. { 35. gl_Buff_High[c] = High[c]; 36. gl_Buff_Open[c] = Open[c]; 37. gl_Buff_Close[c] = Close[c]; 38. gl_Buff_Low[c] = Low[c]; 39. 40. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 41. if ((c - 1) > 0) 42. { 43. high = (High[c - 1] > high ? High[c - 1] : high); 44. low = (Low[c - 1] < low ? Low[c - 1] : low); 45. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 46. if (gl_Buff_Color[c] != 1) 47. { 48. high = DBL_MIN; 49. low = DBL_MAX; 50. } 51. } 52. } 53. 54. return rates_total; 55. }; 56. //+------------------------------------------------------------------+
Código 04
Ahora tenemos nuestro código 04 funcionando de una manera más adecuada a lo ideal para cualquier usuario del indicador, ya que podemos definir los colores desde el inicio. Y todo esto se debe a que, en la línea cuatro de este código 04, estamos definiendo la propiedad del indicador. Observa que, por este motivo, eliminamos aquella parte de carga de colores desde dentro del bloque de manejo del evento OnInit. Con eso, tenemos lo que sería el indicador base que necesitaremos manipular.
Ahora viene la parte divertida. Si este indicador que vemos en el código 04 funciona de manera dinámica y, al mismo tiempo, tiene definida la parte estática para que podamos informar el patrón de colores, ¿cómo hacemos para que funcione y se adapte al tipo de gráfico que se está visualizando?
Bien, esta es la parte en la que muchos, al mirar mis códigos, suelen quedarse con los pelos de punta. Observa que, en la línea 17, estamos diciendo que el sistema de trazado del indicador será del tipo velas. Sin embargo, al cambiar el gráfico al tipo barras, tendremos problemas, como se vio en las imágenes anteriores. Pero, si tú estudias la documentación, encontrarás una función de la biblioteca estándar bastante interesante. Esta nos permite saber qué tipo de gráfico se está usando en ese momento. La función en cuestión es ChartGetInteger. Cuando le pedimos a esta función que nos informe qué tipo de gráfico se está usando, nos devolverá una enumeración, en este caso ENUM_CHART_MODE.
Y eso es justamente lo que necesitamos saber. Pero, antes de ponerlo en práctica, veamos cómo se comportaría el indicador, en un primer momento. Para eso, cambiamos el código a lo que se ve justo abajo.
. . . 14. //+------------------------------------------------------------------+ 15. #define PrintX(X) Print(__FUNCTION__, " => ",#X, " :: ",X); 16. //+------------------------------------------------------------------+ 17. int OnInit() 18. { 19. PrintX(EnumToString((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE))); 20. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 21. 22. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 23. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 24. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 25. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 26. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 27. 28. return INIT_SUCCEEDED; 29. }; 30. //+------------------------------------------------------------------+ . . .
Código 05
Ahora presta atención a lo que voy a explicar, porque, si no, después tú te quedarás perdido en la explicación de lo que se hará. En este fragmento que vemos en el código 05, están los cambios que hubo que hacer en el código 04 para poder saber qué tipo de gráfico se está usando en un momento dado. Entonces, en la línea 19, hacemos una llamada que, al final, se traducirá, por la definición que se ve en la línea 15, en una información que se mostrará en el terminal de MetaTrader 5. Esta información puede verse en las imágenes de abajo.

Imagen 05
En esta imagen 05, podemos ver que estamos usando el gráfico CHART_CANDLES.

Imagen 06
En esta imagen 06, claramente notamos que el gráfico es CHART_BARS.

Imagen 07
Y, por fin, en esta imagen 07, podemos ver que el gráfico es CHART_LINE. Bien, pero ¿cómo usar esto a nuestro favor? Bueno, mi querido lector, es muy simple, con excepción del gráfico CHART_LINE, que veremos después cómo resolver. Para que el indicador use el modo de trazado correcto, solo necesitamos hacer que la llamada de la línea 20, vista en este fragmento de código 05, apunte al tipo DRAW_COLOR_CANDLES cuando estemos en un gráfico de velas, y a DRAW_COLOR_BARS cuando estemos en un gráfico de barras.
El resto es historia. Esto puede hacerse de manera muy simple por cualquiera, y funciona perfectamente bien, siempre y cuando MetaTrader 5 esté aplicando el indicador a partir de un evento Init. Es decir, si tú deseas usarlo de la manera más simple, el código debe quedar como se muestra abajo.
. . . 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)) 18. { 19. case CHART_BARS: 20. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS); 21. break; 22. case CHART_CANDLES: 23. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 24. break; 25. case CHART_LINE: 26. break; 27. } 28. 29. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 30. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 31. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 32. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 33. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 34. 35. return INIT_SUCCEEDED; 36. }; 37. //+------------------------------------------------------------------+ . . .
Código 06
Nota que, en este fragmento del código 06, no estamos tratando el modo de trazado del gráfico de línea. Esto se verá después. Pero observa cómo estamos trazando los demás modos. Y esto que tú estás viendo en este código 06 funciona, pero tiene un pequeño problema. En la animación de abajo, podemos ver cuál es ese problema.

Animación 04
En esta animación 04, podemos ver que, cuando cambiamos el gráfico a otro modo de trazado, el indicador no logra acompañar lo que MetaTrader 5 está haciendo. Muchos se rendirían, diciendo que no es posible crear el indicador de la forma adecuada. Sin embargo, esas personas no saben cómo funciona MetaTrader 5 en realidad. Siempre que ocurre algo, normalmente en un gráfico, MetaTrader 5 dispara un evento muy específico, con alguna información para ayudarnos a identificar qué pasó. Algunos eventos pueden activarse o desactivarse, mientras que otros no. Y cada evento que activamos o desactivamos influye en el rendimiento general de MetaTrader 5, ya que termina ocupado y tiene que atender eventos que normalmente no observaría.
Y esto, para quien quiere un sistema de alto rendimiento, es trivial. Muchos operadores, cuando van a usar MetaTrader 5, no usan absolutamente nada junto a él, como indicadores o Asesores Expertos. Esto se debe a que, muchas veces, estas aplicaciones terminan perjudicando la velocidad de MetaTrader 5 durante momentos de alta volatilidad. Y, en esos momentos, estos operadores quieren aprovechar; entonces, usan MetaTrader 5 básico, y solo eso, nada más.
Bien, pero aquí no vamos a enfocarnos en operar con MetaTrader 5 en su formato más básico. Queremos, sí, añadir aplicaciones al gráfico para que nos ayuden de alguna manera. Por lo tanto, necesitamos capturar cierto evento que MetaTrader 5 dispara cada vez que se hace algo en el gráfico, sin importar la naturaleza del evento.
Este evento que queremos capturar es el ChartEvent. En nuestro código, se maneja mediante el procedimiento OnChartEvent. Ahora, presta mucha atención, mi querido lector. Un ChartEvent ocurre SIEMPRE que sucede algo en el gráfico. Sin embargo, podemos activar y desactivar ciertas cosas para no capturar cada mínimo instante, o cada cambio, que se haga en el gráfico. Aun así, cuando tú añades un indicador al gráfico, MetaTrader 5 dispara dos eventos, uno tras otro. En algunos casos dispara un tercero, pero vamos a enfocarnos solo en los dos primeros.
El primer evento que se dispara es un Init, que se captura y se maneja con la función OnInit, que venimos viendo desde el inicio de este tema sobre indicadores. Justo después de que el evento Init se haya tratado, se dispara un nuevo evento, sin que hayamos hecho nada todavía. Ese sería un evento ChartEvent, y es cuando el indicador se coloca de hecho en el gráfico.
Bien, pero, para entender lo que ocurre a continuación, necesitamos mirar la declaración del procedimiento de captura de este evento ChartEvent. Esta declaración puede verse justo abajo.
void OnChartEvent(const int id, // Event ID const long& lparam, // Parameter of type long event const double& dparam, // Parameter of type double event const string& sparam // Parameter of type string events );
Código 07
Ahora, si tú miras la documentación OnChartEvent, verás una pequeña tabla. El valor que nos interesa en esa tabla es: CHARTEVENT_CHART_CHANGE, que ocurre cuando hay un cambio en el gráfico. Ese es, entonces, el valor que llega en el primer disparo del ChartEvent. Ahora, sabiendo esto, podemos hacer algo bastante interesante en nuestro código. Así, una vez actualizado, queda como se muestra abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_color1 clrRed, clrRoyalBlue, clrGreen 05. //+----------------+ 06. #property indicator_buffers 5 07. #property indicator_plots 1 08. //+----------------+ 09. double gl_Buff_High[], 10. gl_Buff_Open[], 11. gl_Buff_Close[], 12. gl_Buff_Low[], 13. gl_Buff_Color[]; 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 18. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 19. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 20. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 21. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 22. 23. return INIT_SUCCEEDED; 24. }; 25. //+------------------------------------------------------------------+ 26. 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[]) 27. { 28. static double high = DBL_MIN, 29. low = DBL_MAX; 30. 31. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 32. { 33. gl_Buff_High[c] = High[c]; 34. gl_Buff_Open[c] = Open[c]; 35. gl_Buff_Close[c] = Close[c]; 36. gl_Buff_Low[c] = Low[c]; 37. 38. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 39. if ((c - 1) > 0) 40. { 41. high = (High[c - 1] > high ? High[c - 1] : high); 42. low = (Low[c - 1] < low ? Low[c - 1] : low); 43. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 44. if (gl_Buff_Color[c] != 1) 45. { 46. high = DBL_MIN; 47. low = DBL_MAX; 48. } 49. } 50. } 51. 52. return rates_total; 53. }; 54. //+------------------------------------------------------------------+ 55. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 56. { 57. switch (id) 58. { 59. case CHARTEVENT_CHART_CHANGE: 60. switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)) 61. { 62. case CHART_BARS: 63. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS); 64. break; 65. case CHART_CANDLES: 66. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 67. break; 68. case CHART_LINE: 69. break; 70. } 71. break; 72. } 73. ChartRedraw(); 74. }; 75. //+------------------------------------------------------------------+
Código 08
Hum. No sé. Creo que este código no va a funcionar. Esto se debe a que no le estamos diciendo a MetaTrader 5 qué sistema de trazado se usará cuando se ejecute la función OnInit. No sé. Estoy casi seguro de que el resultado será igual al del código 02.
Bien, tenemos un incrédulo entre nosotros. Entonces vamos a mostrarle que el mundo es mucho más grande de lo que él imagina conocer. Para probar que este código 08, de hecho, funciona, mira lo que muestra la animación de abajo.

Animación 05
Nota que lo único que no sigue lo que MetaTrader 5 está haciendo en el gráfico es cuando cambiamos al modo CHART_LINE. Allí, nuestro indicador, al no implementar el modelo de trazado, no logra acompañar lo que el usuario está haciendo en MetaTrader 5.
Consideraciones finales
En este artículo, se demostró un principio básico, pero bastante interesante, para trabajar con indicadores. Aunque aquí no hayamos mostrado cómo resolver la cuestión de usar un indicador para trazar y acompañar todo lo que MetaTrader 5 solicita, logramos que un indicador simple obedeciera un comando para seguir el modo de trazado del gráfico. Esto, en parte, ya que quedó pendiente mostrar cómo hacer que el indicador muestre el trazado de línea, siguiendo lo que el usuario seleccione en la plataforma.
Sin embargo, como el cambio del trazado de barras o velas, que necesitan cuatro buffers de datos y uno de color, a uno de línea coloreada, que necesita un buffer de datos y uno de color, es algo un poco complicado de mostrar en este momento, lo dejaremos para verlo más adelante. Ya que, para hacerlo, necesitamos usar un enfoque un poco diferente, cargando y descargando un indicador mediante llamadas de la biblioteca estándar. Algo que, justo ahora, sería un poco complicado de explicar, o incluso de ser comprendido correctamente por ti, mi querido y estimado lector.
De cualquier modo, conforme vayamos avanzando hacia materiales aún más complejos y elaborados, en algún momento mostraré cómo solucionar esta cuestión que se abordó aquí. Hasta entonces, te sugiero que estudies lo que se mostró en este artículo y, sobre todo, no te olvides de repasar lo que se vio en los artículos anteriores. Porque, en el próximo artículo, vamos a unir algunos conceptos a este tema de indicadores, algo que realmente será muy interesante de ver. Así que no te lo pierdas.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15932
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Visión por computadora para el trading (Parte 1): Creamos una funcionalidad básica sencilla
Creación de interfaces gráficas dinámicas MQL5 mediante el escalado de imágenes basado en recursos con interpolación bicúbica en gráficos de trading
Particularidades del trabajo con números del tipo double en MQL4
Simulación de mercado (Parte 20): Iniciando el SQL (III)
- 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