English Русский Deutsch 日本語 Português
preview
Desarrollo de un sistema de repetición (Parte 75): Un nuevo Chart Trade (II)

Desarrollo de un sistema de repetición (Parte 75): Un nuevo Chart Trade (II)

MetaTrader 5Ejemplos |
282 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, "Desarrollo de un sistema de repetición (Parte 74): Un nuevo Chart Trade (I)", en el que básicamente expliqué el código del indicador Chart Trade. Comenté las razones por las que deberías usar un método u otro para programar Chart Trade. Detallé algunos aspectos del código, pero quedó pendiente hablar y explicar el código principal.

Uno de los motivos por los que no se mostró el código principal es que se eliminaron más de 100 líneas del mismo. Entonces, pensé en una forma práctica y sencilla de mostrar lo que ocurrió en él. La mejor manera que encontré es la que se presentará en este artículo.

A continuación, veremos el código principal del Chart Trade. Sin embargo, debes tener en cuenta que puedes integrar este código directamente en un Expert Advisor si lo deseas. Claro, con algunos pequeños cambios. Se explicarán en su momento durante este artículo. Pero no olvides los motivos explicados en el artículo anterior para incluir el código en un indicador en lugar de un Expert Advisor. No obstante, eres libre de usar el código como más te convenga.

Así que, sin más preámbulos, vamos al grano.


Entendemos el código fuente de la clase C_ChartFloatingRAD

El código de la clase C_ChartFloatingRAD se encuentra en el archivo de cabecera con el mismo nombre. A continuación puede verse prácticamente en su totalidad. La única parte que falta es el procedimiento DispatchMessage.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A)
008. #define macro_CloseIndicator(A)   {           \
009.                OnDeinit(REASON_INITFAILED);   \
010.                SetUserError(A);               \
011.                return;                        \
012.                                  }
013. //+------------------------------------------------------------------+
014. class C_ChartFloatingRAD : private C_Terminal
015. {
016.    private   :
017.       enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
018.       struct st00
019.       {
020.          short    x, y, minx, miny,
021.                   Leverage;
022.          string   szObj_Chart,
023.                   szObj_Editable,
024.                   szFileNameTemplate;
025.          long     WinHandle;
026.          double   FinanceTake,
027.                   FinanceStop;
028.          bool     IsMaximized,
029.                   IsDayTrade,
030.                   IsSaveState;
031.          struct st01
032.          {
033.             short  x, y, w, h;
034.             color  bgcolor;
035.             int    FontSize;
036.             string FontName;
037.          }Regions[MSG_NULL];
038.       }m_Info;
039.       C_Mouse      *m_Mouse;
040. //+------------------------------------------------------------------+
041.       void CreateWindowRAD(int w, int h)
042.          {
043.             m_Info.szObj_Chart = "Chart Trade IDE";
044.             m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
045.             ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
046.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
047.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
048.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
049.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
050.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
051.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
052.             m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
053.          };
054. //+------------------------------------------------------------------+
055.       void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
056.          {
057.             for (eObjectsIDE c0 = 0; c0 <= MSG_STOP_VALUE; c0++)
058.                if (bArg)
059.                {
060.                   Template.Add(EnumToString(c0), "bgcolor", NULL);
061.                   Template.Add(EnumToString(c0), "fontsz", NULL);
062.                   Template.Add(EnumToString(c0), "fontnm", NULL);
063.                }
064.                else
065.                {
066.                   m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
067.                   m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
068.                   m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
069.                }
070.          }
071. //+------------------------------------------------------------------+
072. inline void AdjustTemplate(const bool bFirst = false)
073.          {
074. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
075.             
076.             C_AdjustTemplate *Template;
077.             
078.             if (bFirst)
079.             {
080.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
081.                for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
082.                {
083.                   (*Template).Add(EnumToString(c0), "size_x", NULL);
084.                   (*Template).Add(EnumToString(c0), "size_y", NULL);
085.                   (*Template).Add(EnumToString(c0), "pos_x", NULL);
086.                   (*Template).Add(EnumToString(c0), "pos_y", NULL);
087.                }
088.                AdjustEditabled(Template, true);
089.             }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
090.             if (_LastError >= ERR_USER_ERROR_FIRST)
091.             {
092.                delete Template;
093.                
094.                return;
095.             }
096.             m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
097.             m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
098.             m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
099.             (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
100.             (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
101.             (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
102.             (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
103.             (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
104.             (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
105.             if (!(*Template).Execute())
106.             {
107.                delete Template;
108. 
109.                macro_CloseIndicator(C_Terminal::ERR_FileAcess);
110.             };
111.             if (bFirst)
112.             {
113.                for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
114.                {
115.                   m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x"));
116.                   m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y"));
117.                   m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x"));
118.                   m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y"));
119.                }
120.                m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
121.                AdjustEditabled(Template, false);
122.             };
123.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
124.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
125.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
126. 
127.             delete Template;
128.             
129.             ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
130.             ChartRedraw(m_Info.WinHandle);
131. 
132. #undef macro_PointsToFinance
133.          }
134. //+------------------------------------------------------------------+
135.       eObjectsIDE CheckMousePosition(const short x, const short y)
136.          {
137.             int xi, yi, xf, yf;
138.             
139.             for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
140.             {
141.                xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
142.                yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
143.                xf = xi + m_Info.Regions[c0].w;
144.                yf = yi + m_Info.Regions[c0].h;
145.                if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
146.             }
147.             return MSG_NULL;
148.          }
149. //+------------------------------------------------------------------+
150. inline void DeleteObjectEdit(void)
151.          {
152.             ChartRedraw();
153.             ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable);
154.          }
155. //+------------------------------------------------------------------+
156.       template <typename T >
157.       void CreateObjectEditable(eObjectsIDE arg, T value)
158.          {
159.             long id = GetInfoTerminal().ID;
160.             
161.             DeleteObjectEdit();
162.             CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
163.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
164.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
165.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
166.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
167.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
168.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
169.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
170.             ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
171.             ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
172.             ChartRedraw();
173.          }
174. //+------------------------------------------------------------------+
175.       bool RestoreState(void)
176.          {
177.             uCast_Double info;
178.             bool bRet;
179.             C_AdjustTemplate *Template;
180.             
181.             if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue))
182.             {
183.                m_Info.x = (short) info._16b[0];
184.                m_Info.y = (short) info._16b[1];
185.                m_Info.minx = (short) info._16b[2];
186.                m_Info.miny = (short) info._16b[3];
187.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl");
188.                if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else
189.                {
190.                   (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL);
191.                   (*Template).Add("MSG_TAKE_VALUE", "descr", NULL);
192.                   (*Template).Add("MSG_STOP_VALUE", "descr", NULL);
193.                   (*Template).Add("MSG_DAY_TRADE", "state", NULL);
194.                   (*Template).Add("MSG_MAX_MIN", "state", NULL);
195.                   if (!(*Template).Execute()) bRet = false; else
196.                   {
197.                      m_Info.IsDayTrade  = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1;
198.                      m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1;
199.                      m_Info.Leverage    = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr"));
200.                      m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr"));
201.                      m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr"));
202.                   }
203.                };               
204.                delete Template;
205.             };
206.             
207.             GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
208.             
209.             return bRet;
210.          }
211. //+------------------------------------------------------------------+
212.    public   :
213. //+------------------------------------------------------------------+
214.       C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop)
215.          :C_Terminal(0)
216.          {
217.             m_Mouse = MousePtr;
218.             m_Info.IsSaveState = false;
219.             if (!IndicatorCheckPass(szShortName)) return;
220.             if (!RestoreState())
221.             {
222.                m_Info.Leverage    = Leverage;
223.                m_Info.IsDayTrade  = true;
224.                m_Info.FinanceTake = FinanceTake;
225.                m_Info.FinanceStop = FinanceStop;
226.                m_Info.IsMaximized = true;
227.                m_Info.minx = m_Info.x = 115;
228.                m_Info.miny = m_Info.y = 64;
229.             }
230.             CreateWindowRAD(170, 210);
231.             AdjustTemplate(true);
232.          }
233. //+------------------------------------------------------------------+
234.       ~C_ChartFloatingRAD()
235.          {
236.             ChartRedraw();
237.             ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart);
238.             if (!m_Info.IsSaveState)
239.                FileDelete(m_Info.szFileNameTemplate);
240.                         
241.             delete m_Mouse;
242.          }
243. //+------------------------------------------------------------------+
244.       void SaveState(void)
245.          {
246. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
247.             
248.             uCast_Double info;
249.             
250.             info._16b[0] = m_Info.x;
251.             info._16b[1] = m_Info.y;
252.             info._16b[2] = m_Info.minx;
253.             info._16b[3] = m_Info.miny;
254.             macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue);
255.             m_Info.IsSaveState = true;
256.             
257. #undef macro_GlobalVariable
258.          }
259. //+------------------------------------------------------------------+
260.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
261.          {

.... The internal code of this procedure will be seen in the next article

386.          }
387. //+------------------------------------------------------------------+
388. };
389. //+------------------------------------------------------------------+
390. #undef macro_NameGlobalVariable
391. #undef macro_CloseIndicator
392. //+------------------------------------------------------------------+

Código fuente del archivo C_ChartFloatingRAD.mqh

Este código fuente ya no contiene las líneas que se eliminaron del código original. También incluye los cambios necesarios para que todo funcione como se esperaba. Dado que supongo que muchos no habrán visto el código original, quiero que presten atención a las explicaciones que se darán para entender su funcionamiento. Pero, principalmente, cómo logra hacer que Chart Trade opere y se cree. Porque, si observas y buscas, no encontrarás los elementos que se ven en Chart Trade. Entonces, ¿cómo es esto posible? ¿Cómo es posible que un código que prácticamente no contiene elementos gráficos pueda mostrar elementos gráficos? ¿Qué tipo de magia o hechizo se está llevando a cabo aquí?

Bueno, mi estimado lector, no se trata de magia. Es programación y uso de diversas técnicas diferentes. Pero, al final, garantizan que Chart Trade se presente de la manera correcta. Y lo más importante, lo hacen funcional, seguro y estable.

En la versión original, algunos elementos eran públicos, como las estructuras y la enumeración que se observa en la línea 17. Sin embargo, ya se había tomado la precaución de evitar que las variables se filtraran fuera del código. El encapsulamiento ya era una medida implementada y adoptada. Entonces, ¿por qué motivo ahora no tenemos esos mismos elementos públicos? La razón es que la idea es generar un protocolo y utilizarlo sin necesariamente requerir la clase C_ChartFloatingRAD para decodificar los datos. Esto lo veremos más adelante en este artículo.

Todo lo que necesitamos en este código en términos de variables está declarado dentro de la estructura que comienza en la línea 18. Ahora presta mucha atención a los tipos que se están utilizando. La mayoría no son de 32 bits, o mejor dicho, del grupo INT. ¿Por qué? Incluso las variables que claramente se utilizarán como coordenadas de pantalla son de 16 bits, es decir, del grupo short.

A diferencia de lo que muchos suelen hacer, al establecer esas variables como tipo estándar INT, no veo la necesidad de hacerlo. En el caso de la resolución de pantalla, un valor de 16 bits es más que suficiente para la tecnología actual. El valor máximo de 65535 es suficiente para la gráfica en una pantalla con resolución 4K. De hecho, dicho valor puede manejar tranquilamente coordenadas en una pantalla 8K o incluso más grande. Por lo tanto, no encuentro motivo para usar el tipo INT aquí. Además, el uso del tipo SHORT nos permite compactar las cosas, como se verá más adelante.

Una vez explicado este pequeño detalle, puedes observar que en la línea 31 hay otra estructura interna a la primera. Dicha estructura contiene la posición relativa a los elementos gráficos. Dichos elementos son parte integral de Chart Trade y con los que el usuario interactuará directamente.

Hay dos macros al principio del código. Por ahora no te preocupes por ellas, simplemente debes saber que existen. Lo más importante es saber dónde están declaradas, ya que se hará uso de ellas a lo largo del código. Tal vez la que te genere más inquietud en este momento sea la macro definida en la línea ocho, pero pronto comprenderemos qué hace realmente.

En cualquier caso, todo se resume a esto. En la línea 14, heredamos privadamente la clase C_Terminal con el propósito de ampliar sus funcionalidades y crear así Chart Trade. Entre las líneas 20 y 39 se encuentran las declaraciones de las variables internas de la clase, es decir, privadas. Ahora podemos pasar a la parte donde se describe el código. Para simplificar, analizaremos función por función conforme aparecen en el código. Empezaremos por el procedimiento CreateWindowRAD, que está declarado en la línea 41.

Este procedimiento se llama única y exclusivamente desde la línea 230 y no se invoca desde ningún otro punto. Su función es muy sencilla: crea un objeto OBJ_CHAT en el gráfico. Solo eso. Pero, ¿por qué se agrega un objeto del tipo OBJ_CHAT al gráfico? La razón es que, al hacerlo, no será necesario crear todos los objetos. Este enfoque puede parecer confuso para quienes están acostumbrados a dibujar en el gráfico mediante código, pero ya lo he explicado en otros artículos. Al final de este artículo, incluiré algunas de mis publicaciones previas para que puedas estudiarlo con más detenimiento. En ellas se explica cómo y por qué se debe hacer esto. Si no has comprendido del todo el tema en este artículo, puedes apoyarte en esas referencias.

La parte realmente importante para nuestro trabajo se encuentra en la línea 52. En esta línea almacenamos el valor de la ID que MetaTrader 5 nos proporciona cuando se crea el objeto OBJ_CHART. Es esencial guardar esta información, ya que será esencial más adelante. Dicho esto, podemos pasar al siguiente procedimiento de la lista. Este es el AdjustEditabled, que comienza en la línea 55. ¿Qué hace este procedimiento? Por extraño que parezca, este procedimiento se encarga de ajustar y capturar el estado de los objetos que se mostrarán en el OBJ_CHART. Pero, ¿cómo funciona esto? Para entenderlo a fondo, es necesario comprender en detalle cómo funciona el procedimiento AdjustTemplate, ya que es justamente ahí donde se utiliza AdjustEditabled. Veamos qué ocurre entre las líneas 72 y 133, que es uno de los procedimientos más complejos de esta clase, ya que es aquí donde realmente sucede la «magia».

En la línea 74 definimos una macro que solo se utilizará en este procedimiento. Observa que al final del procedimiento, en la línea 132, se elimina esta misma macro para que ya no se pueda usar. Luego, en la línea 78, realizamos una prueba para determinar lo que se hará realmente. Si la idea es crear Chart Trade, esta prueba fallará y tendremos que realizar toda una serie de tareas. Si Chart Trade ya existe, se ejecutará la línea 89. En este punto, es necesario que consultes el artículo anterior para entender qué modificaciones se harán en la plantilla, ya que, de cualquier manera, tanto la línea 80 como la 89 iniciarán la construcción de la plantilla. Sin embargo, lo que se haga en concreto dependerá del resultado de la prueba en la línea 78.

Ahora llegamos a la primera parte complicada. Si estamos creando la plantilla, el bucle de la línea 81 realizará una preparación para identificar dónde se encuentran los objetos. Esta misma técnica se utilizará más adelante. Pero, ¿qué objetos son estos? Son los objetos que se añadirán al Chart Trade. Como vemos, no sabemos exactamente dónde están, pero sí cuántos son gracias a la enumeración definida en la línea 17. En la línea 88, llamamos al procedimiento AdjustEditabled para que la prueba de la línea 58 resulte verdadera. Esto nos permitirá añadir más elementos que obtendremos de la plantilla.

Si la clase C_AdjustTemplate tiene éxito, la línea 90 permitirá que el código continúe. Si no, haremos que todo regrese al código que lo llamó, el cual puede verse en el artículo anterior. Pero no te preocupes, hasta este punto no hemos hecho nada. Hasta la línea 90, solo hemos solicitado a la clase C_AdjustTemplate que abra el archivo de plantilla para nosotros. Nada más.

Sin embargo, entre las líneas 96 y 98 ajustamos algunos datos que se presentarán. Justo después, entre las líneas 99 y 104, añadimos ciertos elementos que se ajustarán en la llamada de la línea 105. Es en este preciso momento cuando ocurre la magia. Para comprenderlo, vuelve al artículo anterior y revisa la función Execute de la clase C_AdjustTemplate. En este punto, lo que realmente estamos haciendo es modificar los valores que originalmente se encontraban en el archivo de plantilla. Pero no cambiaremos los valores directamente, sino que estamos accediendo a una copia de este archivo. Por eso realizamos la prueba en la línea 78. De no ser por esa prueba, no sabríamos qué archivo modificar en la línea 105.

No sé si tú, aspirante a programador, lograste realmente entender lo que está sucediendo. Intentaré explicarlo: todo lo realizado hasta aquí, entre las líneas 78 y 104, genera datos para que la clase C_AdjustTemplate modifique el archivo que contiene Chart Trade. Cualquier información que observes en Chart Trade se coloca allí gracias a las líneas 99 a 104. Parece extraño, pero funciona.

Mi consejo es que estudies esta sección del código para entender cómo funciona realmente, ya que todos los objetos se crean y manipulan de la misma manera.

Muy bien, voy a asumir que has comprendido lo que ocurre hasta la línea 105. Si tenemos éxito, el código continuará su ejecución. Si falla, se producirá algo poco común. Por eso, presta atención a lo que voy a explicar, porque es importante.

Si la llamada en la línea 105 falla, debemos destruir la plantilla y finalizar Chart Trade. Destruir la plantilla es sencillo; para eso utilizamos la línea 107. No hay misterios en este punto. Sin embargo, esto no eliminará el indicador del gráfico. Para lograrlo, debemos llamar al procedimiento OnDeInit. Ahora viene la parte complicada, en la que debes prestar atención. Veamos el fragmento de código de OnDeInit, justo a continuación:

41. void OnDeinit(const int reason)
42. {
43.    switch (reason)
44.    {
45.       case REASON_INITFAILED:
46.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
47.          break;
48.       case REASON_CHARTCHANGE:
49.          (*chart).SaveState();
50.          break;
51.    }
52. 
53.    delete chart;
54. }

Fragmento del código del indicador Chart Trade

Este fragmento está aquí solo para facilitar la explicación. Entonces, veamos: cuando se ejecute la línea 109, se entrará en el código de la macro, que puede observarse al inicio del código en la línea ocho. Dentro de esta macro, al ejecutarse la línea nueve, se ejecutará el contenido de la línea 46 del fragmento.

Al mismo tiempo, se ejecutará la línea 53 del fragmento, lo que garantizará la eliminación del indicador Chart Trade. Sin embargo, el código volverá y se ejecutará la línea 10 de la macro. Esto provocará un error y evitará que el código continúe. Finalmente, en la línea 11, se vuelve a llamar al procedimiento que llamó a AdjustTemplate. Da igual quién haya llamado al procedimiento; si no se puede modificar la plantilla, el indicador se eliminará sin generar errores adicionales. Esta es una de las partes más complicadas del sistema. Pero aún no hemos terminado, hay más aspectos que analizar.

Supongamos ahora que la modificación de la plantilla se ha realizado con éxito. En este caso, realizaremos una nueva prueba en la línea 111 para capturar la posición de los objetos clicables. Esta acción solo se lleva a cabo durante la creación de la plantilla. Así, el bucle de la línea 113 se encarga precisamente de esto: captura dónde están los objetos clicables, y las líneas 120 y 121 complementan esta captura.

A continuación, posicionamos el objeto OBJ_CHAT en el gráfico. Esto se lleva a cabo entre las líneas 123 y 125. La línea 127 finaliza la manipulación de la plantilla, lo que nos permite usar la línea 129 para indicar al OBJ_CHAT qué plantilla utilizar. Luego, en la línea 130, forzamos la actualización del contenido del objeto OBJ_CHAT. De este modo, terminamos de construir todo dentro del objeto OBJ_CHAT y se muestran todos los elementos, creando así Chart Trade.

Si no has logrado entender cómo se puede actualizar la plantilla de tal manera que no sea necesario programar Chart Trade mediante código, revisa todo el procedimiento AdjustTemplate. Es aquí donde se controla y muestra Chart Trade. Todos los objetos que aparecen en Chart Trade se crean, posicionan y ajustan dentro de este procedimiento.

No tiene sentido buscar en el código objetos como botones, iconos, imágenes u otros elementos similares, ya que no existen en forma de código. Lo que hace el código es localizar dónde se encuentran dichos elementos y convertirlos en recursos accesibles para el usuario. No intentes entenderlo desde la perspectiva de la creación convencional de objetos mediante código. Aquí estoy haciendo exactamente lo opuesto: primero creo la apariencia deseada y luego solicito al código que haga funcional ese concepto. Si no comprendes esta idea, te sentirás completamente perdido.

El nivel de sofisticación es tal que puedes cambiar la forma, la disposición e incluso las fuentes de los objetos sin tener que escribir ni una sola línea de código. Todo lo que necesitas hacer es abrir MetaTrader 5, cargar la plantilla de Chart Trade y modificar su presentación en el gráfico. Todo lo demás se realiza precisamente gracias al procedimiento AdjustTemplate.

Sin embargo, como siempre, hay algo que complica las cosas. Por este motivo, necesitamos más procedimientos para apoyar este concepto de aplicación RAD (Desarrollo Rápido de Aplicaciones). En este contexto, lo siguiente que vamos a analizar es la función CheckMousePosition, ubicada en la línea 135. Se trata de una función muy sencilla y directa. Su única tarea es comprobar si el mouse está dentro de alguno de los objetos que se pueden cliquear o seleccionar. Si es así, la línea 145 devolverá el objeto en cuestión. Si no es así, la línea 147 informará de que el mouse no está sobre un objeto que pueda recibir un clic o ser seleccionado.

Ahora tenemos dos procedimientos en secuencia que sirven para dar soporte en casos en los que no contamos con una solución óptima al trabajar de otra manera. De hecho, hay un caso específico en el que el procedimiento AdjustTemplate no logra resolver el problema con la calidad suficiente. Este caso se refiere al objeto OBJ_EDIT, que permite al usuario introducir un texto.

El procedimiento de la línea 150 sirve únicamente para eliminar los objetos OBJ_EDIT que se hayan creado. El procedimiento definido en la línea 156 se encargará de crear uno, y solo uno, de estos objetos para que el usuario pueda interactuar e introducir un valor en Chart Trade.

Es posible que la declaración del procedimiento CreateObjectEditable te parezca extraña, pero no es nada sorprendente ni fuera de lo común. La declaración crea una sobrecarga en la llamada y permite que la línea 171 adapte el OBJ_EDIT al tipo de variable que se va a editar. Es decir, si trabajamos con un valor del tipo double, este tendrá dos decimales. De lo contrario, el valor se tratará como una cadena (string). En este caso, el texto mostrado será el mismo que el proporcionado por el autor de la llamada.

Básicamente, no hay complicaciones ni dificultades, al menos para quienes están acostumbrados a programar en MQL5. Todo lo que se realiza es ampliamente conocido incluso por quienes tienen poa experiencia en MQL5 y en la manipulación de objetos.

Con esto, podemos avanzar a la siguiente función: RestoreState, declarada en la línea 175 e implementada hasta la línea 210. Este procedimiento no funciona por sí solo, sino que necesita otro procedimiento, SaveState, ubicado en la línea 244. Sin embargo, ambos procedimientos rompen un poco con las reglas generales, ya que trabajan en conjunto y tienen como objetivo almacenar temporalmente información básica. Estos datos se refieren a la última posición conocida del Chart Trade. Más específicamente, a la ubicación en la que el objeto OBJ_CHAT se encontraba antes de actualizar el gráfico.

Para almacenar esta información, utilizamos las variables globales del terminal. Es importante señalar que solo utilizamos una única variable global para almacenar estos valores. Sin embargo, quiero que prestes atención a otro detalle relevante. Observa que en la línea 187 volvemos a utilizar la clase C_AdjustTemplate. Pero ¿por qué? ¿Acaso vamos a modificar algo en la plantilla en este momento? La respuesta es que no. Ahora utilizaremos la plantilla de otra forma.

Durante su creación, en el procedimiento AdjustTemplate, en la línea 72, mencioné que podemos añadir y quitar elementos de la plantilla. Eso es precisamente lo que haremos aquí. Usaremos los valores de la plantilla, ya modificado, para restaurar los datos en Chart Trade. Pero ¿por qué necesitamos restaurar estos datos si el objeto OBJ_CHAT recibe la plantilla para crear Chart Trade? Porque puede haber interacciones del usuario con los objetos clicables, especialmente con los botones de compra y venta a mercado. Estos botones generan eventos que explicaremos más adelante. Para evitar un retraso excesivo del sistema al buscar la información o que no dispongamos de esos datos cuando sean necesarios, restauramos todo en cuanto se solicita el reinicio del Chart Trade.

Para restaurar correctamente la información contenida en la plantilla, primero debemos abrirlo, lo que se realiza en la línea 187. Si esto tiene éxito, la prueba en la línea 188 permitirá que se ejecuten las líneas 190 a 194. Esto añadirá los nombres y las ubicaciones de los valores que se deben recuperar. A continuación, en la línea 195, se intenta ejecutar la conversión. Como no estamos añadiendo ni modificando nada en la plantilla, la ejecución de la línea 195 capturará los datos solicitados entre las líneas 190 y 194. Si la ejecución tiene éxito, entre las líneas 197 y 201, podremos restaurar las variables privadas de la clase C_ChartFloatingRAD. No es necesario restaurarlas todas, solo las principales, que serán esenciales cuando interactuemos con los eventos, ya que nos aseguraremos de que esos valores estén disponibles. Finalmente, cerramos la plantilla en la línea 204 y, en la línea 207, eliminamos las variables globales creadas temporalmente en el terminal.

Debes entender que estas variables tienen una vida extremadamente corta de menos de un segundo. Su propósito es simplemente almacenar las coordenadas gráficas de la posición del objeto OBJ_CHAT antes de un cambio en el marco temporal del gráfico.

Muy bien, este artículo ya contiene mucha información para que puedas analizarla y comprenderla. Sin embargo, antes de finalizar, podemos hablar brevemente de tres procedimientos adicionales, ya que son sencillos y rápidos de entender.

El procedimiento de la línea 214 es el constructor de esta clase. Su funcionalidad es muy sencilla, ya que utiliza conceptos explicados previamente en este artículo. En la línea 218, se establece una marca que indica si se guardará o no el estado actual. Por defecto, no se guarda nada. En la línea 219, intentamos crear el indicador. Si esto no es posible, retornamos al código que llamó al constructor, lo que provocará que el indicador se cierre. En la línea 220, intentamos restaurar el estado anterior. Si esto falla, utilizamos los datos proporcionados por el usuario. En la línea 230, creamos el objeto OBJ_CHAT y, en la línea 231, ajustamos los parámetros necesarios.

El procedimiento de la línea 234 es el destructor de la clase. Este es aún más sencillo. En la línea 237, eliminamos todos los objetos relacionados con OBJ_CHAT. Es importante hacerlo para evitar que queden elementos innecesarios en el gráfico. En la línea 238, evaluamos si debemos guardar o no el estado actual de la plantilla. Si no se decide guardar, la línea 239 eliminará el archivo de la plantilla del disco. Como resultado, no podremos usar la función RestoreState, implementada entre las líneas 175 y 210, ya que la prueba de la línea 188 fallará y los valores serán los proporcionados por el usuario durante la última carga del indicador.

En la línea 244 encontramos la función SaveState. Esta hace uso de una macro interna que se define en la línea 246 y se destruye en la línea 257. Aunque esta macro es prácticamente innecesaria, se utiliza para simplificar el uso del nombre de la variable global del terminal, ya que esta se emplea dos veces: una para crearla de forma temporal y otra para almacenar su valor. Creo que es más sencillo utilizar una macro. Sin embargo, el punto importante aquí es la línea 255, que garantiza que la prueba de la línea 238 falle y mantiene intacto el archivo de la plantilla.


Consideraciones finales

Aunque quedó pendiente explicar el procedimiento DispatchMessage, que está declarado en la línea 260 y se implementa hasta la línea 386, no lo abordaré en este artículo. La razón es que este procedimiento es bastante complejo y no se puede explicar en un espacio breve. Además, se comprende mucho mejor cuando se observa en funcionamiento junto con otro programa. Esto se debe a que en este procedimiento se crean los eventos que permiten al usuario interactuar con Chart Trade para abrir o cerrar posiciones a mercado.

Este tipo de funcionalidad merece una explicación mucho más detallada. Por esta razón, y para mantener el interés del aspirante a programador en leer el próximo artículo, he optado por no mostrar su contenido interno en esta entrega. Dicho contenido se presentará con todo detalle en el siguiente artículo. De momento, guarda el código de la clase C_ChartFloatingRAD en un archivo y estúdialo con atención, ya que es completamente funcional. Prepárate para el próximo artículo, donde analizaré el código comprendido entre las líneas 260 y 386.

Nos vemos en el próximo artículo. A continuación, incluyo las referencias sobre programación RAD en MQL5. Estos son artículos en los que he explicado previamente los conceptos empleados para construir este Chart Trade de la forma en que se ha mostrado en este artículo. Como habrás notado, no tenemos múltiples objetos, pero aún así logramos interactuar y presentar elementos en el gráfico. Sin embargo, si utilizas la ventana de objetos con el atajo Ctrl + B, solo verás un objeto.

En el archivo adjunto encontrarás todo lo necesario para ver el indicador en funcionamiento y experimentar su interacción, tal y como se muestra en el video siguiente:



Referencias de artículos sobre RAD en MQL5

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

Archivos adjuntos |
Anexo.zip (163.48 KB)
Desarrollo de un sistema de repetición (Parte 76): Un nuevo Chart Trade (III) Desarrollo de un sistema de repetición (Parte 76): Un nuevo Chart Trade (III)
En este artículo, veremos cómo funciona el código faltante del artículo anterior, DispatchMessage. Aquí se introducirá el tema del próximo artículo. Por esta razón, es importante entender el funcionamiento de este procedimiento antes de pasar al siguiente tema. El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos presentados.
Ejemplo de toma de beneficios optimizada automáticamente y parámetros de indicadores con SMA y EMA Ejemplo de toma de beneficios optimizada automáticamente y parámetros de indicadores con SMA y EMA
Este artículo presenta un asesor experto sofisticado para el trading de divisas, que combina el aprendizaje automático con el análisis técnico. Se centra en la negociación de acciones de Apple, presentando optimización adaptativa, gestión de riesgos y múltiples estrategias. Las pruebas retrospectivas muestran resultados prometedores con una alta rentabilidad, pero también caídas significativas, lo que indica potencial para un mayor refinamiento.
Operar con noticias de manera sencilla (Parte 3): Realizando operaciones Operar con noticias de manera sencilla (Parte 3): Realizando operaciones
En este artículo, nuestro experto en negociación de noticias comenzará a abrir operaciones basándose en el calendario económico almacenado en nuestra base de datos. Además, mejoraremos los gráficos del experto para mostrar información más relevante sobre los próximos acontecimientos del calendario económico.
Desarrollo de un sistema de repetición (Parte 74): Un nuevo Chart Trade (I) Desarrollo de un sistema de repetición (Parte 74): Un nuevo Chart Trade (I)
En este artículo, modificaremos el último código visto en esta secuencia sobre Chart Trade. Estos cambios son necesarios para adaptar el código al modelo actual del sistema de repetición/simulador. El contenido expuesto aquí tiene como único propósito ser didáctico. En ningún caso debe considerarse una aplicación destinada a otros fines que no sean el aprendizaje y el estudio de los conceptos mostrados.