Del nivel básico al intermedio: Archivo de plantilla (II)
Introducción
En el artículo anterior Del nivel básico al intermedio: Archivo de plantilla (I), comenzamos a profundizar en algo que, en general, ha sido muy poco explorado por buena parte de los programadores de MQL5. Muchas personas no tienen una idea clara de hasta qué punto MQL5 puede hacer que MetaTrader 5 sea algo más interactivo con mucho menos esfuerzo. Buena parte de los usuarios solo ve en MetaTrader 5 lo que se observa al instalar la plataforma. Por esta razón, muchos no tienen idea de hasta qué punto MetaTrader 5 es altamente personalizable, siempre que, por supuesto, te esfuerces y dediques tiempo a estudiar algunos detalles de la propia plataforma.
No culpo a nadie por no conseguir aprovechar mejor la plataforma, ya que muchos solo ven lo que resulta obvio y habitual. Es decir, la mayoría de las personas imaginan que únicamente podemos programar indicadores y Asesores Expertos. Ni siquiera esos programas pueden generar ciertas configuraciones, dado que la plataforma no nos permite hacer ciertas cosas. Este tipo de pensamiento, tan limitado y rígido, no es propio de un verdadero programador o, como mínimo, de alguien con una visión más amplia de lo que tiene a su alrededor. Para conseguir que MetaTrader 5 sea mucho más de lo que parece justo después de instalarlo, necesitas entender cómo utilizar MQL5. La clave que abre las puertas del paraíso se encuentra precisamente aquí.
Muy bien, como el contenido que veremos puede resultar un tanto confuso, te pido que ahora dejes de lado cualquier distracción. Así podrás concentrarte por completo en lo que se explicará en este artículo. Aquí veremos cómo utilizar plantillas para acceder a determinadas «funcionalidades» con mucha menos programación.Archivo de plantilla (II)
En el artículo anterior mostré cómo podíamos copiar partes de la configuración del gráfico principal a un gráfico que se mostraría en el objeto OBJ_CHART. Creo que has estudiado y realizado algunos experimentos con lo que vimos. Sin embargo, esa es solo la parte aburrida y poco interesante. La diversión comienza realmente cuando empezamos a explorar los archivos de configuración con mayor profundidad.
Antes de hacer esto, necesito poner un poco las cosas en contexto, ya que la experiencia de cada uno puede variar bastante. Mientras que muchos pueden tener amplios conocimientos sobre archivos de configuración, otros no tienen la menor idea de cómo interpretar la información que contienen. Así que, antes de comenzar, necesitamos entender cómo configurar esos elementos directamente en MQL5. Esto se debe a que hay cosas que podemos hacer directamente mediante archivos de configuración, mientras que otras solo pueden hacerse mediante programación en MQL5. Solo con el tiempo, la experiencia y mucha práctica podrás determinar cuándo, dónde y por qué conviene utilizar uno u otro enfoque. No estoy aquí para decirte: haz esto, no hagas aquello. Lo que quiero mostrarte es que puedes hacerlo de esta manera o de aquella otra. La decisión de utilizar una metodología u otra depende única y exclusivamente de ti; es una elección personal.
En artículos anteriores vimos cómo integrar indicadores en otros códigos, convirtiéndolos así en recursos internos de esos códigos. Hasta ahora, esto solo se ha hecho en código de indicadores. Aun así, ya disponemos de un mecanismo que podemos utilizar. También vimos que podemos manipular el propio objeto OBJ_CHART como si fuera una ventana gráfica. Entonces, ¿qué te parece si unimos ambas cosas en una implementación mucho más interesante? Sin embargo, debo recordarte que el conocimiento es acumulativo. Y, como ya hemos hablado de clases, aunque de manera muy superficial, podemos comenzar a implementar la solución con conceptos que ya hemos explorado aquí. Como todo lo que haremos ya se explicó en algún momento, no perderé tiempo explicando código sencillo. Así que vamos a comenzar con el código que se muestra íntegramente 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_Chart 10. { 11. private : 12. //+----------------+ 13. string m_szObject; 14. //+----------------+ 15. public : 16. //+----------------+ 17. C_Chart() 18. { 19. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 20. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 21. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 22. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 23. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 24. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 25. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 26. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 27. } 28. //+----------------+ 29. ~C_Chart() 30. { 31. ObjectDelete(0, m_szObject); 32. ChartRedraw(); 33. } 34. //+----------------+ 35. void Resize(void) 36. { 37. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 38. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 39. ChartRedraw(); 40. } 41. //+----------------+ 42. }*gl_Chart = NULL; 43. //+------------------------------------------------------------------+ 44. int OnInit(void) 45. { 46. gl_Chart = new C_Chart(); 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. //+------------------------------------------------------------------+ 56. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 57. { 58. switch(id) 59. { 60. case CHARTEVENT_CHART_CHANGE: 61. if (gl_Chart) (*gl_Chart).Resize(); 62. break; 63. } 64. }; 65. //+------------------------------------------------------------------+ 66. void OnDeinit(const int reason) 67. { 68. delete gl_Chart; 69. }; 70. //+------------------------------------------------------------------+
Código 01
Al ejecutar este código 01, obtenemos el resultado que se muestra en la siguiente imagen.

Imagen 01
En este punto, creo que todos pueden entender lo que está ocurriendo, ya que el contenido es bastante reciente y, por tanto, debería seguir fresco en esas mentes sedientas de conocimiento. Al menos eso espero. Ahora vamos a mantener este indicador, mostrado en el código 01, en el gráfico y vamos a crear un nuevo código. Se muestra a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_applied_price PRICE_CLOSE 08. #property indicator_buffers 1 09. #property indicator_plots 1 10. #property indicator_type1 DRAW_LINE 11. #property indicator_color1 clrRed 12. #property indicator_style1 STYLE_SOLID 13. #property indicator_width1 3 14. #property indicator_label1 "EMA" 15. //+------------------------------------------------------------------+ 16. input uint def_nPeriods = 9; //Period: 17. input ENUM_MA_METHOD def_MA_Method = MODE_EMA; //Method: 18. //+------------------------------------------------------------------+ 19. double Buff_iMA[]; 20. int Handle; 21. //+------------------------------------------------------------------+ 22. int OnInit(void) 23. { 24. SetIndexBuffer(0, Buff_iMA, INDICATOR_DATA); 25. IndicatorSetString(INDICATOR_SHORTNAME, EnumToString(def_MA_Method) + "_" + (string)def_nPeriods + "_" + EnumToString((ENUM_APPLIED_PRICE)_AppliedTo)); 26. if ((Handle = iMA(NULL, 0, def_nPeriods, 0, def_MA_Method, _AppliedTo)) == INVALID_HANDLE) 27. { 28. Print("Could not start the indicator..."); 29. return INIT_FAILED; 30. }; 31. 32. return INIT_SUCCEEDED; 33. }; 34. //+------------------------------------------------------------------+ 35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 36. { 37. ArrayInitialize(Buff_iMA, EMPTY_VALUE); 38. CopyBuffer(Handle, 0, 0, def_nPeriods, Buff_iMA); 39. 40. return rates_total; 41. }; 42. //+------------------------------------------------------------------+ 43. void OnDeinit(const int reason) 44. { 45. if (Handle != INVALID_HANDLE) 46. IndicatorRelease(Handle); 47. }; 48. //+------------------------------------------------------------------+
Código 02
Este código 02 tampoco debería ser difícil de entender, aunque la única novedad es cómo la línea veinticinco genera el nombre de la aplicación. Pero esto no debería suponer ningún problema, al menos para quienes han venido estudiando el contenido de los artículos. Así que no veo motivos para entrar en más detalles y podemos pasar directamente a la parte práctica. Ahora, si pedimos a MetaTrader 5 que añada también este código 02 al gráfico mostrado en la imagen 01, obtendremos el resultado que puede verse en la imagen siguiente.

Imagen 02
Todo dentro de lo previsto y sin ninguna sorpresa. Todos sabemos ya que podemos unir dos códigos, haciendo que uno sea reconocido como recurso interno de otro código más elaborado. Así, estos códigos 01 y 02 pueden compilarse por separado e integrarse posteriormente en un tercer código, pasando a ser recursos internos de este tercer código. Y, para no complicar las cosas y mantenernos en lo que podemos considerar un nivel adecuado y didáctico, vamos a hacer algo un poco diferente: convertir el código 02 en un recurso interno del código 01. De este modo nace el código que podemos ver 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_Chart 10. { 11. private : 12. //+----------------+ 13. string m_szObject; 14. //+----------------+ 15. public : 16. //+----------------+ 17. C_Chart() 18. { 19. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 20. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 21. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 22. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 23. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 24. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 25. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 26. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 27. } 28. //+----------------+ 29. ~C_Chart() 30. { 31. ObjectDelete(0, m_szObject); 32. ChartRedraw(); 33. } 34. //+----------------+ 35. void Resize(void) 36. { 37. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 38. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 39. ChartRedraw(); 40. } 41. //+----------------+ 42. }*gl_Chart = NULL; 43. //+------------------------------------------------------------------+ 44. #define def_Resource "Code 02.ex5" 45. //+------------------------------------------------------------------+ 46. #resource def_Resource 47. //+------------------------------------------------------------------+ 48. int OnInit(void) 49. { 50. gl_Chart = new C_Chart(); 51. ChartIndicatorAdd(0, 0, iCustom(NULL, NULL, "::" + def_Resource)); 52. 53. return INIT_SUCCEEDED; 54. }; 55. //+------------------------------------------------------------------+ 56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 57. { 58. return rates_total; 59. }; 60. //+------------------------------------------------------------------+ 61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 62. { 63. switch(id) 64. { 65. case CHARTEVENT_CHART_CHANGE: 66. if (gl_Chart) (*gl_Chart).Resize(); 67. break; 68. } 69. }; 70. //+------------------------------------------------------------------+ 71. void OnDeinit(const int reason) 72. { 73. delete gl_Chart; 74. }; 75. //+------------------------------------------------------------------+
Código 03
Este código 03 es un tanto tosco. Puede y debe refinarse para que resulte algo más elegante. Sin embargo, para lo que quiero explicar, es adecuado. Ahora presta atención: si no puedes entender qué hace este código 03 con solo mirarlo, te pido que leas y estudies los artículos anteriores. Entender qué hace este código 03 con solo observarlo es muy importante aquí. No pienses que ya lo sabes simplemente porque crees saber cómo funcionan las cosas. Es realmente necesario comprender el funcionamiento de este código 03 para entender el resto del artículo.
Para quienes saben qué hace este código 03, no es necesario ver el resultado de su ejecución, ya que será el mismo que puede verse en la imagen 02. Por lo tanto, no voy a volver a mostrar la misma imagen. Es aquí donde las cosas empiezan a ponerse interesantes.
Observa atentamente la línea cincuenta y uno del código 03. En el artículo anterior vimos que podíamos copiar datos de configuración del gráfico principal al gráfico creado por un objeto OBJ_CHART. Vimos que esto era posible precisamente gracias a un detalle que debemos implementar en nuestro código. Podíamos pedir a MetaTrader 5 el identificador del gráfico del objeto OBJ_CHART. Al hacerlo, podíamos utilizar las mismas llamadas que empleábamos para ajustar la configuración del gráfico principal y ajustar también la del gráfico del objeto OBJ_CHART. Hasta aquí, creo que está todo claro.
Ahora viene la parte interesante: el primer argumento de la llamada de la línea cincuenta y uno es precisamente el identificador del gráfico al que se añadirá el indicador. Si no lo has entendido, consulta la documentación para obtener más detalles. Ahora surge la cuestión: ¿qué ocurre si obtenemos el identificador del gráfico creado y mantenido por el objeto OBJ_CHART y lo pasamos como primer argumento de la línea cincuenta y uno? Recuerda que el primer argumento es el identificador del gráfico en el que debe colocarse el indicador. Podrías decir: bien, creo que MetaTrader 5 colocará en el gráfico creado por el objeto OBJ_CHART el indicador especificado en la línea cincuenta y uno. Y, en realidad, no estarías del todo equivocado, ya que esa respuesta demostraría que has comprendido cómo funciona todo hasta este punto. Sin embargo, veamos si esto ocurre en la práctica. Para ello, modificamos el código 03 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_Resource "Code 02.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. class C_Chart 14. { 15. private : 16. //+----------------+ 17. string m_szObject; 18. //+----------------+ 19. void Proc(long id) 20. { 21. Print("ID informed: ", id); 22. Print("Result: ", ChartIndicatorAdd(id, 0, iCustom(NULL, NULL, "::" + def_Resource))); 23. ChartRedraw(id); 24. } 25. //+----------------+ 26. public : 27. //+----------------+ 28. C_Chart() 29. { 30. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 31. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 32. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 33. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 34. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 35. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 36. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 37. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 38. } 39. //+----------------+ 40. ~C_Chart() 41. { 42. ObjectDelete(0, m_szObject); 43. ChartRedraw(); 44. } 45. //+----------------+ 46. void Resize(void) 47. { 48. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 49. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 50. ChartRedraw(); 51. } 52. //+----------------+ 53. void AddIndicator() 54. { 55. Proc(ObjectGetInteger(0, m_szObject, OBJPROP_CHART_ID)); 56. Proc(ChartID()); 57. } 58. //+----------------+ 59. }*gl_Chart = NULL; 60. //+------------------------------------------------------------------+ 61. int OnInit(void) 62. { 63. gl_Chart = new C_Chart(); 64. (*gl_Chart).AddIndicator(); 65. 66. return INIT_SUCCEEDED; 67. }; 68. //+------------------------------------------------------------------+ 69. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 70. { 71. return rates_total; 72. }; 73. //+------------------------------------------------------------------+ 74. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 75. { 76. switch(id) 77. { 78. case CHARTEVENT_CHART_CHANGE: 79. if (gl_Chart) (*gl_Chart).Resize(); 80. break; 81. } 82. }; 83. //+------------------------------------------------------------------+ 84. void OnDeinit(const int reason) 85. { 86. delete gl_Chart; 87. }; 88. //+------------------------------------------------------------------+
Código 04
Ahora presta atención, porque entender esta explicación te ayudará a comprender por qué tendremos que hacer las cosas de otra manera para conseguir el resultado previsto. Pero, antes de hacerlo, veamos el resultado de ejecutar el código 04.

Imagen 03
En esta imagen estoy delimitando dos regiones. Cada una muestra, mediante una flecha, el origen de los datos destacados. Para entender correctamente estos datos, tendremos que volver al código 04. En la línea sesenta y cuatro tenemos una llamada a un procedimiento interno de la clase. Lo hacemos así para no tener que extraer o, mejor dicho, buscar ningún dato dentro de la clase. Simplemente utilizaremos los datos que contiene. Dentro del procedimiento llamado tenemos dos líneas que ejecutarán la lógica implementada en el procedimiento de la línea diecinueve. Esta es la parte importante del código. Observa que la única diferencia entre las líneas cincuenta y cinco y cincuenta y seis es precisamente el tipo de argumento que se pasará.
Pero ¿qué tipo de dato estamos pasando aquí? Bien, si entendiste el artículo anterior, sabes que en esta línea cincuenta y cinco estamos obteniendo el identificador del gráfico del objeto OBJ_CHART. En la línea cincuenta y seis, en cambio, obtenemos el identificador del gráfico principal. En resumen, no importa lo que haga el procedimiento de la línea diecinueve. En ambos casos estamos pasando al procedimiento el dato correcto y necesario. Observa que, en la línea veintiuno, imprimimos el valor del identificador proporcionado. Sin embargo, la línea veintidós es la que realmente resulta importante.
En los artículos en los que hablamos de integrar un indicador en otro código, vimos una de las formas de hacerlo. Una de ellas es precisamente la que estamos utilizando aquí: la función de biblioteca ChartIndicatorAdd. Observa que, si la llamada a la función se ejecuta correctamente en MetaTrader 5, se imprimirá esta confirmación en el terminal. Si la función falla por cualquier motivo, también se imprimirá un mensaje en el terminal. Sin embargo, necesito que quede claro que la única diferencia durante la ejecución es que, en un caso, apuntamos al gráfico del objeto OBJ_CHART y, en el otro, al gráfico principal. Y con esto obtenemos lo que se muestra en la imagen 03. Allí puede verse que, cuando intentamos añadir un indicador al gráfico del objeto OBJ_CHART, la función falla. Pero si intentamos hacer lo mismo apuntando al gráfico principal, la función tiene éxito.
Esto significa que no podemos añadir un indicador a un gráfico creado y mantenido por un objeto OBJ_CHART. Pero espera, esto no es del todo cierto. Si revisas otros artículos de mi autoría, comprobarás que, de alguna manera, conseguía añadir un indicador a un gráfico creado y mantenido por un objeto OBJ_CHART. ¿Cómo es posible? En realidad, sí es posible añadir un indicador, o varios si así lo deseas, a un gráfico creado y mantenido por un objeto OBJ_CHART.
Sin embargo, los mecanismos utilizados no son los que estamos viendo en este momento. El hecho de que no encuentres referencias sobre cómo hacerlo se debe precisamente a lo que acabas de observar en este código 04. Muchos programadores imaginan que podrían utilizar el mecanismo mostrado en el código para añadir uno o más indicadores al gráfico del objeto OBJ_CHART. Sin embargo, cualquier intento de hacerlo de la manera que se ha mostrado aquí fracasará estrepitosamente. Y es en este punto donde comenzaremos a trabajar con los archivos de configuración.
Ahora seguiremos un camino completamente diferente. Sin embargo, el resultado será muy interesante. Además, por supuesto, nos permitirá crear casi cualquier cosa que podamos imaginar.
Ahora olvida la programación por un momento. Antes vamos a entender otra cosa. Muchos operadores y usuarios de plataformas de trading, como MetaTrader 5, disponen de distintas configuraciones para diferentes setups, lo que les permite cambiar rápidamente la configuración del gráfico cuando lo necesitan. Dentro de estos archivos de configuración podemos incorporar diversas cosas. Pero, por ahora, nos quedaremos únicamente con lo básico.
Entre estos elementos figuran listas de indicadores, tanto para la ventana principal como para subventanas, entre otras cosas. Creo que incluso tú ya has creado este tipo de configuraciones gráficas. Pero, en general, no se limitan a aplicarse solo al gráfico principal. Si tomamos ciertas precauciones, también podemos aplicarlas a un objeto OBJ_CHART.
Vamos, ¿qué locura es esa que me estás contando? Si lo he entendido bien, estás diciendo que podemos crear una de esas configuraciones utilizando varios indicadores y después añadirla a un objeto OBJ_CHART. ¿Es eso? Sí, mi querido lector. Eso es exactamente lo que has entendido. Esos archivos de configuración, siempre que tomemos ciertas precauciones, pueden aplicarse a un objeto OBJ_CHART. Y, para demostrar que esto es cierto, vamos a ver paso a paso cómo hacerlo. Un detalle: aquí asumiré que no tienes la menor idea de cómo crear y acceder a un archivo de configuración. Así que veamos cómo hacerlo.
En primer lugar, utilizaremos un gráfico lo más limpio posible. Es decir, no tenemos ningún indicador en él. El resultado será el que se muestra a continuación.

Imagen 04
Perfecto. Ahora añadimos un solo indicador a este gráfico. En este caso, añadimos el indicador creado en el código 02, ya que el objetivo es demostrar cómo hacer las cosas en la práctica. Así, obtenemos el resultado que se muestra a continuación.

Animación 01
Muy bien, ahora presta atención, porque cada caso es diferente. En algunos casos tendremos que manipular la ventana antes incluso de guardar la configuración que acabamos de crear, como puede verse en la animación 01. En otros casos no tendremos que modificar nada más y podremos guardar inmediatamente la configuración que acabamos de crear. Y este es uno de esos casos, en los que podemos guardar la configuración justo después de crearla. Para hacerlo, seleccionamos lo que se muestra en la siguiente imagen.

Imagen 05
Esto abrirá la ventana en la que podremos introducir el nombre del archivo de plantilla. Puedes darle cualquier nombre. Sin embargo, como estás aprendiendo a hacer las cosas, te sugiero utilizar un nombre fácil de recordar, para que después recuerdes por qué se creó la plantilla. Así, al final, tendremos lo que se muestra en la siguiente imagen.

Imagen 06
Ahora tenemos una nueva plantilla disponible, que aparece en esta región delimitada en verde en la imagen 06. Siempre que crees una nueva plantilla, aparecerá aquí y podrá aplicarse a cualquier gráfico cuando sea necesario. Sin embargo, es importante que entiendas lo siguiente: una plantilla solo se aplicará por completo si todos los códigos incluidos en ella están disponibles en el momento de aplicarla al gráfico. Pero, como aquí solo estamos practicando, no hay efectos secundarios de los que debamos preocuparnos. Una vez hecho esto, estamos listos para la segunda parte. Y esta vez nos centraremos en la programación en MQL5.
Para que resulte más fácil de entender, haremos un pequeño experimento. Ya sabemos que el código 04 no puede realizar la tarea que queremos llevar a cabo. Eso es un hecho. Sin embargo, si lo modificamos de forma controlada, podemos hacer que genere el resultado que buscamos. Como quiero que entiendas cómo funcionan las cosas, no haremos el cambio de una sola vez. Primero crearemos un código para demostrar el resultado que queremos obtener. Aunque la implementación no resulte especialmente útil para el día a día, servirá para que entiendas el concepto que vamos a utilizar. Para ello, emplearemos 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. int OnInit(void) 10. { 11. ChartApplyTemplate(0, "Testing.tpl"); 12. 13. return INIT_SUCCEEDED; 14. }; 15. //+------------------------------------------------------------------+ 16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 17. { 18. return rates_total; 19. }; 20. //+------------------------------------------------------------------+
Código 05
Este código 05 servirá perfectamente para que entiendas algo importante sobre las plantillas. Observa que el código es realmente muy sencillo. Sin embargo, esta línea once tiene una gran influencia en el resultado que obtendremos al aplicar el código a un gráfico. En esta línea estamos indicando a MetaTrader 5 que aplique al gráfico la plantilla que acabamos de guardar. Ahora presta mucha atención, porque esto es realmente muy importante. Cuando MetaTrader 5 aplique la plantilla al gráfico, hará que se descarte cualquier configuración que tuviera el gráfico antes de ejecutar la línea once. Por lo tanto, incluso el indicador creado mediante este código será eliminado. Es decir, en cuanto la plantilla quede activa, este código dejará de estar presente. Para entenderlo, observa la siguiente animación.

Animación 02
Observa que el indicador tiene como nombre CODE 05. Sin embargo, debido a la línea once, cuando comprobemos qué indicadores están presentes en el gráfico, veremos que no se trata de CODE 05, sino de CODE 02. ¿Por qué? Precisamente porque, al aplicar la plantilla en la línea once del código 05, dicho código será eliminado del gráfico. Y cualquier configuración del gráfico, incluidos los indicadores o el Asesor Experto que estén presentes, se eliminará y, en su lugar, se aplicará la configuración incluida en el archivo de plantilla.
Esta es la parte conceptual que necesitas comprender. Existen algunas pequeñas limitaciones al utilizar este concepto. Sin embargo, esas limitaciones no nos impedirán hacer lo que queremos en este momento. Ahora viene la parte divertida. La función ChartApplyTemplate necesita dos parámetros. El segundo parámetro, obviamente, se refiere a la plantilla que se aplicará al gráfico. Pero ¿qué ocurre con el primer parámetro? ¿A qué se refiere? Bien, este primer parámetro indica el identificador del gráfico al que se aplicará la plantilla. ¿Y por qué puede considerarse esta la parte divertida?
La razón es sencilla. ¿Recuerdas la demostración de que el código 04 NO PUEDE añadir un indicador al gráfico creado y mantenido por un objeto OBJ_CHART? Pues bien, si modificamos el código 04 para poder utilizar lo que aparece en la línea once de este código 05, podremos manipular, aunque con algunas limitaciones, ciertos aspectos del gráfico del objeto OBJ_CHART. Y, como el código 05 funcionó y solo queremos trasladar el mismo concepto al código 04, modificaremos este último para que quede 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_Resource "Code 02.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. class C_Chart 14. { 15. private : 16. //+----------------+ 17. string m_szObject; 18. //+----------------+ 19. void Proc(long id) 20. { 21. Print("ID informed: ", id); 22. Print("Result: ", ChartIndicatorAdd(id, 0, iCustom(NULL, NULL, "::" + def_Resource))); 23. ChartApplyTemplate(id, "Testing.tpl"); 24. ChartRedraw(id); 25. } 26. //+----------------+ 27. public : 28. //+----------------+ 29. C_Chart() 30. { 31. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 32. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 33. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 34. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 35. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 36. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 37. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 38. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 39. } 40. //+----------------+ 41. ~C_Chart() 42. { 43. ObjectDelete(0, m_szObject); 44. ChartRedraw(); 45. } 46. //+----------------+ 47. void Resize(void) 48. { 49. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 50. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 51. ChartRedraw(); 52. } 53. //+----------------+ 54. void AddIndicator() 55. { 56. Proc(ObjectGetInteger(0, m_szObject, OBJPROP_CHART_ID)); 57. Proc(ChartID()); 58. } 59. //+----------------+ 60. }*gl_Chart = NULL; 61. //+------------------------------------------------------------------+ 62. int OnInit(void) 63. { 64. gl_Chart = new C_Chart(); 65. (*gl_Chart).AddIndicator(); 66. 67. return INIT_SUCCEEDED; 68. }; 69. //+------------------------------------------------------------------+ 70. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 71. { 72. return rates_total; 73. }; 74. //+------------------------------------------------------------------+ 75. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 76. { 77. switch(id) 78. { 79. case CHARTEVENT_CHART_CHANGE: 80. if (gl_Chart) (*gl_Chart).Resize(); 81. break; 82. } 83. }; 84. //+------------------------------------------------------------------+ 85. void OnDeinit(const int reason) 86. { 87. delete gl_Chart; 88. }; 89. //+------------------------------------------------------------------+
Código 06
Ahora presta atención, porque esto suele traer de cabeza a mucha gente. Lo único que se modificó en el código 04 para crear este código 06 fue añadir la línea veintitrés. Sin embargo, debes recordar lo que ocurrió cuando se ejecutó el código 05. Entender esto es de suma importancia. Aunque este código 06 intenta utilizar un concepto que realmente puede aplicarse, hacer las cosas como se muestra aquí no producirá el resultado que esperas. Es decir, aunque todo parezca perfecto y totalmente funcional.
Al final, este código 06, con toda su complejidad, producirá el mismo resultado que obtendríamos con un código considerablemente más sencillo. Esto se debe a que, cuando se ejecute, el resultado será el mismo que obtendremos al ejecutar el código 05. Entonces, ¿cómo podemos hacer que las cosas funcionen como queremos? ¿Cómo podemos incluir un indicador en un gráfico creado y mantenido por un objeto OBJ_CHART? Bien, esta es la parte confusa que muchos no comprenden. Y la respuesta es:
No intentes crear algo genérico. Para obtener resultados específicos, tendrás que utilizar mecanismos específicos.
¿Y qué quiere decirnos esta frase? Nos dice que, para cada tipo de problema, necesitamos una solución capaz de resolver específicamente ese problema y solo ese. Muchos principiantes tienen la costumbre de copiar y pegar fragmentos de código en sus propios programas, y esto se ha agravado con el uso de inteligencia artificial para programar. Aunque esto puede funcionar, muchas veces este enfoque fracasa estrepitosamente cuando necesitamos que las cosas funcionen de una manera muy específica.
Por esta razón, a pesar del entusiasmo de mucha gente con respecto a las llamadas inteligencias artificiales para ayudar en la programación, estos sistemas resultan poco viables. Esto se debe a que tienden a crear soluciones genéricas. Y, cuando necesitamos algo específico, esos sistemas no pasan de ser una pérdida de tiempo. Si no entiendes el concepto que debes utilizar, acabarás en un callejón sin salida, esperando que esos modelos GPT generen una respuesta adecuada y resuelvan tu problema. Algo que no ocurrirá.
Entonces, ¿cómo podemos resolver la cuestión planteada en este artículo? Para conseguirlo, tendremos que abandonar la esperanza de utilizar cualquier GPT para crear código y dedicarnos a estudiar y practicar programación como se hacía antes. Sé que esto puede parecer una tarea cansada y tediosa. Pero, al final, conseguirás identificar la solución mientras los demás siguen intentando entender por qué funciona o por qué no funciona.
El problema del código 06 no está en el uso de la línea veintitrés en sí. El problema está en la interacción del propio código consigo mismo. Pero ¿cómo es eso? Ahora sí que estoy confundido. Bien, mi querido lector, ¿por qué se ejecuta la línea cincuenta y siete de este código 06? Se ejecuta precisamente para que, en la línea veintidós, el indicador integrado se añada al gráfico. Sin embargo, si ese es el objetivo, ¿por qué, aun sabiendo que el indicador integrado se colocará en el gráfico, necesitamos ejecutar también la línea veintitrés? Esto no tiene mucho sentido. La línea veintidós se ejecuta correctamente precisamente porque estamos añadiendo un recurso integrado en el código, en este caso, el indicador CODE 02, como puedes comprobar en la línea once. Una vez más, doy por hecho que has leído y comprendido los artículos anteriores para entender de qué estoy hablando en este momento.
Sabiendo que la línea veintidós se ejecutará correctamente cuando ejecutemos esta lógica sobre el gráfico principal, no tiene sentido utilizar la línea veintitrés para aplicar la plantilla, ya que esta eliminará el código del gráfico e impedirá que el gráfico creado por el objeto OBJ_CHART reciba la plantilla que queremos utilizar, ¿verdad? Así que, únicamente para demostrar que este código 06 funciona, lo modificamos 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_Resource "Code 02.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. class C_Chart 14. { 15. private : 16. //+----------------+ 17. string m_szObject; 18. //+----------------+ 19. void Proc(long id) 20. { 21. if (!ChartIndicatorAdd(id, 0, iCustom(NULL, NULL, "::" + def_Resource))) 22. ChartApplyTemplate(id, "Testing.tpl"); 23. ChartRedraw(id); 24. } 25. //+----------------+ 26. public : 27. //+----------------+ 28. C_Chart() 29. { 30. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 31. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 32. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 33. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 34. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 35. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 36. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 37. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 38. } 39. //+----------------+ 40. ~C_Chart() 41. { 42. ObjectDelete(0, m_szObject); 43. ChartRedraw(); 44. } 45. //+----------------+ 46. void Resize(void) 47. { 48. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 49. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 50. ChartRedraw(); 51. } 52. //+----------------+ 53. void AddIndicator() 54. { 55. Proc(ObjectGetInteger(0, m_szObject, OBJPROP_CHART_ID)); 56. Proc(ChartID()); 57. } 58. //+----------------+ 59. }*gl_Chart = NULL; 60. //+------------------------------------------------------------------+ 61. int OnInit(void) 62. { 63. gl_Chart = new C_Chart(); 64. (*gl_Chart).AddIndicator(); 65. 66. return INIT_SUCCEEDED; 67. }; 68. //+------------------------------------------------------------------+ 69. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 70. { 71. return rates_total; 72. }; 73. //+------------------------------------------------------------------+ 74. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 75. { 76. switch(id) 77. { 78. case CHARTEVENT_CHART_CHANGE: 79. if (gl_Chart) (*gl_Chart).Resize(); 80. break; 81. } 82. }; 83. //+------------------------------------------------------------------+ 84. void OnDeinit(const int reason) 85. { 86. delete gl_Chart; 87. }; 88. //+------------------------------------------------------------------+
Código 07
Observa lo que se hizo en el código 07. Antes, la función ChartApplyTemplate se ejecutaba independientemente de que ChartIndicatorAdd hubiera tenido éxito o no. Ahora, la llamada a ChartApplyTemplate solo se ejecutará si ChartIndicatorAdd falla. Recuerda que el objetivo aquí es didáctico. Así que no intentes utilizar lo que se ha mostrado en algo más complejo, porque no funcionará. Y vuelvo a insistir en que cada caso es diferente. Sin embargo, a pesar del escepticismo de muchos respecto al tipo de resultado que podemos obtener, el resultado es bastante sorprendente, como puede verse en la siguiente animación.

Animación 03
Observa que, efectivamente, conseguimos aplicar el indicador del código 02 al gráfico del objeto OBJ_CHART. Y, aunque muchos puedan imaginar que este indicador no es funcional, funcionará perfectamente. Pero hay un detalle:El indicador presente en un gráfico creado y mantenido por un objeto OBJ_CHART NO APARECERÁ EN LA LISTA junto con los demás indicadores.
Esto puede comprobarse solicitando que se muestre la lista de indicadores. En el caso de la animación 03, la lista se muestra en la siguiente imagen.

Imagen 07
Consideraciones finales
En este artículo vimos que no todo es como parece a primera vista. Aunque lo mostrado aquí pueda parecer, en principio, muy complicado, creo haber alcanzado el objetivo de mostrar y explicar que comprender MQL5 es fundamental para poder utilizar mejor MetaTrader 5.
Aunque podemos colocar indicadores en el gráfico de un objeto OBJ_CHART, la solución adoptada no es adecuada para todas las situaciones.
En el próximo artículo profundizaremos aún más en lo mostrado aquí, lo que nos permitirá crear soluciones todavía mejores y con mucho menos trabajo.| 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 |
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/17470
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.
Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 12): Construcción de clasificadores lineales mediante factorización matricial
Red neuronal en la práctica: Retropropagación manual
Del nivel básico al intermedio: Archivo de plantilla (III)
Oscilador Parafrac: Combinación del SAR parabólico y el indicador fractal
- 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