Del nivel básico al intermedio: Archivo de plantilla (III)
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.
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.

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.

Animación 02

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

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

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.

Animación 05

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

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

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
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: Lectura de PNG del MNIST
Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 12): Construcción de clasificadores lineales mediante factorización matricial
Particularidades del trabajo con números del tipo double en MQL4
Del nivel básico al intermedio: Archivo de plantilla (II)
- 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