Del báscio al intermedio: Subventanas (IV)
Introducción
En el artículo anterior, Del báscio al intermedio: Subventanas (III), nos dedicamos a explicar cómo podíamos manejar determinadas situaciones relacionadas con las subventanas. El principio básico de aquel artículo está relacionado con las propiedades que podemos asignar a un determinado indicador. Estas propiedades nos permiten indicar al compilador cómo debe o no crearse una determinada subventana.
Como se explicó en el artículo anterior, hay casos en los que NO QUEREMOS que nuestro indicador tenga una segunda instancia. Este tipo de situación se explorará con más detalle en otro artículo más adelante, cuando empecemos a utilizar ciertas propiedades para tener un mayor control sobre lo que MetaTrader 5 debe hacer realmente.
Por lo tanto, todavía nos quedan muchas cosas por ver en relación con lo que considero básico sobre MQL5. Aunque MetaTrader 5 está pensado principalmente como una plataforma de trading, podemos manipular muchas cosas para conseguir que tenga un propósito final muy diferente.
Recuerda que podemos forzar a MetaTrader 5 a hacer cualquier cosa. En muchos casos tendremos que recurrir a elementos que van más allá de lo que MQL5 ofrece de forma predeterminada. En esos casos tendremos que pasar a C o C++ y utilizar algún tipo de enlace entre el código MQL5 y C o C++. Al hacerlo, las puertas del paraíso, o quizá del infierno, se abrirán ante ti.
Esta serie de artículos no entrará en ese terreno, ya que considero que utilizar otros lenguajes, como C y C++, para ampliar todavía más el control que MQL5 tiene sobre MetaTrader 5 es un tema realmente avanzado. Y eso no se abordará aquí.
Subventanas (IV)
En el artículo anterior vimos algo que nos permitiría crear el código que se muestra a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_separate_window 07. #property indicator_height 50 08. #property indicator_plots 0 09. //+------------------------------------------------------------------+ 10. int OnInit(void) 11. { 12. IndicatorSetString(INDICATOR_SHORTNAME, "Test 123."); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Código 01
Y, aunque todos aquellos códigos vistos en el artículo anterior son funcionales, es decir, pueden ejecutarse dentro de MetaTrader 5, durante la compilación alguno de ellos puede hacer que el compilador emita una advertencia. Esto puede solucionarse añadiendo lo que aparece en la línea ocho de este código 01. Como este indicador que se muestra arriba es lo que considero un indicador vacío, resulta ideal para demostrar y estudiar cada una de las propiedades de los indicadores. Cuando digo que un indicador está vacío, me refiero a que no hace absolutamente nada o no tiene ninguna funcionalidad práctica cuando se coloca en el gráfico. Como no vamos a trazar ninguna línea ni curva en el gráfico al utilizar este código 01, indicamos que el número de plots es cero para evitar que el compilador emita alguna advertencia.
Muy bien, como vimos en el artículo anterior, la línea seis de este código 01 informará a MetaTrader 5 de que, al intentar utilizar este indicador en el gráfico, deberá colocarse en una subventana. Esta podrá crearse o no, como se mostró en los artículos anteriores. La línea siete indicará la dimensión de esta subventana en caso de que sea necesario crearla. En cuanto a estas propiedades del indicador, creo que ninguno de ustedes debería tener dudas, ya que todo esto es bastante sencillo, claro y directo.
Lo que necesito que entiendas es que este código 01 sirve de base para otro enfoque que podemos utilizar. Aunque al principio pueda parecer algo confuso, nos permite tener mucho más control sobre lo que sucede en determinadas situaciones que, de otro modo, serían mucho más difíciles de manejar.
Para que entiendas el tipo de problema que podemos resolver al comprender en profundidad lo que hace este código 01, es necesario que hayas entendido lo mostrado en los últimos artículos de esta serie. Allí se demostró que MetaTrader 5 puede evitar que un indicador tenga más de una instancia en un mismo gráfico, siempre que sus propiedades no se hayan modificado. Esto evita un desperdicio innecesario de recursos. Lo mismo no se aplica cuando empezamos a utilizar la propiedad que indica la necesidad de una subventana. En este caso, MetaTrader 5 ignora que ya existe otra instancia abierta y crea nuevas instancias a medida que el usuario u operador vuelve a añadir el mismo indicador al gráfico. Todo esto se explicó con detalle en los artículos anteriores. No obstante, existe una forma de evitarlo e impedir, al menos de forma general, que una nueva instancia de un determinado indicador que hayamos implementado se coloque en el gráfico cuando trabajamos con subventanas.
Para entender cómo podemos hacerlo, tenemos que recurrir a la documentación de MQL5, ya que allí se explica cómo hacerlo. Si observas con atención, encontrarás la función ChartIndicatorAdd. Esta función es muy interesante, aunque al consultar únicamente la documentación pueda parecer una función como cualquier otra. El hecho de que quizá no imagines usos distintos de los previstos para esta función limita enormemente lo que podrás hacer y, muchas veces, incluso dificulta entender por qué el código presenta un determinado comportamiento. Antes de analizar esto, quiero que prestes atención a una observación incluida en la propia documentación. Para facilitar la comprensión de la explicación, a continuación podemos ver íntegramente una parte importante de lo que dice la documentación sobre esta función que utilizaremos dentro de poco.
Observación
Si un indicador debe trazarse en una subventana separada (por ejemplo, al crear un iMACD o un indicador personalizado con la propiedad especificada #property indicator_separate_window) y se aplica a la ventana principal del gráfico, es posible que no sea visible, aunque seguirá apareciendo en la lista de indicadores. Esto significa que la escala del indicador es diferente de la escala del gráfico de precios y que el valor del indicador aplicado no se encuentra dentro del rango visible del gráfico de precios. En este caso, GetLastError() devuelve el código cero, lo que indica que no se ha producido ningún error. Los valores de este indicador “invisible” pueden verse en la Ventana de Datos (Data Window) y obtenerse desde otras aplicaciones MQL5.
En resumen, lo que dice la documentación es que podemos cargar en la ventana principal un indicador diseñado para utilizar una subventana. Este no será visible ni creará una subventana para mostrarlo si esto no se indica correctamente al llamar a la función ChartIndicatorAdd.
No entender de inmediato lo que explica la documentación es completamente normal. Muchos buenos programadores tienen esta dificultad al principio. Esto ocurre porque la documentación pretende explicar de forma simplificada qué comportamiento cabe esperar, no algo que quizá quieras hacer pero que no se tuvo en cuenta cuando se creó el sistema. Y esto se aplica a cualquier lenguaje de programación. Para quien está empezando, mucha de la información presente en la documentación puede parecer bastante confusa. Con experiencia, aprenderás a extraer de ella información valiosa para crear soluciones fuera de lo común. Precisamente porque conozco esta dificultad y sé lo complicado que puede resultar para muchos utilizar la documentación, de vez en cuando relaciono la explicación del artículo con lo que puede verse en ella. Así, con el tiempo, podrás entender cualquier explicación que aparezca allí y valerte por ti mismo. Por este motivo, podemos profundizar un poco más en la explicación de lo que se ha dicho para que realmente entiendas lo que expresa la documentación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456S."); 12. 13. return INIT_SUCCEEDED; 14. }; 15. //+------------------------------------------------------------------+ 16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 17. { 18. return rates_total; 19. }; 20. //+------------------------------------------------------------------+
Código 02
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 12. ChartIndicatorAdd(0, 0, iCustom(NULL, NULL, "Tutor\\Code 01")); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Código 03
Bien, presta ahora atención a algunos detalles de este código 03. Observa que, en la línea doce, utilizamos la función ChartIndicatorAdd. Aquí no realizamos ninguna comprobación de errores en tiempo de ejecución, el famoso RUN-TIME. Precisamente por esta razón debes tener cuidado al intentar ejecutar localmente este código 03. Primero, asegúrate de que el código 01 se haya compilado correctamente. Así, cuando se ejecute la línea doce de este código 03, MetaTrader 5 tendrá acceso al ejecutable del indicador 01. Segundo, puedes observar que dentro de la función ChartIndicatorAdd utilizo una llamada a iCustom. Esta llamada tiene como objetivo crear una referencia a un indicador personalizado. Es decir, nos permite ampliar, o mejor dicho, utilizar indicadores distintos de los que MetaTrader 5 ya ofrece dentro del lenguaje MQL5.
En el artículo Del nivel báscio al intermedio: Indicador (V), empezamos a hablar de estos indicadores, es decir, de los llamados indicadores técnicos. Aquí estamos ampliando ese mismo tema. Muy bien, en este punto es necesario comprobar que tanto el directorio como el nombre del indicador que se cargará mediante iCustom sean correctos. En el archivo adjunto, la estructura de directorios que debe utilizarse ya estará configurada correctamente. Por lo tanto, para probar este código 03, no cambies la estructura a menos que entiendas realmente lo que estás haciendo. De lo contrario, el resultado final será diferente del que se mostrará aquí.

Animación 01
¿Qué ha sido esto? No lo entiendo. ¿Acaso el indicador del código 03 no añadió el indicador 01 al gráfico? Vamos a comprobar qué ocurrió. Para ello, bastará con abrir la lista de indicadores presentes en el gráfico y comprobar cuáles aparecen allí. Esto puede verse en la siguiente imagen.

Imagen 01
Qué extraño. El indicador 03 sí consiguió cargar el indicador 01 en el gráfico. El indicador 01 no creó la subventana como se esperaba. O mejor dicho, yo imaginaba que, al ejecutarse el indicador 01, debería crear una subventana. Hum, espera un momento. ¿Realmente se está ejecutando este indicador 01? Porque, desde mi punto de vista, lo que acaba de ocurrir no tiene ningún sentido. La verdad es que estoy un poco confundido.
Por eso es importante que intentes utilizar todos los conocimientos expuestos en estos artículos, amigo lector, y que hagas siempre tus propias pruebas locales para comprender los detalles. Cualquier duda que no resuelvas en esta etapa del aprendizaje acabará pasándote factura más adelante. En cualquier caso, al analizar lo que está ocurriendo aquí, observarás que el indicador 01, además de cargarse, también se está ejecutando. Y esta es la parte importante: no se muestra en el gráfico. Esto ocurre porque, en el código 03, no estamos indicando que deba mostrarse. Y eso es precisamente lo que explica el texto de la documentación.

Imagen 02
La región resaltada en la imagen 02 es precisamente la parte que nos interesa. Observa lo siguiente: aquí se indica que, cuando añadimos un indicador mediante la función ChartIndicatorAdd, debemos especificar la subventana en la que debe añadirse. Presta mucha atención a esta información, mi querido lector, porque, dependiendo de lo que estés planeando hacer mediante código, automatizando así alguna estrategia, entender de forma clara y objetiva lo que se destaca en esta imagen 02 puede marcar la diferencia entre que tu código funcione o no de la manera prevista.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 12. ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01")); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Código 04
Ahora, máxima atención a lo que se explicará, porque será sumamente importante para entender lo que haremos a continuación. Observa que la única diferencia entre el código 03 y este código 04 está precisamente en el segundo argumento de la función ChartIndicatorAdd, que aparece en la línea doce de ambos códigos. Y esta es la parte importante: esta simple diferencia es más que suficiente para hacer que el ejecutable del indicador se comporte de una forma completamente distinta a la vista en la animación 01.

Animación 02
Observa qué curioso. A diferencia de lo que ocurría antes, ahora sí se crea una nueva subventana, tal como esperábamos. Y, al consultar la lista de indicadores, podremos ver el siguiente resultado.

Imagen 03
Perfecto. La pregunta es: ¿por qué ahora funciona y antes no? La respuesta es que, si observas el código 03, verás que el segundo argumento pasado a la función ChartIndicatorAdd indica a MetaTrader 5 que añada el indicador a la ventana principal. Como el indicador del código 01 ESPERA Y NECESITA una subventana, no se muestra en pantalla. Aunque estuviera trazando información importante, NO SE TRAZARÍA y permanecería invisible para el usuario u operador que estuviera observando el gráfico.
En cambio, en el código 04, en el mismo argumento que antes apuntaba a la ventana principal, ahora pedimos a MetaTrader 5 que compruebe y nos indique la cantidad de subventanas presentes en el gráfico y que añada el indicador especificado en el tercer argumento a la siguiente subventana. Por esta razón, este código 04 consigue hacer que el código 01 cree una nueva subventana. Esto resulta muy útil y nos permite hacer muchas cosas interesantes que veremos y exploraremos en otro momento.
Antes de terminar este artículo, quiero explicarte una última cosa sobre la función ChartIndicatorAdd. Considéralo un pequeño extra. Si has prestado atención a lo que hemos hecho hasta aquí, seguramente habrás notado algo. En todos los casos podemos obtener resultados bastante interesantes de una forma muy sencilla y con muy poco esfuerzo. En un caso indicamos que el indicador que se iba a añadir debía colocarse en la ventana principal y, en el otro, que debía crearse una nueva subventana. Pregunta: ¿qué ocurre si ya tenemos una subventana en el gráfico y hacemos que el indicador se cargue y se coloque dentro de ella? Espera un momento. ¿Adónde quieres llegar con esta hipótesis?
Bien, la idea que quiero que entiendas ya se vio en otro artículo de esta misma serie. Se trata de Del nivel báscio al intermedio: Subventanas (II). En aquel artículo se demostró que podíamos hacer que un indicador utilizara valores calculados por otro indicador para crear un tercer indicador. La idea en sí es bastante sencilla e interesante. Hacerlo automáticamente nada más añadir el indicador al gráfico requiere que sepas exactamente qué estás implementando.

Animación 03
Hecho esto, vamos ahora a crear un nuevo indicador y enseguida entenderás el motivo. Su código se muestra a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++) 12. for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--) 13. PrintFormat("In the sub window [%d] was found this indicator >>%s<<", c0, ChartIndicatorName(0, c0, c1)); 14. 15. return INIT_SUCCEEDED; 16. }; 17. //+------------------------------------------------------------------+ 18. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 19. { 20. return rates_total; 21. }; 22. //+------------------------------------------------------------------+
Código 05
El objetivo de este código 05 es simplemente indicarnos el nombre de los indicadores presentes en el gráfico. Esto ya se explicó en los artículos anteriores; aquí iremos un paso más allá. Bien, al ejecutar este código 05 obtendremos como respuesta el resultado mostrado en la siguiente imagen.

Imagen 04
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. int index = -1; 12. 13. for (int c0 = 0; (index < 0) && (c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL))); c0++) 14. index = (ChartIndicatorName(0, c0, 0) == "StdDev(20)" ? c0 : index); 15. 16. IndicatorSetString(INDICATOR_SHORTNAME, "Demo 123."); 17. if (index >= 0) 18. ChartIndicatorAdd(0, index, iCustom(NULL, NULL, "Examples\\Custom Moving Average", 20, 0, 0, PRICE_CLOSE)); 19. 20. return (index < 0 ? INIT_FAILED : INIT_SUCCEEDED); 21. }; 22. //+------------------------------------------------------------------+ 23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 24. { 25. return rates_total; 26. }; 27. //+------------------------------------------------------------------+
Código 06
Bien, antes de ver el resultado de la ejecución de este código 06, tengo que explicar una cuestión sobre él. Observa en la línea dieciocho qué indicador estamos utilizando. En este caso utilizo un indicador incluido en el propio MQL5. Esto simplifica bastante las cosas y reduce considerablemente la cantidad de aspectos que habría que explicar aquí. Sin embargo, esto no resuelve por completo el escenario ni hace que el código sea 100 % funcional. El motivo es sencillo. ¿Ven el último argumento de la llamada iCustom? Pues bien, este argumento, PRICE_CLOSE, hará que el indicador que se añada calcule los datos a partir de la información de cotización del gráfico principal. Para utilizar los valores calculados por el indicador añadido en la animación 03, necesitamos que este argumento sea distinto. No basta con cambiarlo; tenemos que seguir otro camino. Ese camino se verá en otro momento, ya que para entender el motivo primero hay que explicar otra cuestión que todavía no se ha mencionado adecuadamente en esta serie de artículos. Así que ten paciencia.

Imagen 05
Observa que, al añadir este código 06 al gráfico, necesitaríamos que el indicador cargado por la línea dieciocho tuviera el mismo trazado que la línea negra de la imagen 05. Precisamente por la cuestión mencionada anteriormente, será diferente y requerirá un ajuste por parte del usuario u operador. Observa en las siguientes animaciones cómo se haría.

Animación 04
Bien, efectivamente, el código 06 consiguió añadir el indicador deseado. Sin embargo, los valores de cálculo son incorrectos, ya que está utilizando los valores procedentes de la cotización del símbolo. Por lo tanto, tendremos que realizar un pequeño ajuste.

Animación 05
Consideraciones finales
En este artículo se mostró cómo trabajar de una forma algo más avanzada con todo lo relacionado con el uso de subventanas. Aunque lo visto aquí es solo la parte que considero básica para un desarrollo mucho más profundo, entenderlo te ayudará a utilizar de forma más adecuada los recursos disponibles en MetaTrader 5. Para conseguirlo, necesitamos utilizar MQL5 para tener un mayor control sobre lo que se mostrará.
| Archivo MQ5 | Descripción |
|---|---|
| Indicators\Code 01 | Demostración básica |
| Indicators\Code 02 | Demostración básica |
| Indicators\Code 03 | Demostración básica |
| Indicators\Code 04 | Demostración básica |
| Indicators\Code 05 | Demostración básica |
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/17152
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.
Red neuronal en la práctica: Funciones de activación
Red neuronal en la práctica: Gráfico de la Rectifier
Del básico al intermedio: Objetos y subventanas (I)
Herramientas de trading de MQL5 (Parte 8): Panel informativo mejorado con funciones de arrastre y minimizado
- 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