Português
preview
Del básico al intermedio: Objetos y subventanas (I)

Del básico al intermedio: Objetos y subventanas (I)

MetaTrader 5 — Ejemplos |
14 0
CODE X
CODE X

Introducción

En el artículo anterior Del nivel básico al intermedio: Recursos, introdujimos un concepto bastante interesante y muy cautivador: utilizar datos o algún tipo de información como recurso interno de una aplicación que estemos implementando. Sin embargo, lo que vimos allí es apenas una pequeña e insignificante parte de todo lo que podemos hacer con este concepto. En la práctica, la única limitación es tu propia imaginación y experiencia al utilizar el concepto explicado allí. Y, como aquí el objetivo es didáctico y buscamos mostrar las cosas de una forma que todos puedan entender, incluso un principiante, no vamos a saltarnos etapas al explicar cómo pueden y deben entenderse determinados conceptos.

Muy bien, a lo largo de esta serie ya hemos hablado de objetos gráficos y de cómo manejar eventos simples en estos mismos objetos. También hicimos una breve introducción a los indicadores y vimos cómo abordar algunas situaciones específicas relacionadas con su uso. Además, ya hemos establecido una buena base sobre el uso de subventanas. Sin embargo, todavía no hemos hablado de cómo utilizar objetos dentro de ellas. Muchos pueden pensar: ¿Qué importancia tiene saber hacer esto? ¿No sería lo mismo que ya se explicó al hablar de objetos gráficos? A grandes rasgos, sí, sería más o menos lo mismo. Sin embargo, y este es precisamente el punto que voy a explicar, existen algunas pequeñas diferencias y matices entre utilizar un objeto en la ventana principal y utilizar ese mismo objeto en una subventana. Lo más interesante comienza cuando empezamos a utilizar elementos que, en principio, deberían estar en la ventana principal, pero que pasarán a utilizarse en alguna subventana. Dicho así, quizá, o mejor dicho, con toda seguridad, todavía no entiendes lo que intento explicar.

Entonces, vamos a empezar a explorar precisamente este tipo de situaciones: el uso de objetos gráficos dentro de subventanas. Dejemos a un lado las distracciones y centrémonos en lo que se explicará en este artículo, porque este tema puede marcar una diferencia en la forma en que concibes el código.


Objetos y subventanas (I)

Aunque todavía no se han explicado algunos conceptos y estructuras de programación, no quiero que pienses que para crear algo útil hacen falta años de experiencia programando. Eso no sería cierto en absoluto. La programación es una forma de arte en la que, a partir de una idea, puedes utilizar medios matemáticos para indicarle al ordenador qué debe hacer para convertirla en realidad. Y no te engañes: los ordenadores no son más que simples máquinas de calcular. Que no los veas de esa manera no los convierte en algo diferente. Por eso, aunque en este primer momento tengamos algunas limitaciones, quiero mostrarte que ya podemos hacer muchas cosas bastante interesantes, aunque al principio puedan parecer muy simples y poco atractivas.

Como de costumbre, vamos a empezar con algo muy simple y fácil de entender. Para ello, implementaremos un pequeño indicador que 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. #define def_ObjName "Mini Chart"
10. //+------------------------------------------------------------------+
11. int OnInit(void)
12. {
13.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
14. 
15.     ObjectCreate(0, def_ObjName, OBJ_CHART, 0, 0, 0);
16.     ObjectSetInteger(0, def_ObjName, OBJPROP_XDISTANCE, 10);
17.     ObjectSetInteger(0, def_ObjName, OBJPROP_YDISTANCE, 10);
18.     ObjectSetInteger(0, def_ObjName, OBJPROP_XSIZE, 300);
19.     ObjectSetInteger(0, def_ObjName, OBJPROP_YSIZE, 300);
20.     ObjectSetInteger(0, def_ObjName, OBJPROP_PERIOD, PERIOD_M15);
21.     ObjectSetInteger(0, def_ObjName, OBJPROP_DATE_SCALE, false);
22.     ObjectSetInteger(0, def_ObjName, OBJPROP_PRICE_SCALE, false);
23.     ObjectSetString(0, def_ObjName, OBJPROP_SYMBOL, _Symbol);
24.     ObjectSetInteger(0, def_ObjName, OBJPROP_SELECTABLE, true); 
25.     ObjectSetInteger(0, def_ObjName, OBJPROP_SELECTED, false);
26.     
27.     return INIT_SUCCEEDED;
28. };
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31. {
32.     return rates_total;
33. };
34. //+------------------------------------------------------------------+
35. void OnDeinit(const int reason)
36. {
37.     ObjectDelete(0, def_ObjName);
38.     ChartRedraw();
39. };
40. //+------------------------------------------------------------------+

Código 01

Este código muestra de forma muy directa lo que queremos hacer. Sin embargo, no es la mejor manera de hacerlo, aunque es algo que todos pueden, o deberían poder, entender. Como ya se explicaron en esta serie de artículos algunos aspectos relacionados con la programación orientada a objetos, podemos mejorar este mismo código 01 para obtener el mismo resultado que produciría, pero con un código mucho más sencillo de utilizar. Así, aunque olvidemos algún detalle, la propia programación orientada a objetos nos ayudará a resolverlo. Por lo tanto, este mismo código 01 puede reescribirse como 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_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. class C_MiniChart
10. {
11.     private :
12. //+----------------+
13.         string m_szNameObject;
14. //+----------------+
15.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
16. //+----------------+
17.     public  :
18. //+----------------+
19.         C_MiniChart(string szName)
20.             :m_szNameObject(szName)
21.         {
22.             ObjectCreate(0, m_szNameObject, OBJ_CHART, 0, 0, 0);
23.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
24.             SetInteger(OBJPROP_XDISTANCE, 10);
25.             SetInteger(OBJPROP_YDISTANCE, 10);
26.             SetInteger(OBJPROP_XSIZE, 300);
27.             SetInteger(OBJPROP_YSIZE, 300);
28.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
29.             SetInteger(OBJPROP_DATE_SCALE, false);
30.             SetInteger(OBJPROP_PRICE_SCALE, false);
31.             SetInteger(OBJPROP_SELECTABLE, true); 
32.             SetInteger(OBJPROP_SELECTED, false);
33.         }
34. //+----------------+
35.         ~C_MiniChart()
36.         {
37.             ObjectDelete(0, m_szNameObject);
38.             ChartRedraw();
39.         }
40. //+----------------+
41. }gl_MiniChart("Mini Chart");
42. //+------------------------------------------------------------------+
43. int OnInit(void)
44. {
45.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
46.    
47.     return INIT_SUCCEEDED;
48. };
49. //+------------------------------------------------------------------+
50. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
51. {
52.     return rates_total;
53. };
54. //+------------------------------------------------------------------+

Código 02

Bien, ahora sí tenemos un código considerablemente más sencillo de modificar y explicar. Además, por supuesto, resulta mucho más adecuado desde el punto de vista didáctico. Ahora quiero que prestes atención a algunos detalles de este código 02. Observa que, en la línea seis, estamos indicando que el indicador NO UTILIZARÁ UNA SUBVENTANA. Es extremadamente importante que entiendas muy bien lo que estamos indicando en esta línea seis. El segundo punto que debemos observar está relacionado con la línea cuarenta y uno. Allí estamos declarando una variable global que, a su vez, inicializa la clase. Sin embargo, al observar el contenido de OnInit, enseguida puedes pensar: Bien, pero este código no producirá el mismo resultado que el código 01. Y es precisamente aquí donde terminas tropezando y cayendo de bruces. Aunque este código 02 parezca diferente del código 01, produce el mismo tipo de resultado y tiene el mismo comportamiento. Esto se debe precisamente a lo que ocurre en la línea cuarenta y uno y a que estamos utilizando programación orientada a objetos.

Si tienes alguna duda al respecto, consulta el artículo Del nivel básico al intermedio: Clases (III), donde muestro algo parecido a lo que estamos haciendo aquí. Bien, pero este código 02 es bastante aburrido y poco interesante, ya que lo único que hace es crear un gráfico cuyo símbolo es el mismo que el del gráfico principal, pero con un período gráfico de quince minutos. Esto puede comprobarse al analizar la línea veintitrés, donde indicamos qué símbolo aparecerá en el gráfico, y la línea veintiocho, donde definimos el período gráfico utilizado para trazar las barras dentro de la pequeña ventana que se crea. Sin embargo, antes de profundizar en el tema, necesito que entiendas muy bien lo que ocurre aquí. Y, para que no quede ninguna duda sobre cómo funciona el código anterior, tenemos la animación que se muestra a continuación.


Animación 01

En esta animación se muestra el resultado de colocar el código 01 en un gráfico cualquiera. Del mismo modo, la animación que se muestra a continuación muestra lo que ocurre cuando retiramos del gráfico el indicador creado en los códigos anteriores.

Animación 02

Puedes ver claramente que el objeto colocado en el gráfico se elimina en cuanto retiramos el indicador. Es exactamente lo que cabría esperar debido a la forma en que se implementó el código. Sin embargo, aunque este resultado parezca obvio, como podrás comprobar al utilizar los códigos adjuntos, no siempre ocurre lo esperado cuando combinamos varias de las cosas que hemos visto hasta ahora en esta serie de artículos. Una de ellas consiste precisamente en aplicar el indicador mediante una llamada de la biblioteca de MQL5, tal como vimos en los últimos artículos.

Como no quiero complicar las cosas innecesariamente en este momento, vamos a hacer lo siguiente: crearemos otro indicador y, a partir de lo explicado en los artículos publicados recientemente, haremos que este nuevo indicador coloque el indicador construido en el código 02 dentro de una subventana. Recuerda que el indicador implementado en el código 02 NO UTILIZA UNA SUBVENTANA. Utiliza la ventana principal para interactuar con el usuario, es decir, con el operador del gráfico. Para hacerlo, utilizaremos 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_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Código 03

Ahora presta atención, porque, a diferencia de cuando trazamos una media en el gráfico, al trabajar con objetos debemos tomar algunas precauciones. Y, como quiero que todo sea fácil de entender, este código 03 contiene solo lo necesario para conseguir lo que pretendemos: colocar en una subventana el indicador visto en el código 02. Como el ejecutable del código 02 se integrará en el código 03 gracias a la línea once, donde incluimos el ejecutable como recurso interno del código 03, no será necesario transferir por separado el código 02 entre distintas instalaciones de MetaTrader 5. Solo tendremos que transferir a otro ordenador el ejecutable generado al compilar este código 03 y, aun así, obtendremos el mismo comportamiento. Esto se explicó en el artículo anterior. Para más detalles, revisa lo explicado allí.

Teniendo en cuenta que ya entiendes lo que realmente hará la línea quince del código 03, cabría esperar que se creara una subventana y que el objeto se colocara en ella. ¿Correcto? Bien, eso es lo que imaginas que debería ocurrir. Sin embargo, al ejecutar el código, te encuentras con el resultado que se muestra en la siguiente animación.

Animación 03

¿Qué ha pasado? No lo entiendo. Bien, mi querido lector, vamos a hacer un pequeño cambio en el código 02 para que todo quede mucho más claro. Este cambio se muestra en el fragmento que aparece a continuación.
                                   .
                                   .
                                   .
26.            SetInteger(OBJPROP_XSIZE, 300);
27.            SetInteger(OBJPROP_YSIZE, 150);
                                   .
                                   .
                                   .

Fragmento 01

Aquí estamos modificando las dimensiones del objeto OBJ_CHART que se va a crear. Entonces, eliminamos el ejecutable del código 02 y volvemos a compilar el código 03. De este modo, volveremos a integrar el ejecutable con las nuevas dimensiones definidas para el objeto OBJ_CHART. Esto hará que, al ejecutar el código 03, obtengamos lo que se muestra en la siguiente imagen.

Imagen 01

Qué cosa tan extraña. Pensé que el objeto OBJ_CHART se añadiría a la subventana, ya que estamos colocando el indicador del código 02 en una subventana. Sin embargo, aparentemente el código no lo ha interpretado así y ha terminado colocando el objeto OBJ_CHART en la ventana principal. Vaya, qué extraño. Pero espera un momento. Tal vez el indicador del código 02 no se esté enviando realmente a una subventana. Quizá por eso estoy viendo este resultado en la imagen 01. ¿Será ese el motivo? En realidad, no, mi querido lector. Si le pides a MetaTrader 5 que muestre la lista de indicadores presentes en el gráfico, verás que el indicador del código 02 sí está colocado en una subventana, como puede observarse claramente en la siguiente imagen.

Imagen 02

Ahora sí que ya no entiendo nada. Pensaba que todo funcionaría de una determinada manera, pero esto se está volviendo demasiado complicado para mí. Tranquilo, amigo mío. La diversión apenas comienza. Lo que ocurre es que imaginabas que las cosas funcionarían de una determinada manera, pero acabas de comprobar que, en la práctica, no siempre sucede lo que esperamos. Realmente necesitamos aprender y estudiar antes de ir por ahí creyendo que ya sabemos cómo funciona todo. Para que las cosas resulten un poco más sencillas, vamos a modificar el código 02 para que el objeto OBJ_CHART se muestre en una subventana, sin necesidad de recurrir al código 03. Sin embargo, no olvides una cosa: el objetivo aquí es utilizar el código 02 y el código 03 para obtener el resultado que veremos dentro de poco. Pero, para que puedas entender lo que vamos a hacer, iremos paso a paso. Por lo tanto, concéntrate en lo que se explicará.

Pues bien, después de modificar el código 02, obtenemos 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_plots           0
08. #property indicator_height          150
09. //+------------------------------------------------------------------+
10. class C_MiniChart
11. {
12.     private :
13. //+----------------+
14.         string m_szNameObject;
15. //+----------------+
16.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
17. //+----------------+
18.     public  :
19. //+----------------+
20.         C_MiniChart(string szName)
21.             :m_szNameObject(szName)
22.         {
23.             ObjectCreate(0, m_szNameObject, OBJ_CHART, 1, 0, 0);
24.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
25.             SetInteger(OBJPROP_XDISTANCE, 10);
26.             SetInteger(OBJPROP_YDISTANCE, 0);
27.             SetInteger(OBJPROP_XSIZE, 300);
28.             SetInteger(OBJPROP_YSIZE, indicator_height);
29.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
30.             SetInteger(OBJPROP_DATE_SCALE, false);
31.             SetInteger(OBJPROP_PRICE_SCALE, false);
32.             SetInteger(OBJPROP_SELECTABLE, true); 
33.             SetInteger(OBJPROP_SELECTED, false);
34.         }
35. //+----------------+
36.         ~C_MiniChart()
37.         {
38.             ObjectDelete(0, m_szNameObject);
39.             ChartRedraw();
40.         }
41. //+----------------+
42. }gl_MiniChart("Mini Chart");
43. //+------------------------------------------------------------------+
44. int OnInit(void)
45. {
46.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
47.    
48.     return INIT_SUCCEEDED;
49. };
50. //+------------------------------------------------------------------+
51. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
52. {
53.     return rates_total;
54. };
55. //+------------------------------------------------------------------+

Código 04

Ahora, al ejecutar este código 04, obtendremos un resultado parecido al de la siguiente animación.

Animación 04

Hmm, esto parece bastante interesante. En efecto, mi querido lector, este tipo de cosas resulta muy interesante y lo será aún más a medida que avancemos. Sin embargo, pese a lo interesante que pueda parecer, este código 04 tiene un problema. Y es un problema que, si no entiendes muy bien lo que está haciendo MetaTrader 5, puede darte muchos dolores de cabeza y bastantes disgustos. Incluso podría hacer que pierdas las ganas de seguir estudiando MQL5. Así que presta atención. Si comparas este código 04 con su antecesor, es decir, el código 02, notarás que se han realizado algunos cambios. Sin embargo, y este es el punto crucial, el cambio que realmente nos interesa está en la línea veintitrés: el cuarto argumento que se pasa a la función ObjectCreate. No lo entiendo: ¿por qué este cuarto argumento supondría un fallo en este código 04? ¿Podrías explicarlo mejor? En realidad, la forma más sencilla de entenderlo sería que probaras personalmente el código. Aun así, intentaré explicarlo para que puedas hacerte una idea del problema que presenta este cuarto argumento.

Como en la línea seis de este código 04 estamos indicando que será necesaria una subventana, MetaTrader 5 se encargará de crearla cuando sea necesario, tal como vimos en los artículos anteriores. Sin embargo, y esta es la parte que suele resultar confusa para muchos, cuando se cree el objeto OBJ_CHART, estaremos indicando a MetaTrader 5 que lo cree y lo coloque en una subventana. Ahora te pregunto: ¿en qué subventana? Bien, podrías decir que debería ser la subventana creada para alojar el indicador. ¿Correcto? Incorrecto, amigo lector. O, mejor dicho, quizá ocurra así, siempre que, en el momento en que este código 04 se añada al gráfico, NO EXISTA NINGUNA OTRA SUBVENTANA EN ÉL.

Presta mucha atención a esta afirmación, porque puede resultar útil en muchas situaciones cuando necesitemos realizar determinadas acciones dentro del gráfico. Sin embargo, este no es el caso aquí. Por esta razón, si ya existe alguna subventana ANTES de añadir al gráfico el indicador del código 04, MetaTrader 5 creará y colocará el objeto OBJ_CHART en la subventana con índice uno. Esto ocurre porque el cuarto argumento apunta precisamente a esa subventana.

Para intentar dejarlo un poco más claro, observa la siguiente animación.

Animación 05

Fíjate en que el comportamiento del código es distinto de lo que imaginabas como correcto. Sin embargo, en algunas situaciones muy concretas, este comportamiento de la animación 05 es exactamente lo que queremos conseguir. Tal vez esto se explique con más detalle en otro momento. Así que volvamos a nuestro problema inicial: crear una subventana y añadir dentro de ella el objeto OBJ_CHART. Bien, existen muchas formas distintas de resolver este problema. Cada una puede resultar más sencilla o más complicada dependiendo del caso concreto y del objetivo que queramos alcanzar. Pero, para mantener el enfoque didáctico del artículo, utilizaremos la que, a mi entender, es la forma más sencilla de todas. Recuerda que existen formas todavía más simples. Sin embargo, como aún no he explicado algunos recursos de la biblioteca de MQL5, la solución que veremos aquí es la más sencilla entre las disponibles actualmente. Esto se debe a que lo que vamos a hacer aquí ya se explicó en otro artículo de esta misma serie.

En el artículo Del nivel básico al intermedio: Subventanas (III), mostré cómo podíamos ver qué indicadores están presentes en un determinado gráfico. Así que, si tienes alguna duda sobre lo que veremos aquí, consulta ese artículo para obtener más detalles. Bien, ahora vamos a la parte realmente divertida del artículo. Y, para ello, empezaremos con 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_plots           0
08. #property indicator_height          150
09. //+------------------------------------------------------------------+
10. #define def_ShortName "Test 123."
11. //+------------------------------------------------------------------+
12. class C_MiniChart
13. {
14.     private :
15. //+----------------+
16.         string  m_szNameObject;
17.         int     m_Sub;
18. //+----------------+
19.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
20. //+----------------+
21.         int WhatSubWin(void)
22.         {
23.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
24.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
25.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
26.                         return c0;
27.             return INVALID_HANDLE;
28.         }
29. //+----------------+
30.     public  :
31. //+----------------+
32.         C_MiniChart(string szName)
33.         {
34.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
35.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
36.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
37.             SetInteger(OBJPROP_XDISTANCE, 10);
38.             SetInteger(OBJPROP_YDISTANCE, 0);
39.             SetInteger(OBJPROP_XSIZE, 300);
40.             SetInteger(OBJPROP_YSIZE, indicator_height);
41.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
42.             SetInteger(OBJPROP_DATE_SCALE, false);
43.             SetInteger(OBJPROP_PRICE_SCALE, false);
44.             SetInteger(OBJPROP_SELECTABLE, true); 
45.             SetInteger(OBJPROP_SELECTED, false);
46.         }
47. //+----------------+
48.         ~C_MiniChart()
49.         {
50.             ObjectDelete(0, m_szNameObject);
51.             ChartRedraw();
52.         }
53. //+----------------+
54. }*gl_MiniChart;
55. //+------------------------------------------------------------------+
56. int OnInit(void)
57. {
58.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
59. 
60.     gl_MiniChart = new C_MiniChart("Mini Chart");
61.    
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnDeinit(const int reason)
71. {
72.     delete gl_MiniChart;
73. };
74. //+------------------------------------------------------------------+

Código 05

A primera vista, este código 05 puede parecer muy complicado y extremadamente elaborado. Sin embargo, nada de lo que se hace aquí es, o debería ser, nuevo para quienes siguen y estudian estos artículos. Todo lo que se hace aquí ya lo hemos visto antes. Por supuesto, se han realizado algunos pequeños ajustes para que el código resulte un poco más fácil de entender. Pero la cuestión principal está directamente relacionada con lo que se hace en la línea cincuenta y cuatro. Observa que, a diferencia de lo que hacíamos antes, ahora ya no tenemos ese mismo grado de libertad. Y el motivo es bastante sencillo: necesitamos saber dónde se colocará el indicador. ¿Cómo que dónde? Claro que sé dónde se colocará. No me refiero a eso, amigo mío.

La cuestión es que, mientras MetaTrader 5 no haya definido el nombre del ejecutable como una cadena que podamos comprobar de forma clara y sencilla, no tenemos manera de saber dónde colocará MetaTrader 5 el indicador ni cómo decidirá tratarlo. No olvides que, dependiendo de dónde coloques el indicador y de la configuración que tenga, MetaTrader 5 podrá tratarlo de una forma u otra. Esto ya se explicó en los artículos anteriores.

Una vez ejecutada la línea cincuenta y ocho, ya podemos saber dónde se coloca realmente el indicador. A partir de ahí, podemos determinar cuál es la subventana en la que debe colocarse el objeto OBJ_CHART. Esto se hace en la línea treinta y cuatro. Ahora bien, como ya no estamos trabajando de la misma forma que antes, debemos asegurarnos de destruir el puntero creado en la línea sesenta. Por esta razón, necesitamos añadir el manejador OnDeinit, que puedes ver en la línea setenta.

Bien, pero ¿funciona realmente este código 05 como queremos? Para responder a esta pregunta, observa la siguiente animación.

Animación 06

Vaya, esto sí que se ha vuelto mucho más interesante. Ahora sí parece que vamos por buen camino. Y, en efecto, mi querido lector, la cosa se ha vuelto bastante más interesante. Pero siempre hay un «pero» en toda historia. Incluso este código 05 contiene un fallo. Vamos, ¿por qué no muestras directamente el código sin ningún defecto? ¿Por qué me haces esto? Primero me das una dosis de ánimo y, justo después, me das un martillazo en la cabeza. Es muy cruel hacer las cosas así. Bueno, visto de esa manera, quizá hasta pueda parecer cruel. Pero creo que estás olvidando que todavía no eres programador y que estás leyendo estos artículos precisamente para aprender cómo podemos hacer las cosas de la mejor manera posible.

Bien, el fallo de este código 05 se debe a que, si intentas añadir dos instancias del mismo indicador a un mismo gráfico, no se crearán nuevas instancias del objeto OBJ_CHART. El motivo es que estamos indicando a MetaTrader 5 que utilice un único nombre para el objeto OBJ_CHART. Bien, entonces es muy sencillo corregirlo: basta con asignar un nombre diferente a cada nueva instancia del objeto que creemos. Resolver este tipo de problema parece demasiado fácil. A decir verdad, ni siquiera parece un problema.

Ojalá las cosas fueran siempre tan sencillas como a veces imaginas. En la práctica, el problema es un poco más complejo. Presta atención: aunque hagas lo que acabas de decir, y no estás equivocado al pensarlo así, el verdadero problema aparece cuando la línea treinta y cuatro llama a la función de la línea veintiuno. Hmm, no lo entiendo. Entonces detente un momento y piénsalo, porque enseguida comprenderás cuál es el verdadero problema.

Cuando el indicador del código 05 se añade al gráfico, buscará la primera instancia del indicador identificado por el nombre que declaramos en la línea diez. Aunque asignes un nombre diferente al objeto OBJ_CHART, cuando la función de la línea veintiuno retorne, nos indicará en qué subventana se encuentra la primera instancia del indicador definido con el nombre de la línea diez. Por esta razón, si únicamente cambias el nombre del objeto OBJ_CHART, no podrás colocar el nuevo objeto en la subventana correcta.

Existen formas de sortear este tipo de problema. Algunas son incluso bastante poco habituales. Sin embargo, ese no es nuestro objetivo en este primer momento. Solo lo menciono para que tengas presente que, por mucho que todo parezca funcionar sin fallos ni contratiempos, todavía existen problemas que quizá no hayas notado. Y, para resolverlos, tendremos que abordar temas y recursos un poco más avanzados. Por eso conviene estudiar cada uno de estos artículos con calma.

Bien, una vez hecha esta observación y mostrado cómo trabajar con lo que acabamos de crear en el código 05, podemos volver a centrar nuestra atención en la integración del código 02 con el código 03. Y no lo olvides: a diferencia del código 02, en el que no indicamos que vayamos a utilizar una subventana, el principio aplicado en el código 05 nos servirá para evitar que el objeto se cree y se coloque en la subventana equivocada. Así que, para corregir el código 02 y posteriormente integrarlo con el código 03, debemos modificarlo como 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. #define def_ShortName "Test 123."
10. //+------------------------------------------------------------------+
11. class C_MiniChart
12. {
13.     private :
14. //+----------------+
15.         string  m_szNameObject;
16.         int     m_Sub;
17. //+----------------+
18.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
19. //+----------------+
20.         int WhatSubWin(void)
21.         {
22.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
23.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
24.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
25.                         return c0;
26.             return INVALID_HANDLE;
27.         }
28. //+----------------+
29.     public  :
30. //+----------------+
31.         C_MiniChart(string szName)
32.         {
33.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
34.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
35.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
36.             SetInteger(OBJPROP_XDISTANCE, 10);
37.             SetInteger(OBJPROP_YDISTANCE, 0);
38.             SetInteger(OBJPROP_XSIZE, 300);
39.             SetInteger(OBJPROP_YSIZE, 150);
40.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
41.             SetInteger(OBJPROP_DATE_SCALE, false);
42.             SetInteger(OBJPROP_PRICE_SCALE, false);
43.             SetInteger(OBJPROP_SELECTABLE, true); 
44.             SetInteger(OBJPROP_SELECTED, false);
45.         }
46. //+----------------+
47.         ~C_MiniChart()
48.         {
49.             ObjectDelete(0, m_szNameObject);
50.             ChartRedraw();
51.         }
52. //+----------------+
53. }*gl_MiniChart;
54. //+------------------------------------------------------------------+
55. int OnInit(void)
56. {
57.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
58. 
59.     gl_MiniChart = new C_MiniChart("Mini Chart");
60.    
61.     return INIT_SUCCEEDED;
62. };
63. //+------------------------------------------------------------------+
64. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
65. {
66.     return rates_total;
67. };
68. //+------------------------------------------------------------------+
69. void OnDeinit(const int reason)
70. {
71.     delete gl_MiniChart;
72. };
73. //+------------------------------------------------------------------+

Código 06

Ahora observa lo siguiente: este código 06 se parece mucho al código 05. Sin embargo, a diferencia del código 05, este código 06 está pensado para utilizarse en la ventana principal del gráfico. Esto se debe precisamente a la declaración de la línea seis de este código 06. Pero, y esta es la parte que nos interesa ahora, si modificamos el código 03 para que utilice el ejecutable generado por este código 06, obtendremos un comportamiento bastante interesante y prácticamente igual al que buscábamos desde el principio.

Para asegurarnos de que dispongas de los códigos fuente que aparecen aquí y puedas estudiarlos y probarlos poco a poco, en los archivos adjuntos encontrarás, además del código 03, otros códigos que te ayudarán a comprender cada detalle con calma. Así, a continuación se muestra el código que integrará el ejecutable del código 06.
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. #define def_Resource "Code 05.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Código 07

Observa que la única diferencia entre este código 07 y el código 03 está precisamente en la línea nueve; todo lo demás es exactamente igual. Sin embargo, esta simple diferencia hará que el código 06 quede integrado en este código 07. Después de compilar el código 07, podrás ejecutarlo en cualquier gráfico de MetaTrader 5 y obtendrás un resultado parecido al que se muestra en la siguiente animación.

Animación 07

Vaya, la verdad es que esto ha quedado realmente espectacular. Qué maravilla. Ahora sí me siento mucho más seguro y todavía más interesado en estudiar cada detalle de tus artículos. Sin embargo, he notado que el código 06 no solo se diferencia del código 05 en la línea seis. ¿Por qué? ¿No podríamos utilizar el código 05 cambiando únicamente la línea seis? Claro que podemos, mi querido lector. Sin embargo, como este código 06 muestra cómo debía quedar el código 02 para que el objeto OBJ_CHART pudiera colocarse en el lugar correcto, hice esos cambios únicamente para mostrar esta actualización del código 02. Pero nada te impide utilizar el código 05 modificando únicamente la línea seis. El resultado sería algo que todavía no vamos a utilizar. Aun así, puede que ya estés pensando en alguna idea, así que veamos cómo habría que hacerlo. En los archivos adjuntos también encontrarás estos códigos para que puedas probarlos. El primer cambio daría lugar al código que 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_height          150
08. #property indicator_plots           0
09. //+------------------------------------------------------------------+
10. #define def_ShortName "Test 123."
11. //+------------------------------------------------------------------+
12. class C_MiniChart
13. {
14.     private :
15. //+----------------+
16.         string  m_szNameObject;
17.         int     m_Sub;
18. //+----------------+
19.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
20. //+----------------+
21.         int WhatSubWin(void)
22.         {
23.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
24.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
25.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
26.                         return c0;
27.             return INVALID_HANDLE;
28.         }
29. //+----------------+
30.     public  :
31. //+----------------+
32.         C_MiniChart(string szName)
33.         {
34.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
35.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
36.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
37.             SetInteger(OBJPROP_XDISTANCE, 10);
38.             SetInteger(OBJPROP_YDISTANCE, 0);
39.             SetInteger(OBJPROP_XSIZE, 300);
40.             SetInteger(OBJPROP_YSIZE, indicator_height);
41.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
42.             SetInteger(OBJPROP_DATE_SCALE, false);
43.             SetInteger(OBJPROP_PRICE_SCALE, false);
44.             SetInteger(OBJPROP_SELECTABLE, true); 
45.             SetInteger(OBJPROP_SELECTED, false);
46.         }
47. //+----------------+
48.         ~C_MiniChart()
49.         {
50.             ObjectDelete(0, m_szNameObject);
51.             ChartRedraw();
52.         }
53. //+----------------+
54. }*gl_MiniChart;
55. //+------------------------------------------------------------------+
56. int OnInit(void)
57. {
58.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
59. 
60.     gl_MiniChart = new C_MiniChart("Mini Chart");
61.    
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnDeinit(const int reason)
71. {
72.     delete gl_MiniChart;
73. };
74. //+------------------------------------------------------------------+

Código 08

A su vez, el código que integrará el ejecutable del código 08 y se encargará de llamar al indicador y crear la subventana 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. #define def_Resource "Code 07.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Código 09

El código 09 es muy parecido a los demás que utilizamos para realizar la integración. Básicamente, la diferencia respecto a los otros está en la definición de la línea nueve. Sin embargo, y esta es la parte que podrás probar por tu cuenta, en el código 08 tenemos una definición en la línea siete. Esta definición nos permitirá establecer la altura de la subventana cuando se cree. Ahora bien, este código 08, a diferencia de lo que vimos en el código 05, NO CREARÁ UNA SUBVENTANA. Será el código 09 quien lo haga por él. Por lo tanto, aunque no tengas especial interés en hacerlo, podrás añadir el indicador del código 09 a una subventana que ya contenga otro indicador. Y esto nos permite crear nuevas y divertidas configuraciones para utilizarlas en MetaTrader 5.

Pero este tipo de cosas se verá con más detalle en otro artículo próximamente. Por ahora, creo que ya tienes bastante que estudiar y practicar.


Consideraciones finales

Muchas veces imaginamos que ciertas cosas no pueden hacerse cuando, en realidad, lo que nos falta es el conocimiento adecuado para encontrar la mejor solución a un determinado problema.

En este artículo hemos visto una pequeña parte de todo lo que podemos explorar con mucha más profundidad para obtener resultados realmente increíbles. Y hemos hecho todo esto utilizando conocimientos básicos y sin ninguna complicación. Por lo tanto, la diferencia entre conseguir un resultado increíble y uno meramente adecuado depende de cuánto te esfuerces por llegar adonde nadie más ha llegado. Aun así, estoy seguro de que, incluso después de ver lo que se ha mostrado aquí, muchos dirán que no sería necesario hacer nada de esto, ya que existen formas mejores y más sencillas de obtener el mismo tipo de resultado. Debo admitir que tienen razón. Sin embargo, todavía no hemos llegado al punto en el que, sin los conocimientos presentados aquí, no podrás reproducir los mismos resultados que consigue quien realmente sabe lo que está haciendo. Así que estudia con calma el contenido presentado aquí, porque en los próximos artículos profundizaremos todavía más en el uso de objetos dentro de subventanas.
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
Indicators\Code 06 Demostración básica
Indicators\Code 07 Demostración básica
Indicators\Code 08 Demostración básica

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

Archivos adjuntos |
Anexo.zip (6.08 KB)
Red neuronal en la práctica: Gradiente Red neuronal en la práctica: Gradiente
El artículo explica por qué y cómo se utiliza el gradiente durante el entrenamiento de un perceptrón, partiendo del error cuadrático mínimo y de la regla de la cadena para obtener las derivadas parciales. Mostramos la implementación del cálculo del gradiente en la clase C_Neuron en MQL5 y lo validamos con ejemplos de 1 y 2 entradas. Aprenderás a ajustar los pesos y el sesgo mediante descenso de gradiente y a prepararte para la propagación hacia adelante y la retropropagación.
Red neuronal en la práctica: Funciones de activación Red neuronal en la práctica: Funciones de activación
Este ha sido, con toda seguridad, el artículo que más me ha gustado escribir hasta ahora sobre este tema. En él mostré que, en realidad, no necesitamos gran cosa ni nada específico para alcanzar un determinado objetivo. Podemos conseguirlo de distintas maneras, siempre que contemos con los conocimientos adecuados y tengamos la disposición necesaria para estudiar y dedicarnos a ello. Agradezco de corazón a todos los que me ayudaron con la elección de las funciones. El contenido expuesto aquí tiene una finalidad exclusivamente didáctica. En ningún caso debe considerarse una aplicación destinada a algo distinto del aprendizaje y el estudio de los conceptos presentados.
Del nivel básico al intermedio: Recursos Del nivel básico al intermedio: Recursos
En este artículo conocerás un concepto que puede resultar extremadamente útil en muchos casos y facilitar enormemente el intercambio de tus aplicaciones y proyectos. Aunque no es un concepto sencillo de explicar por completo en un solo artículo, lo que se explicará y expondrá aquí ya nos permitirá hacer muchas cosas en el futuro, incluidas algunas que, de otro modo, no sería posible realizar. Precisamente porque este artículo aún no se había publicado: para que pudieras contar con material de apoyo y una base inicial de estudio.
Del báscio al intermedio: Subventanas (IV) Del báscio al intermedio: Subventanas (IV)
En este artículo veremos que no todo es como muchos piensan al principio. Una de las cosas más interesantes de la programación es que podemos garantizar que las cosas siempre sean como las hemos planeado. Así que lean este artículo con atención para aprender algunos de los conceptos más confusos relacionados con el uso de subventanas. Si entiendes lo que se explicará aquí, podrás comprender muchas de las cosas que haremos más adelante.