Português
preview
Del nivel básico al intermedio: Archivo de plantilla (III)

Del nivel básico al intermedio: Archivo de plantilla (III)

MetaTrader 5 — Ejemplos |
24 0
CODE X
CODE X

Introducción

En el artículo anterior Del nivel básico al intermedio: Archivo de plantilla (II), vimos cómo podíamos añadir un indicador a un gráfico contenido en un objeto OBJ_CHART. Si no has leído aquel artículo, te sugiero que interrumpas ahora la lectura y leas primero el artículo anterior. Comprender lo explicado allí es fundamental para poder seguir lo que veremos aquí.

Recuerda que, aunque no estamos implementando ninguna aplicación específica, no volveremos a repetir aquí los conocimientos ya tratados en artículos anteriores. Por tanto, si tienes dudas sobre algún punto, revisa los artículos anteriores para poder seguir el contenido que estamos tratando.

Así que prepárate, porque aquí profundizaremos en lo explicado en el artículo anterior y haremos especial hincapié en abordar algunas cuestiones con mayor amplitud. Ha llegado el momento de alejarse de cualquier distracción y concentrarse única y exclusivamente en lo que se explicará aquí. Porque ahora viene lo difícil.


Archivo de plantilla (III)

Lo visto en el artículo anterior es solo un pequeño anticipo de lo que realmente podemos hacer. Aunque muchos principiantes, e incluso programadores con cierta experiencia, imaginen que es totalmente imposible hacer determinadas cosas, lo cierto es que no todo puede ni podrá conseguirse utilizando única y exclusivamente programación en MQL5. En general, MQL5 es excelente para indicarle a MetaTrader 5 cómo hacer las cosas. Sin embargo, entender cómo funciona la plataforma te permitirá tener un control mucho más amplio, completo y profundo sobre la forma en que tus aplicaciones funcionarán realmente dentro de MetaTrader 5.

Lo demostrado en el artículo anterior está lejos de ser algo obvio. Tanto es así que, cuando se publicaron aquí mis primeros artículos, muchos no llegaron a comprender realmente cómo funcionaban aquellas aplicaciones. Esto ocurrió precisamente porque esperaban encontrar en el código algo que, en realidad, no estaba allí. El secreto de las aplicaciones mostradas en aquella época consistía justamente en que no todo puede conseguirse utilizando únicamente MQL5 o mediante programación pura y simple.

Aunque a alguno pueda resultarle extraña la forma en que se han hecho las cosas, quiero que comprendas que, cuando se habla de programación, no todo, o mejor dicho, no todas las respuestas están realmente en el código. Muchas veces, entender cómo funciona el sistema operativo, o incluso la aplicación con la que estamos trabajando, nos dará respuestas que no podríamos obtener de otra manera. Así, algo que en principio parecería imposible termina siendo perfectamente viable.

Ya he visto a mucha gente rompiéndose la cabeza intentando implementar algo cuando, si conociera la aplicación o incluso el sistema operativo, no estaría ahí luchando por programarlo todo. Simplemente utilizaría los recursos o mecanismos disponibles en el propio sistema operativo, o incluso en la aplicación, que en nuestro caso sería MetaTrader 5, para sortear el problema y crear la solución.

Precisamente este tipo de cosas era lo que exploraba en mis primeros artículos. Sin embargo, la explicación de por qué funcionaban no fue lo suficientemente clara. Esto ocurrió porque imaginaba, erróneamente, que el público objetivo era otro. Como consecuencia, muchos de quienes intentaban reproducir los resultados mostrados se encontraron con una laguna de conocimientos.

Precisamente debido a este fallo en la explicación, se decidió volver a los orígenes. Así que no volveré a dar una explicación a medias. Quiero que entiendas hasta el más mínimo detalle de cómo se crea lo que vamos a mostrar.

Para empezar, existen algunos requisitos previos para poder seguir perfectamente lo que haremos. Entre ellos está comprender lo que hicimos en el artículo anterior, además de haber entendido lo explicado en los siguientes artículos:

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

Del nivel básico al intermedio: Subventanas (IV)

Del nivel básico al intermedio: Eventos en objetos (IV)

Dicho esto, podemos pasar a la parte en la que implementaremos algo un tanto disparatado. Digo disparatado porque lo que veremos suele hacerle un buen nudo en la cabeza a muchos programadores principiantes y dejar a los más experimentados con la mosca detrás de la oreja. Esto se debe a que programaremos muy poco, pero obtendremos resultados bastante interesantes que van mucho más allá de lo que, en principio, parece prometer el código.

Para empezar, crearemos un código sencillo y casi completamente insulso. Se muestra a continuación.

01. #property copyright "Daniel Jose"
02. #property description "DEMO Indicator"
03. #property indicator_separate_window
04. #property indicator_height          100
05. #property indicator_plots           0
06. //+------------------------------------------------------------------+
07. class C_MultiChart
08. {
09. private:
10.    string            m_szObject[];
11. 
12.    void              NewObj_CHART(void)
13.      {
14.       int            nObjects = ArraySize(m_szObject) + 1;
15.       string         sz0 = "Demo_Chart #" + (string)ObjectsTotal(0, -1, -1);
16. 
17.       ArrayResize(m_szObject, nObjects);
18.       m_szObject[nObjects - 1] = sz0;
19. 
20.       ObjectCreate(0, sz0, OBJ_CHART, ChartWindowFind(), 0, 0);
21.       ObjectSetString(0, sz0, OBJPROP_SYMBOL, _Symbol);
22.       ObjectSetInteger(0, sz0, OBJPROP_YDISTANCE, 0);
23.       ObjectSetInteger(0, sz0, OBJPROP_PERIOD, PERIOD_M15);
24.       ObjectSetInteger(0, sz0, OBJPROP_DATE_SCALE, false);
25.       ObjectSetInteger(0, sz0, OBJPROP_PRICE_SCALE, false);
26.       ObjectSetInteger(0, sz0, OBJPROP_YSIZE, indicator_height);
27.      }
28. 
29. public:
30.                      C_MultiChart(void)
31.      {
32.       for(int c = 0; c < 2; c++)
33.          NewObj_CHART();
34.      }
35. 
36.    void              Resize(void)
37.      {
38.       const int      nObjects = ArraySize(m_szObject);
39.       const int      step = (int)(ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) /
40.                                   (nObjects > 0 ? nObjects : 1));
41. 
42.       for(int c = 0, x = 0; c < nObjects; c++, x += step)
43.         {
44.          ObjectSetInteger(0, m_szObject[c], OBJPROP_XDISTANCE, x);
45.          ObjectSetInteger(0, m_szObject[c], OBJPROP_XSIZE, step);
46.         }
47.       ChartRedraw();
48.      }
49. };
50. //+------------------------------------------------------------------+
51. C_MultiChart *gl_Multi = NULL;
52. //+------------------------------------------------------------------+
53. int OnInit(void)
54.   {
55.    gl_Multi = new C_MultiChart();
56. 
57.    return(INIT_SUCCEEDED);
58.   }
59. //+------------------------------------------------------------------+
60. int OnCalculate(const int rates_total,
61.                 const int prev_calculated,
62.                 const int begin,
63.                 const double &price[])
64.   {
65.    return(rates_total);
66.   }
67. //+------------------------------------------------------------------+
68. void OnChartEvent(const int id,
69.                   const long &lparam,
70.                   const double &dparam,
71.                   const string &sparam)
72.   {
73.    switch(id)
74.      {
75.       case CHARTEVENT_CHART_CHANGE:
76.          if(gl_Multi != NULL)
77.             gl_Multi.Resize();
78.          break;
79.      }
80.   }
81. //+------------------------------------------------------------------+
82. void OnDeinit(const int reason)
83.   {
84.    delete gl_Multi;
85.   }
86. //+------------------------------------------------------------------+

Código 01

Aunque sea poco interesante, este código 01 nos permite crear lo que se muestra en la siguiente imagen.


Imagen 01

Tal vez tengas una duda sobre algo que aparece en este código 01. Y, para quien viene estudiando y practicando lo que hemos enseñado en esta serie, esa duda es, en cierto modo, pertinente y debe quedar completamente resuelta. Se refiere a lo siguiente: ¿dónde está el procedimiento o la función responsable de eliminar del gráfico los objetos de tipo OBJ_CHART? Pues bien, el detalle es que no necesitamos preocuparnos por eliminar esos objetos en este código 01. Esto se debe a que, en la línea seis, estamos indicando que utilizaremos una subventana. A continuación, en la línea veintitrés, crearemos los objetos.

Estamos indicando a MetaTrader 5 que cree los objetos directamente en la subventana donde se colocará el indicador. Ahora presta mucha atención: como los objetos se colocarán en la misma subventana en la que estará el indicador, cuando elimines el indicador del gráfico, MetaTrader 5 también eliminará todo lo que haya dentro de esa subventana. Es decir, aunque nosotros no eliminemos los objetos del gráfico, MetaTrader 5 lo hará por nosotros, ya que un objeto creado en una subventana no podrá existir si esa subventana deja de existir.

Sin embargo, hay un detalle. Observa que, en la línea setenta y nueve, le estamos pidiendo a MetaTrader 5 que libere la memoria asignada para almacenar la instancia de la clase creada en la línea cincuenta y siete. Esta liberación de memoria sí debe realizarse. De lo contrario, MetaTrader 5 mostrará una advertencia indicando que todavía existen elementos en memoria que no han sido liberados. ¿Ahora puedes ver lo importante que es comprender correctamente cómo funciona la plataforma? Sin comprender este funcionamiento, acabaríamos creando código innecesario. Y, en otras ocasiones, dejaríamos de incluir código que realmente es necesario. Este tipo de conocimiento solo se adquiere con la práctica y el estudio. Así que ten paciencia si todavía no has entendido lo que acabamos de hacer.

A pesar de estas explicaciones, hasta este momento no hemos hecho absolutamente nada. Vamos a dejar por un momento este código 01, ya que solo es una parte de lo que realmente necesitamos hacer aquí, y veremos otra cosa, que es donde empieza lo complicado. Esto se debe a que mucha gente piensa que es necesario crear algo cuando, en realidad, no necesitamos crear nada, sino adaptar lo que ya sabemos para obtener el resultado deseado.

Ahora concéntrate por completo en lo que voy a explicar. Porque esto ya provocó mucha confusión en el pasado. Nuestro objetivo será colocar dos indicadores dentro de la subventana mostrada en la imagen 01. Presta mucha atención: el objetivo es colocar dos indicadores en una misma subventana. Sin embargo, los dos indicadores están diseñados para colocarse en subventanas diferentes. Por tanto, en teoría, nunca podríamos colocar ambos en una sola subventana. Al menos, no sin que su interpretación resultara bastante confusa.

Como el objetivo es didáctico y queremos que todo resulte lo más fácil posible de entender, en este primer momento no implementaremos ningún código. Utilizaremos indicadores que ya están disponibles de forma predeterminada en MetaTrader 5. Así todos podrán comprender que crear el código es solo un detalle. Lo realmente importante es que entiendas hasta dónde podemos llegar utilizando MetaTrader 5 sin recurrir a recursos extremadamente complejos y sofisticados.

Muy bien, existen varios pequeños trucos, o pasos, por así decirlo, que debemos seguir para tener mayor libertad y reducir la necesidad de crear código. Lo primero que debemos hacer se muestra en la siguiente animación.


Animación 01

Aquí estamos asegurándonos de que se utilice toda la ventana. Esto es importante para evitar un desplazamiento demasiado grande después, cuando los indicadores se coloquen en las subventanas. Una vez terminado todo el proceso, podremos volver a activar el desplazamiento. Pero, por ahora, mantenlo desactivado. Si te olvidas de desactivarlo, podrás hacerlo después. Sin embargo, requerirá mucho más trabajo. Así que vamos a reducir el trabajo y a hacer las cosas con cuidado y atención.

Bien, ahora que el desplazamiento está desactivado, podemos pasar al siguiente paso, que consiste en elegir qué indicador utilizaremos. Puedes utilizar cualquier indicador que quieras, literalmente. Sin embargo, dependiendo del caso, puede ser necesario realizar un paso adicional. Por ahora no lo abordaremos, precisamente para evitar una confusión innecesaria. Así que empezaremos por el camino más sencillo.

Bien, como queremos utilizar dos indicadores en una misma subventana, vayamos despacio y añadamos el primero de los indicadores que queremos utilizar. Esto se muestra en la siguiente animación.


Animación 02

Puedes configurar el indicador como quieras. Pero recuerda hacerlo aquí y ahora, porque hacerlo después será mucho más trabajoso. Y, como ya se dijo, queremos evitar tener que hacer ajustes más adelante. Bien, una vez que lo hayas configurado como prefieras, el siguiente paso será guardar esta configuración en un archivo de plantilla. Para ello, seguimos los pasos vistos en el artículo anterior. Pero, para evitar dudas, hacemos lo siguiente:


Imagen 02

En esta imagen seleccionamos la opción indicada. Esto abrirá una ventana en la que podremos guardar la configuración en una plantilla. Cuando termines, aparecerá lo que se muestra en la siguiente imagen.


Imagen 03

Ahora tenemos un nuevo archivo de plantilla disponible y listo para utilizarse. Bien, todo lo relacionado con nuestro primer indicador ya está preparado. Ahora vamos a añadir el segundo indicador. En este punto hay un detalle importante. Como ya debes saber, los archivos de plantilla, cuando se aplican a un gráfico, restauran toda su configuración interna en el gráfico al que se aplican. Así que, si no quieres que varios indicadores compartan el mismo gráfico, tendrás que limpiarlo y eliminar todo lo que no sea necesario. Con esto, volvemos a tener algo parecido a lo que se muestra a continuación.


Imagen 04

Perfecto, ya estamos casi allí. Ahora elegimos el que será nuestro segundo indicador. En este caso, elegiré el que se muestra en la siguiente animación.


Animación 03

Y, una vez más, configuramos el indicador como queramos y procedemos de la misma forma que con el primer indicador. Es decir, volvemos a la imagen 02 y guardamos la plantilla. Al final tendremos lo que se muestra en la siguiente imagen.


Imagen 05

Ahora observa que ya tenemos configurados los dos indicadores que queremos utilizar. Fíjate en que cada uno está en una plantilla diferente. Esto facilita bastante lo que haremos dentro de poco. Sin embargo, nada impide que los coloques juntos y después los separes. Aunque esto generará mucho más trabajo. Un trabajo que podemos evitar simplemente siguiendo el procedimiento mostrado aquí, es decir, manteniendo los archivos separados.

La ventaja de hacerlo de esta manera es que nos ahorra bastante trabajo y esfuerzo. Y ese tiempo que ahorramos podemos dedicarlo después a algo más interesante. Por tanto, aprender a hacer las cosas nunca es una pérdida de tiempo, aunque muchos, a veces, quieran dar a entender lo contrario. Pero entonces, ¿cuál es el siguiente paso? Bien, si leíste y entendiste lo explicado en el artículo anterior, seguramente ya tienes la respuesta en la punta de la lengua. Porque el siguiente paso será aplicar estas plantillas guardadas al gráfico creado y mantenido por el objeto OBJ_CHART.

Podemos hacerlo de mil maneras distintas, y ninguna será mejor ni peor que otra. Todo depende de cómo quieras hacer las cosas y del tipo de comportamiento que quieras que tenga tu código. Como aquí el objetivo es didáctico, no voy a decirte que este o aquel camino sea el correcto. Pero sí voy a mostrarte qué concepto debes utilizar para obtener el resultado deseado, que en este caso consiste precisamente en tener dos indicadores distintos en una misma subventana.

Muy bien, volvamos a centrar nuestra atención en el código 01. Ahora detente y piensa un momento: ¿cuál sería la mejor forma de indicarle al código de la clase C_MultiChart qué plantillas deberían utilizarse en cada uno de los objetos de tipo OBJ_CHART? Bien, la respuesta es: depende. Como cada caso es diferente, puedes imaginar distintas formas de hacerlo. Pero vamos a jugar un poco con este código para hacer algo bastante inusual. Observa que, en el constructor de la clase, estamos indicando que queremos mostrar dos objetos de tipo OBJ_CHART en la subventana. Pero ¿qué ocurre si queremos colocar más? ¿Y si no queremos dos, sino solo uno? Bien, esto puede parecer un poco extraño. Pero podemos hacer que el código se adapte a lo que queremos crear. Y, haciéndolo así, creemos que el código quedará mucho más interesante y divertido, ya que cada uno podrá crear su propia configuración con un esfuerzo mínimo.

Así que, trabajando con lo que ya tenemos entre manos y sin recurrir a otros conceptos ya explicados que no quiero aplicar aquí y ahora, vamos a modificar el código 01 para obtener finalmente lo que se muestra a continuación.

01. #property copyright "Daniel Jose"
02. #property description "DEMO Indicator"
03. #property indicator_separate_window
04. #property indicator_height          100
05. #property indicator_plots           0
06. //+------------------------------------------------------------------+
07. class C_MultiChart
08.   {
09. private:
10.    string            m_szObject[];
11. 
12.    void              NewObj_CHART(void)
13.      {
14.       int            nObjects = ArraySize(m_szObject) + 1;
15.       string         sz0 = "Demo_Chart #" + (string)ObjectsTotal(0, -1, -1);
16. 
17.       ArrayResize(m_szObject, nObjects);
18.       m_szObject[nObjects - 1] = sz0;
19. 
20.       ObjectCreate(0, sz0, OBJ_CHART, ChartWindowFind(), 0, 0);
21.       ObjectSetString(0, sz0, OBJPROP_SYMBOL, _Symbol);
22.       ObjectSetInteger(0, sz0, OBJPROP_YDISTANCE, 0);
23.       ObjectSetInteger(0, sz0, OBJPROP_PERIOD, PERIOD_M15);
24.       ObjectSetInteger(0, sz0, OBJPROP_DATE_SCALE, false);
25.       ObjectSetInteger(0, sz0, OBJPROP_PRICE_SCALE, false);
26.       ObjectSetInteger(0, sz0, OBJPROP_YSIZE, indicator_height);
27.      }
28. 
29. public:
30.                      C_MultiChart(const string &szTemplates[])
31.      {
32.       for(int c = 0; c < ArraySize(szTemplates); c++)
33.          NewObj_CHART();
34.      }
35. 
36.    void              Resize(void)
37.      {
38.       const int      nObjects = ArraySize(m_szObject);
39.       const int      step = (int)(ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) /
40.                                   (nObjects > 0 ? nObjects : 1));
41. 
42.       for(int c = 0, x = 0; c < nObjects; c++, x += step)
43.         {
44.          ObjectSetInteger(0, m_szObject[c], OBJPROP_XDISTANCE, x);
45.          ObjectSetInteger(0, m_szObject[c], OBJPROP_XSIZE, step);
46.         }
47.       ChartRedraw();
48.      }
49.   };
50. //+------------------------------------------------------------------+
51. C_MultiChart *gl_Multi = NULL;
52. //+------------------------------------------------------------------+
53. //| Custom indicator initialization function                         |
54. //+------------------------------------------------------------------+
55. int OnInit(void)
56.   {
57.    const string     szTemplates[] =
58.      {
59.       "Bollinger Bands.tpl",
60.       "Parabolic SAR.tpl"
61.      };
62. 
63.    gl_Multi = new C_MultiChart(szTemplates);
64. 
65.    return(INIT_SUCCEEDED);
66.   }
67. //+------------------------------------------------------------------+
68. //| Custom indicator iteration function                              |
69. //+------------------------------------------------------------------+
70. int OnCalculate(const int rates_total,
71.                 const int prev_calculated,
72.                 const int begin,
73.                 const double &price[])
74.   {
75.    return(rates_total);
76.   }
77. //+------------------------------------------------------------------+
78. //| ChartEvent function                                              |
79. //+------------------------------------------------------------------+
80. void OnChartEvent(const int id,
81.                   const long &lparam,
82.                   const double &dparam,
83.                   const string &sparam)
84.   {
85.    switch(id)
86.      {
87.       case CHARTEVENT_CHART_CHANGE:
88.          if(gl_Multi != NULL)
89.             gl_Multi.Resize();
90.          break;
91.      }
92.   }
93. //+------------------------------------------------------------------+
94. //| Custom indicator deinitialization function                       |
95. //+------------------------------------------------------------------+
96. void OnDeinit(const int reason)
97.   {
98.    delete gl_Multi;
99.   }
100.//+------------------------------------------------------------------+

Código 02

¡Qué maravilla es este código 02! Porque, con muy pocas modificaciones respecto al código 01, conseguimos implementar un método para indicar cuántos objetos de tipo OBJ_CHART queremos en la subventana. Y todo lo que hizo falta fue una sencilla modificación del código. Aun así, sigue produciendo el mismo resultado que el código 01. Ahora viene la cuestión: ¿cuándo debemos aplicar la plantilla? Una vez más, la respuesta es: depende. Vamos, eres realmente pesado. Para cada pregunta, la respuesta siempre es: depende, depende y depende. ¿No puedes ser un poco más directo? ¿Y decir cuál sería realmente el mejor momento para hacer las cosas? Aunque esto pueda parecer un poco extraño y molesto, la cuestión es que todo depende de lo que tengas pensado hacer. No existe una única forma correcta cuando se habla de programación.

Lo que existe es esto: funciona o no funciona. Pero no es posible definir cuál es el mejor momento para hacer las cosas sin entender qué objetivo se pretende alcanzar y cuánto trabajo y esfuerzo estás dispuesto a dedicar. Por eso, la mejor respuesta que alguien puede darte cuando se habla de programación es, precisamente: depende.

Volviendo al tema, como aquí queremos ser lo más directos posible y, en principio, no tenemos intención de modificar la plantilla aplicada a un determinado gráfico creado y mantenido por un objeto de tipo OBJ_CHART, podemos hacer que, durante el proceso de construcción, la plantilla se cargue y se aplique al gráfico del objeto. Como la modificación parte de lo que ya se hace en el código 02, no veo sentido en volver a reproducir todo el código. Así que el cambio que debemos realizar se muestra en el siguiente fragmento.

                                   .
                                   .
                                   .
15. 
16.    void              NewObj_CHART(const string szTemplate)
17.      {
18.       int            nObjects = ArraySize(m_szObject) + 1;
19.       string         sz0 = "Demo_Chart #" + (string)ObjectsTotal(0, -1, -1);
20.       long           id;
21. 
22.       ArrayResize(m_szObject, nObjects);
23.       m_szObject[nObjects - 1] = sz0;
24. 
25.       ObjectCreate(0, sz0, OBJ_CHART, ChartWindowFind(), 0, 0);
26.       ObjectSetString(0, sz0, OBJPROP_SYMBOL, _Symbol);
27.       ObjectSetInteger(0, sz0, OBJPROP_YDISTANCE, 0);
28.       ObjectSetInteger(0, sz0, OBJPROP_PERIOD, PERIOD_M15);
29.       ObjectSetInteger(0, sz0, OBJPROP_DATE_SCALE, false);
30.       ObjectSetInteger(0, sz0, OBJPROP_PRICE_SCALE, false);
31.       ObjectSetInteger(0, sz0, OBJPROP_YSIZE, indicator_height);
32. 
33.       id = ObjectGetInteger(0, sz0, OBJPROP_CHART_ID);
34.       ChartApplyTemplate(id, szTemplate);
35.       ChartRedraw(id);
36.      }
37. 
38. public:
39.                      C_MultiChart(const string &szTemplates[])
40.      {
41.       for(int c = 0; c < ArraySize(szTemplates); c++)
42.          NewObj_CHART(szTemplates[c]);
43.      }
44. 
                                   .
                                   .
                                   .

Fragmento 01

Ahora presta mucha atención, porque esto es muy importante. Observa que estamos pasando el nombre de la plantilla directamente mediante la llamada de la línea cuarenta y uno. En la línea treinta y uno de este fragmento 01, obtenemos el identificador del gráfico creado y contenido en el objeto OBJ_CHART. Este dato se utiliza en la línea treinta y dos para cargar y aplicar la plantilla al gráfico. En la línea treinta y tres garantizamos la primera actualización. Las siguientes se producirán cuando el marco temporal indique que es necesario volver a actualizar el gráfico, pero de ello se encargará MetaTrader 5. Y, aunque todo funciona, como puedes observar en la siguiente animación, debes saber que en este código no estamos realizando ninguna comprobación para detectar posibles errores.

Es decir, si especificas una plantilla que no existe, no se notificará ningún fallo. Esto se debe a que no estamos comprobando el resultado de la operación realizada en la línea treinta y dos, que es precisamente donde se aplica la plantilla al gráfico. Así que, para que todo funcione correctamente, debes seguir al pie de la letra lo que se ha mostrado aquí hasta que comprendas realmente cómo funciona todo. Solo después podrás aventurarte e intentar crear las cosas de una forma más exótica y elaborada. Hasta entonces, intenta utilizar únicamente lo que se ha mostrado aquí.



Animación 04

Este tipo de cosas resulta bastante interesante. Pero ¿qué tal si llevamos esto un poco más lejos? Puede que queramos utilizar algún indicador algo más complejo, como, por ejemplo, un estocástico u otro indicador que, por su propia naturaleza, utilice una subventana. Bien, en ese caso quizá necesitemos algunos ajustes adicionales, aunque en ciertas situaciones no harán falta.

Y es precisamente en estos momentos cuando empieza a surgir confusión entre muchas personas capaces. Pero eso ocurre porque no prestaron la debida atención a lo explicado hasta ahora. Si este es tu caso, detente, relájate un poco y vuelve al principio del artículo. Así podrás entender por qué el resultado obtenido al ejecutar el código 02 fue precisamente el que podemos ver en la animación 04. Cuando entiendas esto, podrás pasar al siguiente nivel, que consiste precisamente en utilizar indicadores que emplean de forma natural una subventana.

Para este tipo de indicador, la mayoría de las veces necesitamos realizar un paso adicional entre colocar el indicador en el gráfico y guardar la configuración como archivo de plantilla. Este paso adicional no es obligatorio, pero hace que los resultados sean más atractivos a largo plazo. Bien, suponiendo que queremos añadir el estocástico como indicador adicional, estos son los pasos que debemos seguir.


Animación 05

Ahora presta atención al siguiente detalle. Si guardas este gráfico tal como se muestra al final de la animación 05, se mostrará el gráfico del estocástico. Sin embargo, habrá un pequeño inconveniente: el gráfico también contendrá las cotizaciones. Pero espera un momento. No lo entiendo. ¿Cómo que también tendremos el gráfico de cotizaciones? Bien, para demostrarlo vamos a modificar algunas cosas en el código 02. Y no te preocupes: en el archivo adjunto tendrás acceso a estos códigos. Lo único que no incluiré son las plantillas, ya que podrían sobrescribir alguna plantilla que tengas en tu instalación de MetaTrader 5. No creo que nadie tenga problemas para reproducir lo que se muestra en el artículo. En cualquier caso, guarda la plantilla que aparece en la animación 05 con el nombre mostrado en la siguiente imagen.


Imagen 06

Hecho esto, volvamos al código. Para que las cosas resulten un poco menos incómodas, permitiremos que el usuario, u operador, cambie la altura de la subventana. Al mismo tiempo, cargaremos la plantilla de la imagen 06 en el indicador y la aplicaremos al gráfico del objeto OBJ_CHART. El código completo se muestra a continuación.

01. #property copyright "Daniel Jose"
02. #property description "DEMO Indicator"
03. #property indicator_separate_window
04. #property indicator_plots           0
05. //+------------------------------------------------------------------+
06. class C_MultiChart
07.   {
08. private:
09.    string            m_szObject[];
10.    int               m_Sub;
11. 
12.    void              NewObj_CHART(const string szTemplate)
13.      {
14.       int            nObjects = ArraySize(m_szObject) + 1;
15.       string         sz0 = "Demo_Chart #" + (string)ObjectsTotal(0, -1, -1);
16.       long           id;
17. 
18.       ArrayResize(m_szObject, nObjects);
19.       m_szObject[nObjects - 1] = sz0;
20. 
21.       ObjectCreate(0, sz0, OBJ_CHART, m_Sub = ChartWindowFind(), 0, 0);
22.       ObjectSetString(0, sz0, OBJPROP_SYMBOL, _Symbol);
23.       ObjectSetInteger(0, sz0, OBJPROP_YDISTANCE, 0);
24.       ObjectSetInteger(0, sz0, OBJPROP_PERIOD, PERIOD_M15);
25.       ObjectSetInteger(0, sz0, OBJPROP_DATE_SCALE, false);
26.       ObjectSetInteger(0, sz0, OBJPROP_PRICE_SCALE, false);
27. 
28.       id = ObjectGetInteger(0, sz0, OBJPROP_CHART_ID);
29.       ChartApplyTemplate(id, szTemplate);
30.       ChartRedraw(id);
31.      }
32. 
33. public:
34.                      C_MultiChart(const string &szTemplates[])
35.      {
36.       for(int c = 0; c < ArraySize(szTemplates); c++)
37.          NewObj_CHART(szTemplates[c]);
38.      }
39. 
40.    void              Resize(void)
41.      {
42.       const int      nObjects = ArraySize(m_szObject);
43.       const int      step = (int)(ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) /
44.                                   (nObjects > 0 ? nObjects : 1));
45. 
46.       for(int c = 0, x = 0; c < nObjects; c++, x += step)
47.         {
48.          ObjectSetInteger(0, m_szObject[c], OBJPROP_YSIZE,
49.                           ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, m_Sub));
50.          ObjectSetInteger(0, m_szObject[c], OBJPROP_XDISTANCE, x);
51.          ObjectSetInteger(0, m_szObject[c], OBJPROP_XSIZE, step);
52.         }
53.       ChartRedraw();
54.      }
55.   };
56. //+------------------------------------------------------------------+
57. C_MultiChart *gl_Multi = NULL;
58. //+------------------------------------------------------------------+
59. //| Custom indicator initialization function                         |
60. //+------------------------------------------------------------------+
61. int OnInit(void)
62.   {
63.    const string     szTemplates[] =
64.      {
65.       "Stochastic.tpl"
66.      };
67. 
68.    gl_Multi = new C_MultiChart(szTemplates);
69. 
70.    return(INIT_SUCCEEDED);
71.   }
72. //+------------------------------------------------------------------+
73. //| Custom indicator iteration function                              |
74. //+------------------------------------------------------------------+
75. int OnCalculate(const int rates_total,
76.                 const int prev_calculated,
77.                 const int begin,
78.                 const double &price[])
79.   {
80.    return(rates_total);
81.   }
82. //+------------------------------------------------------------------+
83. //| ChartEvent function                                               |
84. //+------------------------------------------------------------------+
85. void OnChartEvent(const int id,
86.                   const long &lparam,
87.                   const double &dparam,
88.                   const string &sparam)
89.   {
90.    switch(id)
91.      {
92.       case CHARTEVENT_CHART_CHANGE:
93.          if(gl_Multi != NULL)
94.             gl_Multi.Resize();
95.          break;
96.      }
97.   }
98. //+------------------------------------------------------------------+
99. //| Custom indicator deinitialization function                       |
100.//+------------------------------------------------------------------+
101. void OnDeinit(const int reason)
102.   {
103.    delete gl_Multi;
104.   }
105.//+------------------------------------------------------------------+

Código 03

Al ejecutar este código, obtendrás el resultado que puede verse en la siguiente animación.


Animación 06

Observa lo extraña que ha quedado la subventana. A pesar de todo, puedes notar que el código en sí no ha sufrido grandes modificaciones. Pero la subventana resulta realmente extraña, ya que contiene también las cotizaciones. Algo que no ocurriría si añadiéramos directamente aquí el indicador estocástico. Sin embargo, si añadiéramos directamente el indicador estocástico, no podríamos colocar dos indicadores en una misma subventana. Así que, únicamente con fines didácticos, vamos a crear una segunda configuración para este indicador estocástico. Para ello seguiremos una serie de pequeños pasos, empezando por lo que se muestra en la siguiente animación.


Animación 07

Aquí estamos cargando la plantilla que utilizamos en el código 03. Esto deja la configuración lista para el siguiente paso. Una vez hecho esto, procedemos como se muestra en la animación siguiente.


Animación 08

Este paso realizado en la animación 08 es la clave. Una vez hecho lo que se muestra en la animación, podemos pasar al siguiente y último paso, que consiste precisamente en guardar esta configuración en una plantilla. En este caso, tendremos algo como lo que se muestra en la siguiente imagen.


Imagen 07

Con todo esto preparado, podemos volver al código 03 y modificarlo como se muestra en el siguiente fragmento. No olvides que no estamos comprobando posibles fallos de carga. Así que verifica los nombres utilizados para evitar errores posteriores.

                                   .
                                   .
                                   .
58. //+------------------------------------------------------------------+
59. //| Custom indicator initialization function                         |
60. //+------------------------------------------------------------------+
61. int OnInit(void)
62.   {
63.    const string     szTemplates[] =
64.      {
65.       "Stochastic.tpl",
66.       "Stochastic FULL.tpl"
67.      };
68. 
69.    gl_Multi = new C_MultiChart(szTemplates);
70. 
71.    return(INIT_SUCCEEDED);
72.   }
73. //+------------------------------------------------------------------+
                                   .
                                   .
                                   .

Fragmento 02

Observa lo fácil que ha sido modificar el código, ya que lo único que necesitamos hacer es añadir el nombre de la plantilla. Esto se hace en la línea sesenta y cuatro. El resultado, sin embargo, es muy, pero que muy interesante, como puedes ver en la siguiente animación.


Animación 09

Y, como puede que la animación no sea suficiente para apreciar hasta el más mínimo detalle, podemos detenernos y observar esos detalles en la siguiente imagen.


Imagen 08

Es decir, ahora tenemos entre manos algo realmente magnífico. Pero, para saber cómo utilizarlo, necesitas practicar e intentar seguir los pasos explicados aquí.


Consideraciones finales

Finalmente, hemos explicado adecuadamente algo que mostré en uno de mis primeros artículos. Aquí se ha puesto todo el énfasis en el aspecto didáctico, mostrando cómo podemos aplicar múltiples indicadores en una misma subventana. Sé que a muchos puede parecerles extraño o incluso completamente absurdo, ya que no es algo que veamos hacer habitualmente. Sin embargo, este tipo de conocimiento marca la diferencia entre saber y creer que ya se sabe.

Espero que hayas podido seguir la explicación. Como ya mencioné, en el archivo adjunto NO HABRÁ PLANTILLAS, pero, siguiendo las indicaciones dadas aquí, cualquiera podrá conseguir resultados bastante interesantes. Recuerda que los códigos disponibles en el archivo adjunto no realizarán ninguna comprobación para detectar posibles errores. Por ejemplo, si intentas utilizar un archivo de plantilla que no existe. Dejo que cada uno implemente este tipo de comprobación de la forma que considere más adecuada.

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


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

Archivos adjuntos |
Red neuronal en la práctica: Lectura de PNG del MNIST Red neuronal en la práctica: Lectura de PNG del MNIST
Aquí veremos cómo implementar un algoritmo sencillo para leer imágenes en formato PNG. Poder leer el contenido de estas imágenes será importante para avanzar en el desarrollo de una topología de red perceptrón. Esto se debe a que comenzaremos a utilizar la base de datos MNIST para entrenar la red que implementaremos.
Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 12): Construcción de clasificadores lineales mediante factorización matricial Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 12): Construcción de clasificadores lineales mediante factorización matricial
Este artículo explora el importante papel de la factorización matricial en el trading algorítmico, específicamente en aplicaciones MQL5. Desde modelos de regresión hasta clasificadores de múltiples objetivos, analizamos ejemplos prácticos que demuestran la facilidad con la que estas técnicas pueden integrarse utilizando las funciones integradas de MQL5. Ya sea que esté prediciendo la dirección de los precios o modelando el comportamiento de los indicadores, esta guía sienta una base sólida para construir sistemas de trading inteligentes utilizando métodos matriciales.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Del nivel básico al intermedio: Archivo de plantilla (II) Del nivel básico al intermedio: Archivo de plantilla (II)
El artículo muestra cómo aplicar indicadores a gráficos creados mediante el objeto OBJ_CHART utilizando plantillas, cuando ChartIndicatorAdd no funciona en este contexto. Explicamos cómo guardar una plantilla con el indicador, obtener el ID del gráfico y aplicar ChartApplyTemplate al destino correcto. Aprenderás a integrar indicadores como recurso, automatizar la configuración del gráfico incrustado y comprender algunas limitaciones prácticas, como el hecho de que los indicadores no aparezcan en la ventana estándar.