Português
preview
Desarrollo de un sistema de repetición (Parte 46): Proyecto Chart Trade (V)

Desarrollo de un sistema de repetición (Parte 46): Proyecto Chart Trade (V)

MetaTrader 5Ejemplos | 18 julio 2024, 12:37
21 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, Desarrollo de un sistema de repetición (Parte 45): Proyecto Chart Trade (IV), mostré cómo comenzar a funcionalidad en el indicador Chart Trade. Pero habrás notado que en el anexo de ese artículo, había muchos archivos que transportar. El riesgo de cometer algún error, como olvidar añadir un archivo o borrar algo importante por error, hace que ese método no sea muy interesante.

Sé que muchos utilizan exactamente esa forma de distribuir y guardar las cosas. Pero existe una manera mucho más adecuada. Al menos, en lo que respecta a la distribución de ejecutables y a su almacenamiento.

La forma que explicaré aquí, puede ser de gran ayuda. Ya que puedes usar el propio MetaTrader 5 como un gran ayudante, así como el MQL5. No es algo tan complejo ni difícil de entender. Pero comprender y saber cómo aprovechar lo que explicaré en este artículo, puede ayudarte mucho a no tener problemas para guardar adecuadamente los ejecutables o cualquier cosa que necesiten para su correcto funcionamiento.

 

Comprendiendo la idea

Antes de comenzar, es necesario que comprendas que existen problemas y limitaciones para hacer esto. Tales limitaciones se deben al hecho de que tanto el lenguaje MQL5, como la propia plataforma MetaTrader 5, no están diseñados para ser utilizados de determinadas maneras.

El lenguaje, así como la plataforma, están diseñados para ser utilizados como medio para visualizar el mercado. El hecho de que queramos o intentemos que funcione de manera diferente no significa necesariamente que sea incapaz de hacerlo.

Esta es la primera de las limitaciones que existe y de la que debes estar al tanto. El próximo punto a observar es el hecho de que puedes añadir casi cualquier cosa a los ejecutables. Atención a esto, dije CASI. En teoría, puedes añadir prácticamente cualquier cosa, siempre que sepas cómo hacerlo para añadir o incluir esas cosas en el ejecutable. Naturalmente, la forma de incluir información en el ejecutable es convertirla en un recurso interno del propio ejecutable.

Notoriamente, verás a muchos añadiendo imágenes, sonidos u otras cosas similares. Pero, ¿podrías añadir un archivo de plantilla para que forme parte del ejecutable? Al observar la documentación, verás que dice que esto no es posible. De hecho, si intentas hacerlo, el compilador generará un error al intentar compilar el programa.

#define def_IDE_RAD  "Files\\Chart Trade\\IDE_RAD.tpl"
#resource "\\" + def_IDE_RAD;

Si intentas compilar un ejecutable que contiene el código anterior, verás que el compilador generará un error, ya que la definición hace referencia a una plantilla. Esto, por regla general, está prohibido para un ejecutable que se vaya a utilizar en la plataforma MetaTrader 5.

Este tipo de limitación es bastante desagradable, debo confesar, pero es algo que podemos sortear fácilmente, aunque no sin coste. Existen limitaciones y problemas que resolver. Esto se debe a que puedes, y te mostraré cómo hacerlo, añadir una plantilla a un ejecutable compilado por MetaEditor. Pero el gran problema no es cómo incluir la plantilla en el ejecutable. El verdadero problema es conseguir usar esa misma plantilla.

¿Y por qué quiero explicar cómo hacer esto? ¿No sería más sencillo usar las cosas como de costumbre? Sí, de hecho, usar las cosas como estamos acostumbrados es mucho más sencillo. Sin embargo, como mencioné al inicio de este artículo, es mucho más práctico tener todo incorporado en el ejecutable. Piensa en el trabajo que supone empaquetar todos los archivos necesarios para ejecutar una función determinada en MetaTrader 5, además de transferir esto a la máquina que se usará para operar en el mercado. Si olvidas algo, tienes que volver a buscar el archivo o archivos que has perdido. Debes colocarlos en la posición correcta en el árbol de directorios para que la aplicación pueda acceder a dichos archivos.

Esto, a mi parecer, es algo bastante tedioso. Una cosa que muchos hacen es usar la misma máquina tanto para desarrollar como para usar MetaTrader 5. Me gustaría decirles a estas personas que esto es un gran error, por no decir otra cosa. Tú NUNCA, pero NUNCA jamás, debes usar la misma máquina o instalación de MetaTrader 5 para operar y desarrollar. Esto se debe a que al hacer tal cosa, puedes acabar creando algún tipo de fallo o brecha en la aplicación, lo que puede provocar que, en caso de estar usando un EA automático, este haga cosas extrañas.

Yo mismo he visto cosas muy extrañas ocurrir durante la fase de desarrollo. A veces, un programa o aplicación simplemente comienza a interactuar con otra aplicación presente en el gráfico, haciendo que las cosas funcionen de manera muy extraña. A veces se generan errores y, en otras ocasiones, ocurren cosas inexplicables. Algunos dirán que es porque estoy haciendo pruebas. De acuerdo, está bien. A veces, sí. Es por motivos de pruebas, pero en otras ocasiones las cosas simplemente no tienen sentido.

Por eso es imprescindible que uses dos instalaciones para utilizar MetaTrader 5. Una para desarrollo y otra solo para operar en el mercado. Esto es si eres un desarrollador. En cualquier otro caso, esto no será necesario.

Pensé mucho si debería o no mostrar cómo hacer ciertas cosas. La mayoría no logra entender lo básico sobre MQL5 ni cómo funciona el MetaTrader 5. Imagínate entonces entender lo que voy a mostrar. Algunos pensarán que estoy loco, o que soy un fanfarrón. Pero lo cierto es que puedes comparar el anexo de este artículo con el del anterior. Esto se aplica tanto a su funcionamiento como a su portabilidad. En cuanto al funcionamiento, ambos son idénticos. En cuanto a la portabilidad, creo que este es mucho mejor. Ya que todo lo que necesitas hacer es enviar un único archivo. No tienes que preocuparte por las estructuras de directorios.

Importante: Aunque digo que no es necesario preocuparse por la estructura de directorios, esto solo se aplica al hecho de mantener el ejecutable exactamente en el directorio en que se encuentra. Moverlo de directorio, o incluso cambiarle el nombre, hará que el sistema no funcione adecuadamente.

Entonces, veamos cómo se implementó y cómo puedes aprovecharlo en tus propios códigos.


Recursos, recursos y recursos

Para poder utilizar este modelo, será necesario realizar algunos cambios en el código. Bien, pero a pesar de esto, el código fuente del indicador no fue modificado. Por lo tanto, no aparecerá aquí en el artículo. El código de la clase C_ChartFloatingRAD sufrió algunos pequeños cambios. Pero, como los cambios no se produjeron en toda la clase, me centraré solo en el fragmento donde de hecho se realizó el cambio.

A continuación, se puede ver el fragmento, seguido de una explicación del mismo.

068. inline void AdjustTemplate(const bool bFirst = false)
069.                    {
070. #define macro_AddAdjust(A) {                   \
071.            (*Template).Add(A, "size_x", NULL); \
072.            (*Template).Add(A, "size_y", NULL); \
073.            (*Template).Add(A, "pos_x", NULL);  \
074.            (*Template).Add(A, "pos_y", NULL);  \
075.                            }
076. #define macro_GetAdjust(A) {                                                                        \
077.            m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));  \
078.            m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));  \
079.            m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \
080.            m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \
081.                            }
082. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
083.                            
084.                            C_AdjustTemplate *Template;
085.                            
086.                            if (bFirst)
087.                            {
088.                                    Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
089.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
090.                                    AdjustEditabled(Template, true);
091.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
092.                            m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
093.                            m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
094.                            m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
095.                            (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
096.                            (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
097.                            (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
098.                            (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
099.                            (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
100.                            (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
101.                            (*Template).Execute();
102.                            if (bFirst)
103.                            {
104.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
105.                                    m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
106.                                    AdjustEditabled(Template, false);
107.                            };
108.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
109.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
110.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
111. 
112.                            delete Template;
113.                            
114.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
115.                            ChartRedraw(m_Info.WinHandle);
116. 
117. #undef macro_PointsToFinance
118. #undef macro_GetAdjust
119. #undef macro_AddAdjust
120.                    }

Fragmento del código fuente de la clase C_ChartFloatingRAD

En este fragmento, es posible que no notes ninguna gran diferencia. Pero sí las hay. La diferencia se encuentra en la línea 88, de forma bastante sutil. Entonces, si tomas el código de la clase C_ChartFloatingRAD, que se encuentra en el artículo anterior, y exactamente en la línea 88 lo cambias como se muestra en este fragmento aquí, podrás hacer uso del nuevo modelo de datos que vamos a utilizar.

Se puede observar que, a diferencia del código original, en este solo estamos definiendo una cadena. ¿Y por qué esto? El motivo es que ahora no utilizaremos más una plantilla como antes. Ahora utilizaremos un recurso. O, dicho de otro modo, ahora la plantilla estará incorporada en el ejecutable del indicador. Por ello, ya no necesitamos informar muchos más datos.

Sin embargo, este cambio pronto generará una pregunta. ¿Cómo usaremos una plantilla como recurso si no podemos de hecho compilar una plantilla como parte de un ejecutable, es decir, incluirla como un recurso? En realidad, sí podemos incluir cualquier cosa en un ejecutable. El gran detalle es: ¿Cómo debemos, de hecho, hacer esto?

Para comprender esta cuestión, es necesario mirar el código de la clase C_AdjustTemplate, para comprender por qué se realizó el cambio en la línea 88 de la clase C_ChartFloatingRAD. Bien, el código de la clase C_AdjustTemplate puede verse en su totalidad justo a continuación. Ya que en él ocurrieron más cambios, aunque no fueron tan grandes, será interesante entender qué sucedió realmente.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade"
007. #define def_BTN_BUY  def_PATH_BTN + "\\BUY.bmp"
008. #define def_BTN_SELL def_PATH_BTN + "\\SELL.bmp"
009. #define def_BTN_DT   def_PATH_BTN + "\\DT.bmp"
010. #define def_BTN_SW   def_PATH_BTN + "\\SW.bmp"
011. #define def_BTN_MAX  def_PATH_BTN + "\\MAX.bmp"
012. #define def_BTN_MIN  def_PATH_BTN + "\\MIN.bmp"
013. #define def_IDE_RAD  "Files\\Chart Trade\\IDE_RAD.tpl"
014. //+------------------------------------------------------------------+
015. #resource "\\" + def_BTN_BUY
016. #resource "\\" + def_BTN_SELL
017. #resource "\\" + def_BTN_DT
018. #resource "\\" + def_BTN_SW
019. #resource "\\" + def_BTN_MAX
020. #resource "\\" + def_BTN_MIN
021. #resource "\\" + def_IDE_RAD as string IdeRad;
022. //+------------------------------------------------------------------+
023. class C_AdjustTemplate
024. {
025.    private :
026.            string m_szName[],
027.                   m_szFind[],
028.                   m_szReplace[],
029.                   m_szFileName;
030.            int    m_maxIndex,
031.                   m_FileIn,
032.                   m_FileOut;
033.            bool   m_bFirst;
034. //+------------------------------------------------------------------+
035.    public  :
036. //+------------------------------------------------------------------+
037.            C_AdjustTemplate(const string szFile, const bool bFirst = false)
038.                    :m_maxIndex(0),
039.                     m_szFileName(szFile),
040.                     m_bFirst(bFirst),
041.                     m_FileIn(INVALID_HANDLE),
042.                     m_FileOut(INVALID_HANDLE)
043.                    {
044.                            ResetLastError();                               
045.                            if (m_bFirst)
046.                            {
047.                                    int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE);
048.                                    FileWriteString(handle, IdeRad);
049.                                    FileClose(handle);
050.                            }
051.                            if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE)        SetUserError(C_Terminal::ERR_FileAcess);
052.                            if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
053.                    }
054. //+------------------------------------------------------------------+
055.            ~C_AdjustTemplate()
056.                    {
057.                            FileClose(m_FileIn);
058.                            FileClose(m_FileOut);
059.                            FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
060.                            ArrayResize(m_szName, 0);
061.                            ArrayResize(m_szFind, 0);
062.                            ArrayResize(m_szReplace, 0);
063.                    }
064. //+------------------------------------------------------------------+
065.            void Add(const string szName, const string szFind, const string szReplace)
066.                    {
067.                            m_maxIndex++;
068.                            ArrayResize(m_szName, m_maxIndex);
069.                            ArrayResize(m_szFind, m_maxIndex);
070.                            ArrayResize(m_szReplace, m_maxIndex);
071.                            m_szName[m_maxIndex - 1] = szName;
072.                            m_szFind[m_maxIndex - 1] = szFind;
073.                            m_szReplace[m_maxIndex - 1] = szReplace;
074.                    }
075. //+------------------------------------------------------------------+
076.            string Get(const string szName, const string szFind)
077.                    {
078.                            for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
079.                            
080.                            return NULL;
081.                    }
082. //+------------------------------------------------------------------+
083.            void Execute(void)
084.                    {
085.                            string sz0, tmp, res[];
086.                            int count0 = 0, i0;
087.                                                            
088.                            if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
089.                            {
090.                                    sz0 = FileReadString(m_FileIn);
091.                                    if (sz0 == "<object>") count0 = 1;
092.                                    if (sz0 == "</object>") count0 = 0;
093.                                    if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
094.                                    {
095.                                            if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off")))
096.                                                    sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
097.                                            i0 = (count0 == 1 ? 0 : i0);
098.                                            for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
099.                                            for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
100.                                            {
101.                                                    if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
102.                                                    else m_szReplace[c0] = res[1];
103.                                            }
104.                                    }
105.                                    FileWriteString(m_FileOut, sz0 + "\r\n");
106.                            };
107.                    }
108. //+------------------------------------------------------------------+
109. };
110. //+------------------------------------------------------------------+

Código fuente de la clase C_AdjustTemplate

Este código anterior puede parecer extraño para muchos que están comenzando. Principalmente para aquellos que no programan de forma profesional. Aunque es bastante extravagante, evita que tengamos que transferir los archivos como se hacía en el artículo anterior. Aquí es donde incorporamos todo dentro del ejecutable. Pero esto no es gratis. Esto tiene un precio. El coste principal es: No podemos cambiar el directorio o el nombre del ejecutable una vez que haya sido compilado. Podemos hacerlo, pero para ello será necesario hacer otras cosas, de las cuales no explicaré en este momento cómo proceder.

Observen con atención las definiciones entre las líneas 6 y 13. Noten que en su mayoría, tenemos algo que nos es bastante familiar desde hace un buen tiempo. Observen con más atención la línea 13. Se trata de una plantilla. La misma plantilla que se utilizaba antes. Pero ahora haremos que deje de ser un archivo aparte y comience a formar parte del ejecutable. La forma de lograrlo se explica a continuación.

Observen que, entre las líneas 15 y 20, hacemos las definiciones como es habitual. Esto se hace cuando queremos añadir imágenes o sonidos. Pero en la línea 21 tenemos algo diferente. Algo realmente muy poco visto normalmente en códigos. Esto se debe a que normalmente no utilizamos alias o apodos en la programación MQL5 cuando vamos a incluir recursos en los ejecutables. Para tener una idea de lo que estará ocurriendo, puedes mirar en la documentación RECURSOS. Pero además de lo que está allí, también necesitas entender algunos otros detalles. Así podrás comprenderlo todo.

Los alias o apodos son muy comunes en algunos tipos de lenguajes. Por ejemplo, en Visual Basic o en VBA (Visual Basic for Applications), que se usa mucho en Excel, estos alias sirven como una forma de acceder a las cosas de una manera un poco diferente. Normalmente, cuando accedemos a un recurso usamos "::" que muchas veces se usa como solucionador de ámbito. Pero cuando lo usamos para acceder a un recurso, normalmente usamos el nombre de definición del recurso. Esto parece complicado, pero es mucho más simple de lo que parece. Para comprenderlo, mira el fragmento a continuación:

01. #define def_BTN_BUY  "Images\\Market Replay\\Chart Trade\\BUY.bmp"
02. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp"
03. #resource "\\" + def_BTN_BUY
04. #resource "\\" + def_BTN_SELL
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     long id;
09.     string sz;
10.     
11.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::" + def_BTN_BUY);
13.     ResourceSave("::" + def_BTN_SELL, "BTN_SELL.bmp");
14.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
15.     
16.     return INIT_SUCCEEDED;
17. }
18. //+------------------------------------------------------------------+

Fragmento 01 - Ejemplo de usos

En este fragmento 01, podemos ver y entender mejor cómo funcionan las cosas en el primer nivel de uso de recursos. En ese momento, olvida la clase C_AdjustTemplate. Vamos a aprender primero a utilizar correctamente los recursos para transportar más fácilmente nuestros códigos ya compilados.

En las líneas 1 y 2, definimos dos cadenas de caracteres. El contenido indicado por ellas será compilado y embutido en el ejecutable por el compilador, debido a las líneas 3 y 4. Hasta este punto, no tenemos nada grandioso o complicado. Estamos trabajando de la misma manera habitual.

En la línea 11, indicamos que vamos a crear un objeto. En este caso, un Bitmap. Nuevamente, nada espectacular. Pero ahora viene la primera fase, en la que utilizaremos los recursos incorporados en el ejecutable. Observen la línea 12. En esta línea, estamos usando el "::" para indicar que haremos uso de un recurso. En este caso, un recurso presente en el ejecutable. Podríamos estar refiriéndonos a otro programa, pero, para no complicar las cosas, vamos a entender primero este concepto más sencillo. Al leer esta línea 12, el compilador entenderá lo siguiente:

12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::Images\\Market Replay\\Chart Trade\\BUY.bmp");

Pero, si miras el contenido de las cadenas de texto en el objeto, no verás lo que se muestra arriba. Verás otra cosa. Pero por el momento, continuemos con lo básico. Esto es necesario para entender las cosas de hecho.

Bien. Esta línea 12 fue fácil de entender. Pero, ¿qué pasa con las demás líneas a continuación? Pues bien, ahora comienzan las diferencias. Normalmente, no verás tales cosas en los códigos. Pero es importante que las entiendas para comprender realmente el indicador Chart Trade.

La línea 13 tomará el recurso señalado y lo guardará con el nombre y en la ubicación indicadas. Por tanto, para el compilador, la línea 13 sería algo así:

13.     ResourceSave("::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");

Una vez más, aquí estoy utilizando el concepto más básico. Esta vez, el asunto es un poco más complicado. Pero puedes notar algo en esta llamada ResourceSave. El recurso presente en el ejecutable se guardará como un archivo convencional. Básicamente, sería el equivalente a usar la función FileMove. O sea, entiende la línea 13 como si fuera algo así:

FileMove("::Images\\Market Replay\\Chart Trade\\SELL.bmp", 0, "\\Files\\BTN_SELL.bmp", FILE_REWRITE);

Así es como funcionarían las cosas en la práctica.

Entonces, en la línea 14, en lugar de acceder a un recurso interno del ejecutable, haremos uso indirecto del recurso. Esto es porque lo guardamos en un archivo en la línea 13 y ahora apuntamos a él en la línea 14. Pero presta atención a esto. A diferencia de la línea 12, en la línea 14 estamos usando, de hecho, un archivo físico presente en el disco. Este permanecerá allí hasta que se borre o modifique.

El uso de este tipo de cosas en las líneas 13 y 14 no es muy habitual en programas MQL5. Normalmente, iniciamos el recurso en el ejecutable y lo usamos directamente dentro del ejecutable. Es así como hacemos las cosas de hecho. Pero, como mencioné antes, la historia es un poco más complicada. Sin embargo, interesante. No necesitas realmente colocar los recursos en cada uno de tus ejecutables. Además, esto complicaría mucho la estandarización. Puedes hacer uso de algo muy común en programación. Usar un archivo para los recursos. Normalmente, en el caso de Windows, esto se colocaría en una DLL. De este modo, podemos estandarizar un poco las cosas.

En MQL5, puedes hacer algo similar. Pero ten en cuenta que es necesario estandarizar algún tipo de código. De lo contrario, generarás una cantidad monstruosa de datos inútiles.

¿Y cómo podemos hacer esto en MQL5? Simple. Bastará indicar el nombre del archivo en el momento en que vayas a hacer uso del recurso. Recuerda que mencioné que las cosas eran mucho más complicadas. Eso es precisamente lo que ocurría. Allí estaba ignorando el nombre del ejecutable. Entonces, supongamos que tienes un ejecutable llamado ICONS.LIB, y que en él están las mismas imágenes del fragmento 01. Y que este ejecutable, ICONS.LIB, está en la raíz de la carpeta Indicators. Para hacer lo mismo, pero ahora usando el ICONS.LIB, el fragmento 01 quedaría como se muestra a continuación:

01. //+------------------------------------------------------------------+
02. #define def_BTN_BUY  "Images\\Market Replay\\Chart Trade\\BUY.bmp"
03. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp"
04. #define def_LIB      "\\Indicators\\Icons.Lib"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     long id;
09.     string sz;
10.     
11.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, def_LIB + "::" + def_BTN_BUY);
13.     ResourceSave(def_LIB + "::" + def_BTN_SELL, "BTN_SELL.bmp");
14.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
15.             
16.     return INIT_SUCCEEDED;
17. }
18. //+------------------------------------------------------------------+

Fragmento 02

Noten que ahora estamos definiendo un archivo ejecutable. Por tanto, toda la explicación dada anteriormente se aplica a este fragmento 02. Por supuesto, también necesitarás entender cómo el compilador ve el código. Entonces, para ayudar, puedes mirar el código traducido a continuación.

01. //+------------------------------------------------------------------+
02. int OnInit()
03. {
04.     long id;
05.     string sz;
06.     
07.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
08.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");
09.     ResourceSave("\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");
10.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
11.             
12.     return INIT_SUCCEEDED;
13. }
14. //+------------------------------------------------------------------+

Traducción del fragmento 02 (Código extendido)

Es cierto que difícilmente verás a alguien escribiendo este código extendido. Esto se debe a que el mantenimiento del código es mucho más laborioso. Pero para el compilador, las cosas funcionarán de la misma manera.

Pues bien. Ahora que he explicado esta parte más sencilla, volvamos a la cuestión de los alias. Cuando utilizas alias aquí en MQL5, "fuerzas" al compilador a actuar de una manera un poco diferente. Lo que ocurrirá es que usará un sistema de compresión de datos para reducir el tamaño de los archivos. Al mismo tiempo, ignorará cualquier tipo de uso directo del recurso. Es decir, NO podrás usar directamente la definición.. Es muy importante que comprendas esto. Puedes añadir cualquier cosa en el ejecutable. Pero NO podrás usar directamente lo que se ha añadido.

De hecho, para poder usar lo que se ha añadido en el ejecutable mediante un alias, deberás utilizar dicho alias y no el recurso. ¿Te ha confundido? Calma. Intentemos entender esto.

01. #resource "\\Images\\euro.bmp" as bitmap euro[][] 
02. #resource "\\Images\\dollar.bmp" 
03. //+------------------------------------------------------------------+ 
04. void Image(string name,string rc,int x,int y) 
05. { 
06.     ObjectCreate(0, name, OBJ_BITMAP_LABEL, 0, 0, 0); 
07.     ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); 
08.     ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); 
09.     ObjectSetString(0, name, OBJPROP_BMPFILE, rc); 
10. } 
11. //+------------------------------------------------------------------+ 
12. void OnStart() 
13. { 
14.     for(int x = 0; x < ArrayRange(euro, 1); x++) 
15.             euro[ArrayRange(euro, 1) / 2][x] = 0xFFFF0000; 
16.     ResourceCreate("euro_icon", euro, ArrayRange(euro, 1), ArrayRange(euro, 0), 0, 0, ArrayRange(euro, 1), COLOR_FORMAT_ARGB_NORMALIZE); 
17.     Image("Euro" , "::euro_icon", 10, 40); 
18.     Image("USD", "::Images\\dollar.bmp", 15 + ArrayRange(euro,1), 40); 
19.     Image("E2", "::Images\\euro.bmp", 20 + ArrayRange(euro, 1) * 2, 40);
20. }
21. //+------------------------------------------------------------------+ 

Código fuente presente en la documentación

Este código fuente anterior muestra exactamente esto. Esta es una de las maneras de utilizar recursos cuando empleamos alias. El alias se definió en la línea 1. A partir de ese momento, no debemos usar más el nombre del recurso. Debemos usar su alias. Por eso, al ejecutar la línea 19, tendremos un error, pues estamos intentando usar el nombre del recurso cuando en realidad debemos usar su alias.

En el caso del código mostrado anteriormente, el alias nos indica cuál es la estructura que se debe utilizar para modelar los datos. Podría ser cualquiera de las posibles. También podríamos utilizar herramientas para transformar un tipo de dato en otro. De esta manera, es posible hacer muchas más cosas de las que normalmente muchos imaginan ser posible. La diferencia entre este código, tomado prestado de la documentación, y el que estoy utilizando es precisamente la naturaleza de la información.

En este código de documentación, estamos usando un sistema para poner un ejemplo del uso de alias. En este caso, sin embargo, no es necesario guardar los datos presentes como recursos del ejecutable. El motivo es que se trata de datos de los que podemos hacer uso directamente. ¿Pero y si el recurso es una plantilla? De acuerdo con las normas de uso, MetaTrader 5 no nos permite utilizar plantillas como recursos. Entonces es necesario retirar la plantilla del interior del ejecutable, de manera que deje de ser un recurso y pase a ser un archivo convencional.

Esto podría lograrse de la misma forma que se mostró en el código del fragmento 1. En la línea 13, convertimos una imagen incluida en el ejecutable como recurso en un archivo para poder utilizarla en un objeto en la línea 14.

Bien, esto funciona en teoría. En la práctica, la cosa es un poco más complicada. Si observas, verás que en la línea 13 del código del fragmento 01 estamos utilizando el nombre del recurso y no un alias. La forma de trabajar con alias es algo diferente a la de los recursos. Pero ese no es, de hecho, nuestro principal problema. Nuestro principal problema es que no podemos incluir plantillas como recursos dentro de ejecutables. Por ello, centramos nuestra atención en el código de la clase C_AdjustTemplate. Esto para comprender cómo logré superar estos dos problemas. El de no poder incluir plantillas en ejecutables. Y el de cómo hacer para usar la plantilla almacenada en el ejecutable.

Pues bien, en la línea 13 de la clase C_AdjustTemplate, defino la plantilla que voy a utilizar. Esta es la misma que apareció en artículos anteriores sobre Chart Trade. Pero observa que en la línea 21, donde convierto la definición en un recurso, no lo hago para usar un recurso. Sin embargo, lo hago de manera que uso un alias. Este alias se basa en el tipo string, por lo que todo el código presente en el archivo de la plantilla se añade al ejecutable como si fuera una larga cadena de caracteres. Sin embargo, esta cadena es como una gran constante. Es preciso que entiendas muy bien esto. El archivo de la plantilla será tratado por el sistema como si fuera una constante, aunque en realidad no lo sea, para que el entendimiento sea correcto.

Se supone que el archivo de la plantilla es una cadena de texto con el alias IdeRad dentro del ejecutable. Podemos comenzar a pensar en cómo trabajar con él.

Lo primero que debemos entender es que no podemos utilizar esta plantilla, cuyo alias es IdeRad, directamente en el objeto OBJ_CHART. Esto no es posible. Necesitamos transformar estos datos en un archivo nuevamente. Sin embargo, la función ResourceSave no puede manejar este tipo de caso. Esto se debe a que la plantilla NO ES UN RECURSO. Pero podemos hacer algo un poco diferente. De esta forma surge el código presente entre las líneas 45 y 50.

Atención: Estoy haciendo uso de esta manera. No porque sea obligatorio, sino porque no quiero cambiar el código ya funcional de la clase. Podríamos leer directamente el contenido de la plantilla usando el alias IdeRad y hacer las modificaciones que sean necesarias. Sin embargo, esto complicaría la lógica ya creada y probada.

Entonces, vamos entender qué está pasando entre las líneas 45 y 50. Cuando el constructor es llamado, para poder manipular la plantilla, informamos cuál es el nivel de la llamada, así como el nombre del archivo que se va a acceder. Así, si se trata de la primera llamada, la línea 45 permitirá crear el archivo de la plantilla. El archivo será construido en la línea 47 y cerrado en la línea 49. Si solo existieran estas dos líneas, la plantilla tendría un contenido vacío. Pero en la línea 48 ocurre la magia.

En esta línea 48, colocamos todo el contenido de la cadena dentro del archivo. ¿Y qué cadena es esta? La que está en la variable IdeRad. Ops. Espera un momento. ¿Quiere decir que almacenamos la plantilla dentro del ejecutable como un recurso? Para evitar problemas, le damos un alias. Luego, cuando queremos recuperar su contenido, tomamos el contenido de este alias y lo colocamos en un archivo. ¿Es esto? Sí. Exactamente eso. Ahora te estarás preguntando: ¿por qué nadie pensó en hacer esto o en mostrar cómo se hace? Bueno, no lo sé. Tal vez porque nadie lo haya intentado de verdad. O puede que no pudieran imaginar cómo hacerlo.

Una vez que se cierra el archivo, en la línea 49, todo el resto del proceso se lleva a cabo de manera idéntica a lo explicado anteriormente. Esto se debe a que ahora MetaTrader 5 no tendrá que lidiar con un recurso, sino con un archivo del disco.

Por último, existe un pequeño detalle que no puedo pasar por alto. Muchos se imaginarán cosas o intentarán manipular los datos para entender lo que está pasando al interactuar con el indicador Chart Trade. La cuestión son los botones. Si lanzas el indicador en el gráfico, que estará presente en el anexo, no entenderás cómo se accede a los botones. Esto solo ocurrirá si tienes poca experiencia en MQL5. No encontrarás en ningún lugar las imágenes que se han declarado en los botones. Si miras el contenido de la plantilla, verás algo como:

bmpfile_on=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp

bmpfile_off=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp

Y esto no tiene ningún sentido. No si estás iniciando tus estudios en MQL5. Pero si reproduces esta misma plantilla en el gráfico, notarás que se presenta de manera correcta. ¿Cómo es posible? ¿Dónde están las imágenes que está referenciando? Esta es la cuestión. Las imágenes están dentro del ejecutable. Es decir, en la plantilla se indica directamente la imagen que debe ser utilizada. Es lo mismo que se veía en el código visto y en la traducción del fragmento 02. El código en cuestión es precisamente la línea 08, que muestro de nuevo a continuación para facilitar la comprensión:

08.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");

Observen cómo las cadenas son similares, tanto en el código como en la plantilla. Esto demuestra el escaso aprovechamiento del MetaTrader 5 y del lenguaje MQL5. Créanme, el MetaTrader 5 y el MQL5 pueden hacer mucho más de lo que puedan imaginar. Incluso con las limitaciones que muchos dicen que tiene, podemos hacer mucho, pero mucho más de lo que parece. Sin recurrir a otros lenguajes.


Conclusión

En este artículo mostré cómo hacer algo que muchos no creen posible: usar plantillas dentro de un ejecutable. Aunque muestro este conocimiento de una forma fácil de comprender, este conocimiento me permite hacer muchas más cosas. Es cierto que podemos hacer que este mismo trabajo mostrado aquí sea mucho más extenso usando DLL.

Todo depende de la creatividad, capacidad y personalidad del programador. Hay gente que dice que es imposible hacer algo. Otros dicen que no existen medios. Pero hay gente que lo intenta y lo consigue. No seas uno de los que desisten al ver el desafío. Sé uno de los que resuelve el problema. Mi lema es:

Un verdadero programador profesional resuelve el problema. Los demás solo ven el problema.


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

Archivos adjuntos |
Indicators.zip (149.64 KB)
Desarrollo de un sistema de repetición (Parte 47): Proyecto Chart Trade (VI) Desarrollo de un sistema de repetición (Parte 47): Proyecto Chart Trade (VI)
En este artículo finalizaremos el indicador Chart Trade, haciéndolo funcional hasta el punto de poder usarlo junto con algún Expert Advisor. Entonces, en este artículo finalizaremos el indicador Chart Trade, haciéndolo funcional hasta el punto de poder usarlo junto con algún Expert Advisor. Esto nos permitirá acceder y trabajar con el indicador, como si estuviera realmente vinculado al Expert Advisor. Pero lo haremos de una manera mucho más interesante que en el pasado.
Algoritmos de optimización de la población: Algoritmo genético binario (Binary Genetic Algorithm, BGA). Parte II Algoritmos de optimización de la población: Algoritmo genético binario (Binary Genetic Algorithm, BGA). Parte II
En este artículo, analizaremos el algoritmo genético binario (BGA), que modela los procesos naturales que ocurren en el material genético de los seres vivos en la naturaleza.
Desarrollo y prueba de los sistemas comerciales Aroon Desarrollo y prueba de los sistemas comerciales Aroon
En este artículo, aprenderemos a construir un sistema comercial Aroon, aprendiendo asimilando los fundamentos de los indicadores y los pasos necesarios para crear un sistema comercial basado en el indicador Aroon. Una vez creado este sistema comercial, comprobaremos si puede ser rentable o necesita una mayor optimización.
Desarrollamos de un asesor multidivisa (Parte 1): Funcionamiento conjunto de varias estrategias comerciales Desarrollamos de un asesor multidivisa (Parte 1): Funcionamiento conjunto de varias estrategias comerciales
Existen bastantes estrategias comerciales distintas. Para diversificar los riesgos y aumentar la estabilidad de los resultados comerciales, puede resultar útil utilizar varias estrategias que funcionen en paralelo. Pero si cada estrategia se implementa como un asesor independiente, se hace mucho más difícil gestionar su trabajo conjunto en una cuenta comercial. Para resolver este problema, es deseable implementar el funcionamiento de diferentes estrategias de negociación en un asesor.