Português
preview
Del nivel básico al intermedio: Recursos

Del nivel básico al intermedio: Recursos

MetaTrader 5 — Ejemplos |
15 0
CODE X
CODE X

Introducción

En el artículo anterior, Del nivel básico al intermedio: Subventanas (IV), vimos que no todo es como solemos imaginar antes de ver las cosas funcionando. No es raro que alguien crea que ya conoce algo simplemente porque domina sus fundamentos, lo cual es un gran error.

Antes de seguir hablando de las subventanas, necesitamos hacer una pequeña pausa y abordar otro tema igualmente importante. Debido a la naturaleza de lo que veremos en este artículo, te pido que prestes la máxima atención posible a lo que se va a explorar, ya que una comprensión incorrecta de este contenido, con toda seguridad, te traerá serios problemas en el futuro.

Lo que veremos en este artículo permite construir aplicaciones que, de otro modo, tendrías que distribuir como una colección de varios archivos, con los inconvenientes que eso inevitablemente conlleva. En cambio, podremos distribuirlas como un único archivo ejecutable, con un mantenimiento mucho más sencillo.


Recursos

Uno de los conceptos más difíciles de comprender para un principiante es el de los llamados recursos. Me refiero a recursos en términos de código, no de hardware. El motivo es que el concepto de recurso no tiene demasiado sentido cuando se presenta así, de repente. Y tiene todavía menos sentido cuando se explica de forma superficial y atropellada, como suele ocurrir.

Para comprender correctamente este tema, antes necesitamos entender varios conceptos que se presentaron en los artículos anteriores. Así que, si tienes dificultades para comprender algún detalle concreto, lee los artículos anteriores.

En teoría, un recurso es algo sencillo de entender, ya que todo lo que utilizas dentro de un programa y todo lo necesario para ejecutar el propio programa se considera un recurso. Ese es precisamente el concepto básico. Sin embargo, como programador, hay ciertos detalles que deben comprenderse mejor para poder incorporar nuevos elementos a un programa ya terminado.

Para empezar, existen dos tipos básicos de recursos cuando hablamos de software. El primero corresponde a los recursos internos de un código y el segundo, a los recursos externos de ese mismo código. Un recurso interno sería cualquier elemento incluido dentro de un ejecutable. En cambio, un recurso externo es algo que el programa deberá buscar en algún lugar fuera del área de memoria donde se encuentra el propio ejecutable. Comprender correctamente esta distinción es precisamente la parte que puede resultar confusa al principio. Esto se debe a que muchos principiantes se quedan bastante desconcertados cuando ven que un ejecutable es capaz de hacer determinadas cosas sin necesitar aparentemente ningún archivo ni dato externo, mientras que otro requiere una gran cantidad de datos almacenados en distintas ubicaciones y en todo tipo de archivos externos.

Por esta razón, muchos principiantes tienden a pensar que un código que genera el mismo tipo de resultado que otro debe ser necesariamente mejor o peor. En realidad, la diferencia entre un enfoque y otro depende de cómo haya sido diseñado el sistema que se va a ejecutar. Según el caso, será más adecuado utilizar un enfoque u otro. Existe además otra cuestión que quizá sea el verdadero factor determinante a la hora de elegir finalmente un modelo. Y aquí es donde las cosas empiezan a ponerse interesantes, ya que esta cuestión está relacionada con las futuras actualizaciones de la propia aplicación que estás construyendo.

Probablemente te parezca que este tema es algo aburrido y que no tiene demasiada importancia. Sin embargo, resulta extremadamente importante cuando nos encontramos con determinados tipos de problemas. Supongamos que quieres crear un Asesor Experto que realice operaciones automáticas utilizando algún tipo de indicador. Muchos podrían pensar que la única forma de crear algo así sería mediante dos o más ejecutables: uno correspondiente al propio Asesor Experto y uno o varios ejecutables correspondientes a los indicadores que se utilizarán. En principio, esta sería la forma correcta y más adecuada de hacerlo. Sin embargo, cuando decidas distribuir tu sistema, te encontrarás con un pequeño problema: ¿cómo garantizar que el usuario no elimine el indicador que utiliza el Asesor Experto?

Una forma de evitarlo sería convertir todo el sistema en un único archivo, que en este caso sería el propio archivo del Asesor Experto. Pero espera un momento. ¿No acabas de decir que el Asesor Experto necesitará indicadores? Sí, necesitará los indicadores que construyas. Entonces, ¿cómo podrá funcionar si no estamos distribuyendo esos indicadores? Pues bien, aquí es donde todo empezará a tener sentido, mi amigo lector. Existe una forma de incorporar los indicadores, o cualquier otro elemento, dentro del ejecutable del Asesor Experto. De este modo, ni el usuario ni el operador podrán comprometer el análisis para el que realmente fue diseñado el Asesor Experto, ya que, en principio, en principio, ni el usuario ni el operador sabrían siquiera que esos indicadores existen y están siendo utilizados.

Digo en principio porque quizá quieras que se tracen en el gráfico. En ese caso, el usuario los verá, aunque no entenderá por qué, al colocar ese Asesor Experto en el gráfico, empiezan a aparecer esas líneas. Y, cuando se elimina el Asesor Experto, esas mismas líneas desaparecen del gráfico junto con él.

Aunque los indicadores no lleguen a trazarse en el gráfico, como vimos en artículos anteriores, seguirán apareciendo en la lista de indicadores. Sin embargo, ni el usuario ni el operador podrán saber de dónde han salido ese o esos indicadores, ya que simplemente aparecen y desaparecen cada vez que el Asesor Experto se coloca o se elimina del gráfico.

Hum, ahora esto sí me parece bastante interesante. Entonces, en lugar de enviar un montón de archivos a un colega, solo necesito enviarle uno. Y dentro de ese archivo estará toda una configuración preprogramada de lo que debe hacerse. Me gusta la idea.

En realidad, ese sería precisamente el principio básico. Sin embargo, no se limita únicamente a eso. Podemos incluir tanto datos que no podrán modificarse como datos que sí podrán modificarse. Y todo ello siguiendo una serie de criterios que tú, como programador, definirás previamente para determinar qué puede o no modificar cada usuario. Y, si el operador o el usuario provoca algún problema en la configuración y te pide ayuda para corregirlo, solo tendrás que decirle: desinstala el programa, elimina todos los archivos de configuración y después vuelve a instalarlo. Al hacerlo, todo volverá a la normalidad. Aunque al principio al usuario no le guste la idea, le servirá de lección para que la próxima vez tenga más cuidado al modificar la configuración. O, como mínimo, para que sea menos entrometido y no toque lo que no debería. :-)

Bien, ahora que el concepto ha quedado debidamente presentado, podemos pensar en cómo empezar a trabajar con lo que conocemos como recursos. Para empezar, olvídate de que vas a trabajar con uno o varios archivos. Trabajar con recursos requiere paciencia y disciplina. Sin ellas, todo tu código terminará convertido en un verdadero caos. En la propia documentación de MQL5 puedes hacerte una idea de cómo se puede trabajar con recursos. Sin embargo, lo que allí se explica es bastante superficial. Y ni siquiera este artículo será suficiente para mostrar todo lo que realmente podemos hacer. A medida que se publiquen nuevos artículos, verás que este tema es realmente muy interesante y bastante divertido de utilizar.

Así que vamos a comenzar con un ejemplo muy sencillo, solo para que entiendas cómo se implementa este concepto en la práctica. El ejemplo utiliza dos archivos. El primero será un archivo de texto, y no te preocupes por su contenido. Concéntrate en entender cómo se aplica aquí el concepto de recurso. El archivo de texto se muestra a continuación.

Imagen 01

En esta imagen 01, tenemos resaltados en verde el nombre y la ubicación del archivo de texto. En amarillo aparece exactamente el contenido de ese archivo. Bien, ¿qué vamos a hacer con esto? Tranquilo, concéntrate y presta atención. Ahora viene la parte que deberá hacerse en MQL5. Esta se muestra a continuación.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #resource "MyFile.txt" as string Antoine;
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print(Antoine);
09. }
10. //+------------------------------------------------------------------+

Código 01

Cuando ejecutes este código en el terminal de MetaTrader 5, verás como resultado lo que se muestra en la siguiente animación.

Animación 01

Ahora observa que la zona marcada en amarillo en la imagen 01 se está reproduciendo en el terminal. Sin embargo, aquí ocurre algo que quizá no estés entendiendo. O posiblemente estés pensando que se está haciendo una cosa cuando, en realidad, se está haciendo otra completamente distinta. Así que presta mucha atención a lo que voy a explicar, porque no volveré a repetirlo más adelante cuando utilicemos este tipo de enfoque.

Cuando se compila este código 01, la línea cuatro toma todo el contenido del archivo mostrado en la imagen 01 y lo incorpora al ejecutable. Ahora piensa en lo siguiente: para el ejecutable, lo que contiene esta línea cuatro es un array enorme con todo el contenido que antes se encontraba en el archivo MyFile.txt. A partir de ese momento, todo ese contenido estará dentro de este array. Para simplificar las cosas, estamos utilizando el tipo string para representar los datos almacenados en esta enorme constante llamada Antoine. Volviendo a algo que seguramente ya conoces y sabes interpretar, lo que hacemos en este código 01 sería equivalente a lo que se muestra en el código 02, que aparece a continuación.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. const string Antoine = 
05. "E foi então que apareceu a raposa:\n\r"                                                \
06. "- Boa dia, disse a raposa.\n\r"                                                        \ 
07. "- Bom dia, respondeu polidamente o principezinho, que se voltou, mas não viu nada.\n\r"\ 
08. "- Eu estou aqui, disse a voz, debaixo da macieira…\n\r"                                \ 
09. "- Quem és tu? perguntou o principezinho. Tu és bem bonita…\n\r"                        \ 
10. "- Sou uma raposa, disse a raposa.\n\r"                                                 \ 
11. "- Vem brincar comigo, propôs o principezinho. Estou tão triste…\n\r"                   \ 
12. "- Eu não posso brincar contigo, disse a raposa.\n\r";
13. //+------------------------------------------------------------------+
14. void OnStart(void)
15. {
16.     Print(Antoine);
17. }
18. //+------------------------------------------------------------------+

Código 02

Hum, no lo entiendo. ¿Cómo es eso? Desde mi punto de vista, el código 01 es mucho más sencillo y plantea muchas menos complicaciones que el código 02. Y aun así me dices que el código 01 es equivalente a este código 02. Para mí, eso no tiene el menor sentido.

Precisamente por esta razón esperé todo este tiempo para explicar este tema. Al principio, este tipo de cosas parece no tener el menor sentido. Y eso que apenas estamos comenzando a ver lo que realmente podemos hacer. Sin embargo, comprender correctamente esta parte inicial es realmente muy importante. También resulta importante entender todo lo explicado en los artículos anteriores, porque todo se irá volviendo cada vez más confuso a medida que empecemos a añadir nuevos datos aquí. Por extraño que pueda parecer, lo que realmente ocurre es que la línea cuatro del código 01 está creando, en la práctica, el equivalente de la línea cuatro del código 02. Y aquí está precisamente la parte interesante de todo esto: es mucho más fácil entender el código 01 que el código 02, sobre todo si el objetivo fuera incluir algún dato más complejo dentro del ejecutable.

Así que presta atención, porque entender esto es importante. Podemos incluir cualquier tipo de dato dentro del ejecutable. Cualquier tipo, desde contenido ejecutable por MetaTrader 5 hasta imágenes, sonidos o cualquier otro elemento que necesites o quieras incluir allí. Este tipo de elementos son precisamente los llamados recursos de software.

Espera un momento. ¿Me estás diciendo que podemos incluir un ejecutable dentro de otro utilizando este sistema que acabamos de ver? Pero eso no va a funcionar. Al menos, no consigo ver cómo podría funcionar. Porque, en principio, incluir algo dentro de un indicador, script o Asesor Experto no lo convierte necesariamente en un objeto ejecutable. Al menos, así es como imagino que funciona.

Pues bien, como todavía eres un inocente en este mundo y desconoces ciertas artimañas que podemos utilizar como programadores, ha llegado el momento de perder esa inocencia y descubrir que el mundo no es un camino de rosas donde todo es bonito y maravilloso y todos queremos lo mejor para los demás. Porque, en realidad, las cosas no funcionan exactamente así.

Para entender lo que vamos a empezar a hacer, tendremos que volver al artículo anterior. Allí vimos cómo podíamos hacer que un indicador cargara otro indicador en el gráfico. De este modo, seríamos capaces de modificar o controlar determinados elementos del gráfico. Ahora fíjate en lo siguiente: para que el sistema no fallara, era necesario que ambos códigos estuvieran compilados y se encontraran en sus ubicaciones correspondientes. Esto es un hecho y, si estudiaste lo que se mostró allí, sabes perfectamente de qué estoy hablando. Sin embargo, no siempre queremos que las cosas funcionen de esa manera, ya que el sistema acabaría siendo mucho menos estable al depender de si esos ejecutables están presentes o no, lo que podría hacer que la experiencia del usuario final resultara bastante desagradable. Pero ¿qué ocurriría si utilizáramos el concepto que estamos viendo en este artículo? ¿Cómo quedaría realmente el sistema? Para responder a esta pregunta, volvamos a lo que vimos anteriormente.

Bien, los códigos originales utilizados en aquel artículo se reproducen a continuación.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_separate_window
07. #property indicator_height          50
08. #property indicator_plots           0
09. //+------------------------------------------------------------------+
10. int OnInit(void)
11. {
12.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
13.     
14.     return INIT_SUCCEEDED;
15. };
16. //+------------------------------------------------------------------+
17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
18. {
19.     return rates_total;
20. };
21. //+------------------------------------------------------------------+

Código 03

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.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 456.");
12.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01"));
13.     
14.     return INIT_SUCCEEDED;
15. };
16. //+------------------------------------------------------------------+
17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
18. {
19.     return rates_total;
20. };
21. //+------------------------------------------------------------------+

Código 04

Muy bien, la idea es que este código 04 cargue y ejecute el indicador mostrado en el código 03. Hasta aquí, creo que todos han entendido lo que ocurre. Observa que la línea doce de este código 04 es precisamente donde se realiza la carga. Allí indicamos el nombre y la ubicación del archivo que se va a utilizar. Ahora presta mucha atención a los detalles, porque el diablo está en los detalles.

El primer detalle que debemos observar es la ubicación del propio ejecutable que vamos a incorporar en el otro que estamos creando. Presta mucha atención a este punto, porque, si no lo entiendes, acabarás haciéndote un verdadero lío. En la línea doce del código 04 indicamos la ubicación del ejecutable. Sin embargo, cuando vamos a utilizarlo como recurso, necesitamos indicar la ruta de forma relativa y no absoluta. Hum, ¿qué es eso de ubicación relativa y absoluta? Nunca había oído hablar de ello. Bien, mi querido lector, MetaTrader 5 utiliza la ruta absoluta para acceder a determinados archivos dentro del sandbox.

En el artículo Del nivel básico al intermedio: Navegando por el sandbox, hablamos de este tema del sandbox; para más detalles, consulta ese artículo. Cuando utilizas una ruta absoluta, debes indicarla tomando como referencia el propio sandbox. Sin embargo, para el compilador, el compilador no utiliza el sandbox de la misma manera que MetaTrader 5. Para simplificar la explicación, piensa en lo siguiente: MetaTrader 5 localiza el ejecutable partiendo de una posición dentro de la raíz del sandbox. En cambio, el compilador toma como referencia la ruta original del código, independientemente de dónde se encuentre dentro del sandbox.

Vaya, todo esto del sandbox y de las rutas relativa y absoluta parece muy confuso. ¿No hay alguna forma de resumirlo y hacer que sea más sencillo de entender? Por desgracia, no. Necesitas comprender esta cuestión antes de intentar entender cómo se modificará el código 04. Así que volvamos a la explicación. Sabemos que el código 03 SE ENCUENTRA EN EL MISMO DIRECTORIO que el código 04. Esta información es muy importante durante la compilación y la integración. Si el código 03 estuviera en otro directorio, tendríamos que indicárselo al compilador mediante una ruta completa que le permitiera localizarlo. Sin esta información, el compilador NO PODRÁ convertir el código 03 en un recurso dentro del código 04.

Bien, una vez entendido esto, el primer cambio que debemos hacer es crear una definición. Esto es así porque no queremos indicar una ruta durante la integración y luego utilizar otra distinta al acceder al recurso. Por lo tanto, el primer cambio práctico se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. int OnInit(void)
12. {
13.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 456.");
14.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01"));
15.     
16.     return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21.     return rates_total;
22. };
23. //+------------------------------------------------------------------+

Código 05

La única modificación que hicimos aquí fue añadir la línea nueve a este código 05. Así que no lo olvides:

El indicador mostrado en el código 03 ESTÁ EN EL MISMO DIRECTORIO que este código que vamos a crear.

Después de hacer esto, podemos realizar la integración, haciendo que el indicador mostrado en el código 03 pase a considerarse un recurso dentro del código que estamos creando. Para llevar a cabo esta integración, necesitamos añadir un pequeño detalle al código 05. Este se muestra a continuación.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 456.");
16.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01"));
17.     
18.     return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
22. {
23.     return rates_total;
24. };
25. //+------------------------------------------------------------------+

Código 06

Ahora tenemos algo realmente interesante. Y todo por el simple hecho de haber añadido esta línea once al código anterior. Lo que hace esta línea once es integrar el código definido en la línea nueve en este código 06, convirtiéndolo así en un recurso del propio código 06. Y eso no es todo. El asunto es mucho, muchísimo más interesante. Así que vayamos por partes, porque aquí hay una dificultad práctica que más adelante explicaré cómo resolver y que saca de quicio a mucha gente cuando se integra un ejecutable dentro de otro ejecutable.

En el artículo anterior mencioné que, para colocar en el gráfico el indicador mostrado en el código 03, necesitamos asegurarnos de que haya sido compilado y esté disponible para ejecutarse. Eso es un hecho. Ahora bien, cuando compiles este código 06, ocurrirá algo que parece magia. Esto se debe a que, SI el ejecutable NO EXISTE, el compilador creará el ejecutable correspondiente al código definido en la línea nueve. ¿Ah? ¿Cómo? ¿Qué locura es esta? ¿Me estás diciendo que, con solo tener el código e intentar compilar otro código que necesite ese ejecutable, el compilador lo creará por nosotros? Sí, eso es exactamente lo que ocurrirá. El mensaje que muestra el compilador es bastante largo. Y, como existe un límite para el ancho de las imágenes, no se mostrará completo. Sin embargo, la parte que nos interesa aparece resaltada a continuación.

Imagen 02

En este mensaje, el compilador nos informa de que no ha encontrado el ejecutable y que está intentando compilarlo. Como consiguió hacerlo, obtenemos el mensaje que aparece en la segunda línea de la imagen 02. Si se hubiera producido algún fallo, el mensaje sería distinto, como se muestra a continuación.

Imagen 03

Observa que ahora el compilador informa de que existe un error de compilación. Sin embargo, el error se debe precisamente a que el compilador NO PUDO compilar el código necesario para resolver la dependencia de la línea once. Es decir, precisamente la línea en la que debía realizarse la integración. Esto puede deberse a que el código que debe integrarse contiene algún error o a algún problema con la ruta relativa del propio código que se va a integrar. En cualquier caso, el ejecutable final no se creará hasta que corrijas el problema que impide realizar la integración.

Creo que ya lo voy entendiendo. Pero ¿qué ocurre si el ejecutable que se va a integrar ya existe? ¿El compilador volverá a compilarlo? En ese caso, no se realizará una nueva compilación. Aunque el código haya sido modificado, el compilador seguirá utilizando el ejecutable compilado anteriormente. Por esta razón, conviene prestar atención a lo que estás haciendo; de lo contrario, podrías modificar el código que debe integrarse y, al ejecutarlo, descubrir que se está ejecutando como una versión anterior.

En cualquier caso, conviene observar atentamente los mensajes que muestra el compilador. Si el ejecutable que se va a integrar ya existe, obtendremos un resultado como el que aparece en la siguiente imagen.

Imagen 04

Observa que esta imagen 04 se parece bastante a lo que verías si compilaras el código 04. Sin embargo, el resultado final puede ser muy diferente del obtenido durante la ejecución. Dejaremos los detalles para otro momento, ya que este tema requiere tiempo y todavía quiero mostrar en este artículo cómo se resolverá la integración en tiempo de ejecución. Hasta ahora, únicamente hemos creado los códigos y los hemos integrado como recursos uno del otro. Así que ha llegado el momento de ver cómo convertir todo esto en algo realmente ejecutable.

Ahora viene la segunda parte, que puede resultar bastante confusa. Para que el código funcione, necesitamos hacer una maniobra. Esta maniobra se basa en el uso de un operador especial, que debe introducirse en el código de una forma muy concreta. Este operador se utiliza para realizar la resolución de ámbito dentro del propio código. Vaya, esto se está volviendo cada vez más confuso y complicado. ¿Qué es eso de un operador de ámbito? Bien, puedes leer algo más sobre este tema en la propia documentación de MQL5, en este apartado. Discúlpame por no utilizar un nombre más apropiado.

Sin embargo, la propia documentación solo muestra los aspectos básicos de este operador de resolución de ámbito. Lo que realmente nos interesa, aquí y a partir de ahora, es saber que necesitamos indicar de algún modo a nuestro nuevo código cómo ejecutar el que está integrado en él. Es decir, necesitamos hacer que la línea dieciséis del código 06 ejecute el código que ahora es un recurso interno del ejecutable que acabamos de crear, en lugar de seguir intentando ejecutar aquel código externo, como hacía antes y como todavía hace debido precisamente a la forma en que sigue escrita la línea dieciséis.

Bien, creo que entiendo adónde quieres llegar. Pero ahora te pregunto: ¿cómo vamos a hacerlo? Porque, desde mi punto de vista, no tenemos ningún medio para acceder a este recurso que se integra en el ejecutable mediante la línea once de este código 06. Y hay algo más. He consultado la documentación sobre cómo utilizar código como recurso y allí se muestra algo completamente distinto de lo que estás haciendo en este código 06. Así que, para mí, aunque esta integración se esté realizando, en realidad no hará que el código integrado sea funcional. A menos, claro está, que tengas un as bajo la manga que te permita hacerlo. Pero, en ese caso, no tengo la menor idea de cómo podría funcionar semejante truco.

Pues bien, por ahora no voy a entrar en demasiados detalles sobre por qué funciona lo que vamos a hacer. Pero te aseguro que lo único que necesitaremos hacer será modificar la línea dieciséis del código 06 para dejarla como aparece en esa misma línea del código que se muestra a continuación.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 456.");
16.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
17.     
18.     return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
22. {
23.     return rates_total;
24. };
25. //+------------------------------------------------------------------+

Código 07

Con este pequeño cambio, por fin habremos conseguido crear algo realmente interesante. Y aunque puedas pensar que seguiremos necesitando el ejecutable generado a partir del código 03 mostrado en este artículo, ese ejecutable podrá eliminarse justo después de compilar este código 07. De hecho, incluso te aconsejo que lo elimines inmediatamente después; el motivo lo explicaré en otro artículo. En cualquier caso, este código 07 hará que el indicador integrado en el ejecutable final sea perfectamente funcional. Esto se debe a que el propio indicador mostrado en el código 03 ahora forma parte del código 07. De este modo, bastará con transferir y compartir con tus amigos y colegas el ejecutable correspondiente a este código 07, sin que ellos tengan que preocuparse por la presencia o ausencia del ejecutable mostrado en el código 03.

Bien, ahora, al ejecutar este código 07, verás lo que se muestra en la siguiente animación.

Animación 02

Hum, no veo que esté ocurriendo nada especial. Este tipo de comportamiento ya se conseguía anteriormente. Y aun así, no estoy seguro de si intentas engañarme con esta historia de que puedes integrar y ejecutar el código directamente solo porque has cambiado la línea dieciséis. Para mí, estás bromeando. No hay forma de que esta línea dieciséis del código 07 ejecute realmente el indicador integrado en el ejecutable.

Bien, tenemos un incrédulo entre nosotros. Pues comprobemos si el código 07 realmente puede hacer lo que se ha explicado aquí. Para ello, bastará con abrir la lista de indicadores presentes en el gráfico y comprobar la información que aparece allí. Al hacerlo, veremos algo parecido a la imagen que se muestra a continuación.

Imagen 05

Observa que estoy resaltando algo en esta imagen. Y ahora, ¿estás convencido de que el código 07 realmente puede ejecutar el indicador integrado en él? En cualquier caso, lo ideal es que lo veas con tus propios ojos y confirmes lo que se ha demostrado aquí.


Consideraciones finales

En este artículo conociste un concepto que puede resultar extremadamente útil en muchos casos y facilitar enormemente el intercambio de tus aplicaciones y proyectos. Aunque no es un concepto sencillo de explicar por completo en un solo artículo, lo que se ha explicado y expuesto aquí ya nos permitirá hacer muchas otras cosas en un futuro próximo, incluidas algunas que, de otro modo, no sería posible realizar. Precisamente porque este artículo aún no se había publicado: para que pudieras contar con material de apoyo y una base inicial de estudio.

Por eso, mi recomendación es la siguiente: estudia con calma el concepto explicado aquí. A medida que se publiquen nuevos artículos, notarás que comprender lo que se ha explicado aquí te ayudará enormemente a entender y asimilar mejor los próximos contenidos. Así que aprovecha al máximo los códigos incluidos en el archivo adjunto y nos vemos en el próximo artículo.
Archivo MQ5Descripción
Indicators\Code 01Demostración básica
Indicators\Code 02Demostración básica
Indicators\Code 03Demostración básica
Scripts\Code 01Demostración básica
Scripts\Code 02Demostración básica
Scripts\MyFile.txtArchivo de ejemplo sencillo

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

Archivos adjuntos |
Anexo.zip (3.18 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Red neuronal en la práctica: Gradiente Red neuronal en la práctica: Gradiente
El artículo explica por qué y cómo se utiliza el gradiente durante el entrenamiento de un perceptrón, partiendo del error cuadrático mínimo y de la regla de la cadena para obtener las derivadas parciales. Mostramos la implementación del cálculo del gradiente en la clase C_Neuron en MQL5 y lo validamos con ejemplos de 1 y 2 entradas. Aprenderás a ajustar los pesos y el sesgo mediante descenso de gradiente y a prepararte para la propagación hacia adelante y la retropropagación.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Del básico al intermedio: Objetos y subventanas (I) Del básico al intermedio: Objetos y subventanas (I)
El artículo detalla la creación y el posicionamiento del objeto OBJ_CHART dentro de subventanas, destacando las diferencias entre la ventana principal y las subventanas. Muestra cómo integrar indicadores mediante recursos (#resource, ChartIndicatorAdd) e identificar la subventana correcta a partir del nombre corto del indicador. El resultado es un código más estable, portátil y fácil de reutilizar.