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

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

MetaTrader 5 — Ejemplos |
24 0
CODE X
CODE X

Introducción

En el artículo anterior, Del nivel básico al intermedio: Objetos y subventanas (III), mostramos cómo manejar, o mejor dicho, cómo podríamos recrear un objeto que debería estar presente en un gráfico debido a alguna necesidad del indicador. Sin embargo, por algún motivo, dicho objeto fue eliminado del gráfico, aunque el indicador siguiera allí, dando la impresión de que todo está tal y como fue diseñado para utilizarse durante la fase de desarrollo de la aplicación.

Aunque muchos puedan pensar que debemos profundizar aún más en ese tema, por ahora estoy satisfecho con lo que ya se ha tratado. Por lo tanto, en este artículo comenzaremos a abordar un tema que probablemente aclare a quienes leyeron mis primeros artículos cómo conseguía hacer ciertas cosas. Esto se debe precisamente a que aparecen elementos adicionales en el gráfico que, en realidad, no se encuentran en ninguna parte específica del código. De este modo, resulta muy difícil entender por qué la aplicación funciona cuando hay tan poco código que indique cómo debería funcionar.

Muchos conceden poca importancia a este tipo de cosas, hasta el punto de que es un tema muy poco explorado. No solo aquí en MQL5, sino también en muchos otros lenguajes de programación. En realidad, es un tema bastante entretenido e incluso muy interesante de abordar. Así que dejemos de lado las distracciones y centrémonos en lo que se explicará aquí, porque lo que comenzaremos a ver en este artículo implica el uso de técnicas bastante inusuales.


Archivo de plantilla (I)

Es muy probable que estés mirando el título de este tema y pensando: ¿De qué se supone que vamos a hablar aquí? Bien, mi querido lector, aunque a primera vista el tema pueda parecer algo extraño y completamente distinto de lo que utilizarías en tu día a día, lo cierto es que los archivos de plantilla están mucho más presentes en tu vida de lo que probablemente hayas notado. Sobre todo si eres un operador muy activo en el uso de MetaTrader 5. Recuerda que el concepto de archivo de plantilla es mucho más amplio. Aquí, sin embargo, nos limitaremos a tratarlo dentro del ámbito de MQL5. En esencia, estos archivos de plantilla no son más que archivos que contienen algún tipo de configuración gráfica o un patrón de presentación de objetos gráficos por parte de la plataforma MetaTrader 5.

De acuerdo, pero nunca he oído hablar de esos archivos, al menos que yo recuerde. ¿Estás seguro? Porque no conozco a nadie que, de una forma u otra, no los utilice habitualmente. Esto se debe a que en estos archivos se almacenan configuraciones gráficas que pueden transferirse a otro gráfico o incluso guardarse para utilizarlas más adelante. Así, en ellos tendrías los indicadores presentes en ese momento, los esquemas de colores, los objetos gráficos existentes, entre otras cosas relacionadas con el propio gráfico. Se puede acceder a estos archivos desde MetaTrader 5 mediante el acceso directo que se muestra en la siguiente imagen.


Imagen 01

Aquí tenemos la opción de guardar, cargar o incluso eliminar alguno de estos archivos. Además, por supuesto, podemos disponer de un acceso rápido a ellos. En el caso de la imagen 01, tenemos dos archivos. Uno contiene una plantilla predeterminada que MetaTrader 5 utilizará al cargar un gráfico, y el otro lo utiliza un indicador concreto que mostré cómo crear en otro artículo.

El artículo en cuestión puede consultarse en Desarrollo de un EA de trading desde cero (Parte 13): Times And Trade (II). Es decir, lo que escribo en los artículos es realmente algo que utilizo. No escribo artículos por escribir. Quiero que aprendas con ellos o, como mínimo, que te sirvan de inspiración para construir algo interesante y que te resulte útil. Y, como muchos de ustedes no son programadores, sino entusiastas, esta serie pretende precisamente enseñar cómo llegar hasta ahí, poniendo sus ideas en práctica, sin depender de la buena voluntad de nadie.

Pero, antes de llegar al punto en el que podamos utilizar estas cosas de una manera espectacular, necesitamos dar los primeros pasos con algo básico y sencillo. Para no perder demasiado tiempo ni hacer que el artículo resulte demasiado tedioso, voy a suponer que todos aquí saben cómo guardar, cargar, configurar y ajustar una plantilla para utilizarla en un gráfico. Y todo ello utilizando únicamente lo que MetaTrader 5 ofrece de forma predeterminada: los controles que aparecen en la imagen 01 y los que se muestran en la siguiente imagen.


Imagen 02

Ahora presta atención: lo que estás viendo en esta imagen 02 es un acceso directo que permite utilizar un recurso aún más avanzado disponible en MQL5. Sin embargo, quizá todavía no te hayas dado cuenta de ello, precisamente porque hasta ahora has estado envuelto en la oscuridad. Ha llegado el momento de salir a la luz y ver que el mundo no está cubierto de sombras, sino que es un mundo multicolor, lleno de posibilidades que muchos ni siquiera han imaginado porque todavía se niegan a ver la luz del día y prefieren vivir en la ignorancia, con una visión limitada de las cosas.

En caso de que todavía no lo hayas entendido, quiero que vuelvas a mirar esta imagen 02 y respondas sinceramente: ¿Conoces algún objeto, dentro de MQL5, que se parezca a la figura de la esquina izquierda de esta imagen 02? Pues sí: el objeto OBJ_CHART es precisamente lo que estamos viendo en la esquina izquierda de esta imagen 02. Espera. ¿Por qué me estás llamando la atención sobre este hecho? ¿Acaso me estás diciendo que podemos controlar el objeto OBJ_CHART sin necesidad de programarlo? ¿He estado perdiendo el tiempo haciendo algo que podría haber hecho de una forma más sencilla, rápida y eficiente todo este tiempo? Bien, respecto a eso, no sé qué responderte. Pero sí, muy probablemente hayas estado perdiendo el tiempo, limitándote a hacer las cosas de una determinada manera cuando podrías hacerlas mucho más rápido siguiendo otro camino.

Para que puedas entender adónde quiero llegar, primero tendrás que comprender una cosa, mi querido lector. Todo, absolutamente todo lo que ves en la pantalla de una computadora, se abstrae de alguna manera para facilitar la presentación de esa información. Para un profano, o un simple usuario, ver en la pantalla algo como lo que se muestra en la siguiente imagen no representa más que el gráfico de un símbolo.


Imagen 03

Para un programador, o para un usuario algo más atento e igualmente curioso, esta misma imagen 03 contiene elementos que ya son bastante conocidos y que pueden manipularse y ajustarse. Para demostrarlo, vamos a crear un pequeño código para aclarar un poco las cosas. El código en cuestión 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. int OnInit(void)
12. {
13.     string sz0 = def_Prefix + (string)ObjectsTotal(0, -1, -1);
14. 
15.     ObjectCreate(0, sz0 , OBJ_CHART, 0, 0, 0);
16.     ObjectSetString(0, sz0, OBJPROP_SYMBOL, _Symbol);
17.     ObjectSetInteger(0, sz0, OBJPROP_XDISTANCE, 0);
18.     ObjectSetInteger(0, sz0, OBJPROP_YDISTANCE, 0);
19.     ObjectSetInteger(0, sz0, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
20.     ObjectSetInteger(0, sz0, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
21.     ObjectSetInteger(0, sz0, OBJPROP_PERIOD, PERIOD_M15);
22.     ObjectSetInteger(0, sz0, OBJPROP_DATE_SCALE, false);
23.     ObjectSetInteger(0, sz0, OBJPROP_PRICE_SCALE, false);
24.    
25.     return INIT_SUCCEEDED;
26. };
27. //+------------------------------------------------------------------+
28. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
29. {
30.     return rates_total;
31. };
32. //+------------------------------------------------------------------+
33. void OnDeinit(const int reason)
34. {
35.     ObjectsDeleteAll(0, def_Prefix);
36.     ChartRedraw();
37. };
38. //+------------------------------------------------------------------+

Código 01

Este código 01, que imagino que no necesita explicación, produce el resultado que se muestra en la siguiente imagen.


Imagen 04

Obviamente, ya debes saber que podemos modificar algunas de las propiedades de este objeto OBJ_CHART. Esto nos permite configurar y utilizar el gráfico de una forma que antes nadie imaginaría posible. Sin embargo, si cambian las dimensiones del gráfico, este código 01 no se adaptará a esos cambios. Para corregirlo, modificaremos el código 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
16.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
17.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
18.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
19.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
20.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
21.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
22.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
23.    
24.     return INIT_SUCCEEDED;
25. };
26. //+------------------------------------------------------------------+
27. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
28. {
29.     return rates_total;
30. };
31. //+------------------------------------------------------------------+
32. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
33. {
34.     switch(id)
35.     {
36.         case CHARTEVENT_CHART_CHANGE:
37.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
38.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
39.             break;
40.     }
41.     ChartRedraw();
42. };
43. //+------------------------------------------------------------------+
44. void OnDeinit(const int reason)
45. {
46.     ObjectsDeleteAll(0, def_Prefix);
47.     ChartRedraw();
48. };
49. //+------------------------------------------------------------------+

Código 02

Esta sencilla modificación del código 01 para convertirlo en el código 02 permitirá que el objeto OBJ_CHART se ajuste automáticamente a las nuevas dimensiones del gráfico. Y esto ya empezará a proporcionarnos cierto control, aunque apenas estamos empezando a entrar en materia.

De acuerdo, pero sigo sin entender qué tiene que ver un archivo de plantilla, pensado para utilizarse en gráficos dentro de MetaTrader 5, con un objeto OBJ_CHART.

Bien, mi querido lector, para entenderlo, lo mejor sería explicártelo personalmente, ya que así podríamos explicar cada detalle por separado hasta que los comprendieras. Sin embargo, creo que muchos tendrían cierta dificultad para entender cómo algunos detalles afectan al objeto OBJ_CHART. Así que intentaré utilizar otro enfoque, algo más sencillo. De esta forma, creo que será más fácil comprender cómo pueden utilizarse los archivos de plantilla sin necesidad de entrar en los detalles del propio archivo.

Cuando se coloca un objeto OBJ_CHART en un gráfico, este absorberá una parte —y presta atención a esto— de la plantilla del gráfico. La palabra que nos interesa aquí es: PARTE. ¿Y por qué es importante esta palabra? Porque, independientemente de cómo esté configurada la plantilla del gráfico, solo se utilizará una parte de ella para crear la plantilla que se aplicará al objeto OBJ_CHART. Esto se hace para que el objeto OBJ_CHART se muestre en el gráfico.

Amigo, no entiendo lo que quieres decir. ¿Podrías explicarlo con un poco más de claridad, por favor? Bien, lo intentaré. Como ya he mencionado, sería más fácil mostrártelo personalmente. Así que hagamos lo siguiente: una vez compilado el código 02, vamos a colocarlo en un gráfico que inicialmente esté configurado como se muestra a continuación.


Imagen 05

Esta configuración que vemos en la imagen 05 sigue un esquema de colores predefinido en MetaTrader 5. Para quienes tengan alguna duda al respecto, el esquema se muestra a continuación.


Imagen 06

Ahora presta atención: cuando coloquemos el indicador en el gráfico, obtendremos el resultado que podemos ver a continuación.


Imagen 07

De acuerdo, pero sigo sin entenderlo, porque a mí todo me parece bastante normal y no veo ninguna gran novedad. Bien, ahora, con el indicador todavía presente en el gráfico, cambiaremos el esquema de colores por el que aparece en la imagen 02. Con ello, obtendremos el resultado que vemos en la siguiente imagen.


Imagen 08

Hum, todavía no consigo entender adónde quieres llegar. Hasta donde sé, este resultado que vemos en la imagen 08 es normal. Todo está dentro de lo esperado. Pero aquí es donde las cosas empiezan a ponerse un poco más interesantes, mi querido lector. Esto se debe a que ahora tenemos dos esquemas de colores completamente diferentes que se muestran en el gráfico. Muchos podrían decir lo siguiente: Bien, ¿y qué? Si queremos actualizar el esquema de colores para que ambos sean iguales, bastará con copiar cada uno de los colores en el objeto OBJ_CHART, ya que podemos hacerlo de una manera muy sencilla. Esta solución no sería incorrecta. Es perfectamente funcional y, sin ninguna duda, es una metodología que podemos utilizar en determinados casos.

Sin embargo, hacer este tipo de copia nos dará mucho más trabajo que utilizar otras metodologías. Además, complicará la tarea de garantizar que todo siga funcionando correctamente con el paso del tiempo. Y, a medida que las cosas se vuelvan cada vez más elaboradas, la posibilidad de que algo salga mal aumentará vertiginosamente. Porque, además del esquema de colores, también podríamos estar modificando otras características del propio gráfico. Tarde o temprano, esto acaba convirtiendo toda la implementación en algo tedioso, agotador, rígido y muy propenso a errores.

Precisamente por eso podemos adoptar otra metodología que, además de ser mucho más práctica, también es mucho más sencilla, ya que nos proporciona un mayor control y permite obtener una copia más fiel. A veces, incluso demasiado fiel, dicho sea de paso. Esto exigirá que hagamos algunos ajustes para evitar dolores de cabeza, independientemente de lo que estemos haciendo en el gráfico.

Pero vayamos con calma, ya que creo que este es el primer contacto que muchos están teniendo con este tipo de modelado y enfoque. No quiero que ocurra lo mismo que pasó cuando mostré otras cosas en mis primeros artículos. Esta vez quiero que todos entiendan realmente lo que está ocurriendo, para que nadie se quede con dudas ni deje de aprovechar lo que se está explicando.

Entonces, vamos a comenzar con el enfoque basado en modificar o copiar directamente los parámetros de color. Es decir, si cambia el color del gráfico principal, deberá ocurrir lo mismo con el gráfico incrustado en el objeto OBJ_CHART. Para conseguirlo, necesitamos hacer algunas cosas y, como no quiero crear algo complejo y tedioso, seguiremos un camino aún más sencillo. Para ello, partiremos del código 02.

Lo primero que debe quedar claro es que el objeto OBJ_CHART es un gráfico con su propio identificador. Sin embargo, ese mismo gráfico está presente, o mejor dicho, incrustado en un gráfico principal. Y como queremos modificar los colores de las barras del gráfico que se encuentra en OBJ_CHART, tendremos que utilizar un enfoque bastante inusual. No te preocupes, el procedimiento en sí es fácil de entender. Pero debemos prestar atención a los detalles para que todo funcione correctamente.

Así que no empezaremos cambiando los colores del gráfico presente en el objeto OBJ_CHART. Cambiaremos el color del gráfico principal. Para ello, modificaremos el código 02 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     ChartSetInteger(id, CHART_COLOR_CANDLE_BULL, clrGreen);
16.     ChartSetInteger(id, CHART_COLOR_CANDLE_BEAR, clrFireBrick);
17.     ChartSetInteger(id, CHART_COLOR_CHART_UP, clrBlack);
18.     ChartSetInteger(id, CHART_COLOR_CHART_DOWN, clrBlack);
19.     ChartSetInteger(id, CHART_COLOR_CHART_LINE, clrBlue);
20. }
21. //+------------------------------------------------------------------+
22. int OnInit(void)
23. {
24.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
25.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
26.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
27.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
28.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
29.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
30.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
31.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
32.    
33.     return INIT_SUCCEEDED;
34. };
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     return rates_total;
39. };
40. //+------------------------------------------------------------------+
41. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
42. {
43.     switch(id)
44.     {
45.         case CHARTEVENT_CHART_CHANGE:
46.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
47.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
48.             NewColors(0);
49.             break;
50.     }
51.     ChartRedraw();
52. };
53. //+------------------------------------------------------------------+
54. void OnDeinit(const int reason)
55. {
56.     ObjectsDeleteAll(0, def_Prefix);
57.     ChartRedraw();
58. };
59. //+------------------------------------------------------------------+

Código 03

La idea es muy sencilla. Partiendo del gráfico que vemos en la imagen 03, queremos que, al añadir este código 03, cambien los colores de las barras. Lo que estamos haciendo aquí se parece mucho a lo que hicimos en otro artículo de esta misma serie, Del nivel básico al intermedio: Indicador (IV). Sin embargo, a diferencia de lo que ocurría en el artículo mencionado, aquí estamos cambiando el color manualmente, por así decirlo. Es decir, MetaTrader 5 no interviene directamente y solo se encarga de mostrarnos los resultados. Como resultado de ejecutar este código 03, obtenemos lo que se muestra en la siguiente imagen.


Imagen 09

Perfecto, ya hemos confirmado que los colores se están modificando. El detalle es que, si eliminas el indicador del gráfico, el esquema de colores se mantendrá, ya que no hemos guardado el esquema anterior para poder restaurarlo en el gráfico. Esta quizá sea la mayor diferencia entre hacer las cosas como se muestra aquí y hacerlas como se explicó anteriormente, cuando utilizábamos MetaTrader 5 para establecer el esquema de colores.

De acuerdo, como aquí el objetivo es didáctico, no me preocuparé por recuperar ni guardar el esquema de colores original. Así que podemos pasar al siguiente paso. Este consiste en hacer que el procedimiento de la línea trece de este código 03 se aplique al objeto OBJ_CHART. De este modo, el esquema de colores se aplicará a los elementos trazados en el objeto OBJ_CHART. La idea es sencilla, pero ¿cómo lo haremos en la práctica? Bien, debes recordar que un objeto OBJ_CHART es, en realidad, un gráfico y, como tal, tiene su propio identificador. Fíjate en lo siguiente: si en la línea cuarenta y ocho del código 03 indicamos cuál es el identificador del gráfico que debe recibir los valores del procedimiento de la línea trece, MetaTrader 5 sabrá a qué gráfico debe aplicar esos mismos valores.

¿Estás de acuerdo conmigo en esto? Bien, ¿y cómo podemos saber cuál es el identificador del gráfico presente en el objeto OBJ_CHART? En cierto modo, esta es la parte fácil, ya que en la biblioteca de MQL5 disponemos de los mecanismos necesarios para hacerlo. Así que lo único que necesitamos modificar en el código 03 es precisamente la línea cuarenta y ocho. Esta deberá sustituirse por la línea que se muestra a continuación.

NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));

Ahora, al ejecutar de nuevo el código 03 después de esta pequeña modificación, obtenemos el resultado que se muestra en la siguiente animación.


Animación 01

Pero ¿qué ha ocurrido aquí? Bien, esto ha sido bastante inusual. Debo confesar que no esperaba precisamente este resultado. Esperaba que las cosas fueran diferentes o, al menos, que se produjera algún cambio en los colores de los elementos trazados en el gráfico del objeto OBJ_CHART. Pero, al parecer, no ha funcionado como esperábamos.

En realidad, amigo mío, este tipo de cosas suele ocurrir bastante, sobre todo cuando estamos aprendiendo cómo funcionan las cosas. Quien ya tiene experiencia detecta enseguida el problema, porque habrá pasado por este tipo de situación muchas veces. Sin embargo, para quien está empezando, el resultado que vemos en la animación no resulta nada alentador. Muy al contrario, acaba desanimándonos y dejándonos un poco sin saber por dónde seguir, ya que aquello que pensábamos que iba a funcionar ha resultado no funcionar en absoluto.

Sin embargo, si estudias la documentación, verás que se menciona un hecho bastante interesante. Los cambios realizados en el gráfico se colocan en una cola de ejecución para aplicarse posteriormente. Pero aquí está lo importante: normalmente estos cambios solo se ponen en cola en determinadas situaciones. En otras, pueden simplemente perderse y no llegar a ejecutarse nunca. Uno de esos casos es precisamente el que observas en la animación 01. El identificador del gráfico presente en el objeto OBJ_CHART es distinto del que MetaTrader 5 gestiona normalmente de forma predeterminada. Por esta razón, el cambio que queremos realizar no se colocará correctamente en la cola de ejecución. Nuestro código debe forzar explícitamente esa actualización. Solo así se aplicará. Y aquí es donde la experiencia nos permite saber qué debemos modificar en este código 03 para que la modificación deseada surta efecto en el gráfico.

Observa que, al haber modificado la línea cuarenta y ocho con el contenido mostrado anteriormente, dentro del procedimiento de la línea trece disponemos del identificador del gráfico que debe redibujarse. Para efectuar este redibujado o, mejor dicho, para forzar a MetaTrader 5 a redibujar el gráfico presente en el objeto OBJ_CHART, debemos indicar a la función ChartRedraw de la biblioteca de MQL5 cuál es el identificador del gráfico que debe redibujarse inmediatamente. Sabiendo esto, podemos modificar el código 03 para obtener el código 04, cuyo contenido completo 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     ChartSetInteger(id, CHART_COLOR_CANDLE_BULL, clrGreen);
16.     ChartSetInteger(id, CHART_COLOR_CANDLE_BEAR, clrFireBrick);
17.     ChartSetInteger(id, CHART_COLOR_CHART_UP, clrBlack);
18.     ChartSetInteger(id, CHART_COLOR_CHART_DOWN, clrBlack);
19.     ChartSetInteger(id, CHART_COLOR_CHART_LINE, clrBlue);
20.     ChartRedraw(id);
21. }
22. //+------------------------------------------------------------------+
23. int OnInit(void)
24. {
25.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
26.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
27.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
28.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
29.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
30.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
31.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
32.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
33.    
34.     return INIT_SUCCEEDED;
35. };
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     return rates_total;
40. };
41. //+------------------------------------------------------------------+
42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
43. {
44.     switch(id)
45.     {
46.         case CHARTEVENT_CHART_CHANGE:
47.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
48.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
49.             NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));
50.             break;
51.     }
52.     ChartRedraw();
53. };
54. //+------------------------------------------------------------------+
55. void OnDeinit(const int reason)
56. {
57.     ObjectsDeleteAll(0, def_Prefix);
58.     ChartRedraw();
59. };
60. //+------------------------------------------------------------------+

Código 04

En consecuencia, al compilar este código 04 y ejecutarlo, obtendremos el resultado que se observa en la siguiente animación.


Animación 02

Vaya, qué cosa tan disparatada. Definitivamente, nunca habría imaginado que fuera tan sencillo. Es incluso uno de esos detalles a los que, si no hubieras tenido esta paciencia para explicármelos, no habría prestado la debida atención y lo habría pasado por alto. Al fin y al cabo, todo se reducía a un simple detalle que había que modificar en el código. Sin embargo, ese pequeño detalle terminó marcando la diferencia. Ahora entiendo por qué nunca conseguía hacer ciertas cosas, y mucho menos comprender aquellos primeros artículos tuyos, ya que antes no explicabas estos detalles. Me alegra que hayas cambiado la forma de explicar las cosas y de escribir los artículos.

De acuerdo, pero no me lo agradezcas a mí. Agradece a quienes insistieron y me pidieron que escribiera artículos dirigidos a principiantes. Porque, si esas personas no hubieran mostrado interés por este tipo de artículos, esta serie jamás se habría escrito.

Ahora vamos a hacer una última modificación, por así decirlo, en este código 04. Este cambio tiene precisamente como objetivo copiar los valores presentes en el gráfico principal al gráfico que se mostrará dentro del objeto OBJ_CHART. El código que hace esto 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     ChartSetInteger(id, CHART_COLOR_CANDLE_BULL, ChartGetInteger(0, CHART_COLOR_CANDLE_BULL));
16.     ChartSetInteger(id, CHART_COLOR_CANDLE_BEAR, ChartGetInteger(0, CHART_COLOR_CANDLE_BEAR));
17.     ChartSetInteger(id, CHART_COLOR_CHART_UP, ChartGetInteger(0, CHART_COLOR_CHART_UP));
18.     ChartSetInteger(id, CHART_COLOR_CHART_DOWN, ChartGetInteger(0, CHART_COLOR_CHART_DOWN));
19.     ChartSetInteger(id, CHART_COLOR_CHART_LINE, ChartGetInteger(0, CHART_COLOR_CHART_LINE));
20.     ChartRedraw(id);
21. }
22. //+------------------------------------------------------------------+
23. int OnInit(void)
24. {
25.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
26.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
27.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
28.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
29.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
30.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
31.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
32.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
33.    
34.     return INIT_SUCCEEDED;
35. };
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     return rates_total;
40. };
41. //+------------------------------------------------------------------+
42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
43. {
44.     switch(id)
45.     {
46.         case CHARTEVENT_CHART_CHANGE:
47.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
48.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
49.             NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));
50.             break;
51.     }
52.     ChartRedraw();
53. };
54. //+------------------------------------------------------------------+
55. void OnDeinit(const int reason)
56. {
57.     ObjectsDeleteAll(0, def_Prefix);
58.     ChartRedraw();
59. };
60. //+------------------------------------------------------------------+

Código 05

Aunque funciona, este código 05 es espantoso y horrible. No me gusta crear cosas de esta manera, porque da la impresión de que no sabemos realmente lo que estamos haciendo. Además, hace que todo sea mucho más laborioso y agotador a medida que van surgiendo nuevas necesidades y se implementan nuevos elementos. Y el motivo es que, dentro del procedimiento de la línea trece, estamos duplicando elementos de una manera muy arriesgada. Esto acaba haciendo que el código sea muy propenso a errores de escritura o a fallos de edición con el paso del tiempo.

En mi humilde y modesta opinión, este código 05 estaría mucho mejor escrito si se hiciera 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     const ENUM_CHART_PROPERTY_INTEGER list[] = {
16.         CHART_COLOR_CANDLE_BULL,
17.         CHART_COLOR_CANDLE_BEAR,
18.         CHART_COLOR_CHART_UP,
19.         CHART_COLOR_CHART_DOWN,
20.         CHART_COLOR_CHART_LINE
21.     };
22.     
23.     for (uint c = 0; c < list.Size(); c++)
24.         ChartSetInteger(id, list[c], ChartGetInteger(0, list[c]));
25.     ChartRedraw(id);
26. }
27. //+------------------------------------------------------------------+
28. int OnInit(void)
29. {
30.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
31.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
32.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
33.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
34.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
35.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
36.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
37.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
38.    
39.     return INIT_SUCCEEDED;
40. };
41. //+------------------------------------------------------------------+
42. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
43. {
44.     return rates_total;
45. };
46. //+------------------------------------------------------------------+
47. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
48. {
49.     switch(id)
50.     {
51.         case CHARTEVENT_CHART_CHANGE:
52.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
53.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
54.             NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));
55.             break;
56.     }
57.     ChartRedraw();
58. };
59. //+------------------------------------------------------------------+
60. void OnDeinit(const int reason)
61. {
62.     ObjectsDeleteAll(0, def_Prefix);
63.     ChartRedraw();
64. };
65. //+------------------------------------------------------------------+

Código 06

El resultado será exactamente el mismo. Sin embargo, observa que el riesgo de cometer algún error de escritura o de edición en el código con el paso del tiempo disminuye considerablemente. Y esto se debe simplemente a que hemos cambiado la forma de estructurar los propios datos. Cuando vemos que estamos leyendo un dato de un lugar y aplicando el resultado en otro, resulta más fácil simplificar las cosas. Por lo tanto, al colocar todo en una lista de datos que deben procesarse, reducimos en cierta medida el riesgo de cometer alguna torpeza en el futuro.

Esta definitivamente no sería mi primera opción. Esto se debe precisamente a que tendremos que incluir en la lista todos y cada uno de los parámetros que deban copiarse de un lugar a otro. Y, como aquí queremos copiar absolutamente todos los parámetros presentes en un gráfico, crear y mantener esta lista se convierte en una tarea bastante tediosa y propensa a errores. Aunque lo hagas con toda la calma y prestando la máxima atención, nada garantiza que no se produzcan errores. De cualquier forma, este código 06 es mucho más sencillo y seguro que el código 05, aunque ambos hagan lo mismo.

Precisamente por este motivo, este enfoque basado en copiar valores no es mi preferido. Prefiero utilizar otros métodos. Además, hacer las cosas como hemos visto aquí acaba limitándonos a la hora de tomar determinadas decisiones. De ahí surge la creencia de muchos de que hace falta un código enorme y muy elaborado para obtener algún resultado práctico cuando, en realidad, puede hacerse con un código mínimo y mucho más sencillo. Esto, por un lado, facilita las cosas a quien entiende lo que se está haciendo, pero las dificulta enormemente para quien esperaba encontrar otro tipo de implementación en el código. Y por esta razón iremos dando cada uno de los pasos con mucha calma y prestando mucha atención. Porque este tipo de cosas, aunque resulte muy interesante, acaba volviéndolo todo mucho más confuso para quien espera la implementación de un determinado tipo de código.


Consideraciones finales

En este artículo vimos los fundamentos de una plantilla y cómo podríamos utilizarla para transferir la configuración del gráfico principal al gráfico creado y mantenido por un objeto OBJ_CHART. Aunque creas haber entendido perfectamente lo que ha ocurrido aquí, me gustaría que hicieras un pequeño experimento. Intenta escribir los códigos que hemos visto y pruébalos a medida que vayas añadiendo cada una de las llamadas que realizan a la biblioteca de MQL5. Esto te permitirá comprobar en la práctica cómo debe construirse y plantearse tu código. Aunque en el anexo tienes los códigos completos, ver y entender qué problemas o fallos puede provocar en el código la presencia o ausencia de una determinada llamada te ayudará enormemente a comprender mejor cómo debe plantearse e implementarse la programación en MQL5. Esto te permitirá adquirir mucha más experiencia como programador.
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

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

Archivos adjuntos |
Red neuronal en la práctica: Una cuestión de escala Red neuronal en la práctica: Una cuestión de escala
Existe una falsa sensación, entre el público general, de que una red neuronal, o una inteligencia artificial, puede, de algún modo, comprender el mundo en el que vivimos. En algunos casos, esto incluso puede ser cierto. Pero tú, que quieres conseguir que una red neuronal, o incluso una neurona, converja más rápido, necesitas entender que la escala utilizada en los valores muchas veces termina influyendo en la velocidad de convergencia. Además, en este artículo mostraré algo extraño que ocurre en las redes neuronales. Algo que te llevará a la locura y hará que te cuestiones muchas cosas sobre el tema.
Red neuronal en la práctica: Iniciando la cadena Red neuronal en la práctica: Iniciando la cadena
Ha llegado el momento de empezar a ver y entender cómo conectar perceptrones en una cadena. Esto nos permitirá implementar lo que conocemos como una red de perceptrones. Esta red constituye la base de todo aquello que conoces y utilizas como alguna forma de «inteligencia artificial». Sin embargo, construir una red de este tipo plantea diversos desafíos. Aquí comenzaremos a ver cuáles son.
Oscilador Parafrac: Combinación del SAR parabólico y el indicador fractal Oscilador Parafrac: Combinación del SAR parabólico y el indicador fractal
Exploraremos cómo se pueden combinar el indicador Parabolic SAR y el indicador Fractal para crear un nuevo indicador de tipo oscilador. Al integrar las ventajas únicas de ambas herramientas, los operadores pueden aspirar a desarrollar una estrategia de trading más refinada y eficaz.
Automatización de estrategias de trading en MQL5 (Parte 27): Creando un patrón armónico «Crab» de acción del precio con señales visuales Automatización de estrategias de trading en MQL5 (Parte 27): Creando un patrón armónico «Crab» de acción del precio con señales visuales
En este artículo, desarrollamos un sistema de patrones armónicos «Crab» en MQL5 que identifica patrones armónicos «Crab» alcistas y bajistas utilizando puntos de pivote y niveles de Fibonacci, y que activa operaciones con niveles precisos de entrada, stop loss y take profit. Incorporamos información visual mediante elementos gráficos, como triángulos y líneas de tendencia, para mostrar la estructura del patrón XABCD y los niveles de negociación.