Português
preview
Desarrollo de un sistema de repetición (Parte 54): El nacimiento del primer módulo

Desarrollo de un sistema de repetición (Parte 54): El nacimiento del primer módulo

MetaTrader 5Ejemplos | 29 julio 2024, 09:59
14 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, "Desarrollo de un sistema de repetición (Parte 53): Esto complica las cosas (V)", expliqué algunos de los conceptos que a partir de ahora formarán parte de lo que programaremos y usaremos en términos de MQL5 y de la plataforma MetaTrader 5. Sé que muchos de esos conceptos son nuevos para la mayoría de quienes puedan leer estos artículos. También sé que todos conocen estos conceptos si tienen experiencia en programación de sistemas, como, por ejemplo, aquellos que programan sistemas Windows.

Así que, para aquellos que realmente deseen profundizar y entender por qué y cómo funciona lo que se mostrará, les aconsejo estudiar un poco sobre programación de Windows para comprender cómo se produce el intercambio de mensajes entre programas. Aquí, en los artículos, esto me haría desviarme mucho del enfoque de lo que realmente quiero mostrar: cómo diseñar y trabajar en MQL5 y MetaTrader 5 de una manera más avanzada.

No tendrás dificultades para encontrar programas que usan estos intercambios de mensajes para comunicarse en el entorno Windows, pero para comprenderlos adecuadamente, debes tener conocimientos previos y una base sólida y consolidada en términos de programación C. Si no tienes esos conocimientos, mi consejo es que empieces aprendiendo a programar en C y que luego estudies cómo se produce el intercambio de mensajes entre programas en Windows. Así, podrás crear una base amplia y sólida para comprender lo que mostraré a partir de ahora.


Hacer las cosas posibles

Si prestaste atención a lo que se mostró en el artículo anterior, debes haberte dado cuenta de que durante un largo período de tiempo estuve intentando hacer algo. Sin embargo, aunque las cosas funcionaban en parte, no podían coexistir en un sistema más amplio. No de la forma en que se estaba desarrollando.

Quizás el mayor error que cometí fue precisamente ignorar durante varias semanas el hecho de que nuestras aplicaciones responden a eventos de la plataforma MetaTrader 5, pero el error no fue este, sino el hecho de que no estaba pensando en MetaTrader 5 como una plataforma, sino como un simple programa en el que otros procesos se ejecutarían.

Tal error en la forma de ver MetaTrader 5 se debe a que otras plataformas no nos dan la misma flexibilidad que tenemos en MetaTrader 5. Esto me hizo perder algo de tiempo en términos de velocidad para desarrollar las aplicaciones de forma más avanzada. Sin embargo, una vez que este sistema de repetición/simulador se mostró más adecuado para desarrollarse de una manera más modular, diferente a lo que normalmente hacen muchos o a lo que necesita la gran parte de las aplicaciones que se ejecutan en MetaTrader 5, acabé encontrando algunos problemas. Pero no se trataba de problemas, sino de algo que estaba ignorando.

Como puedes ver en los videos del artículo anterior, MetaTrader 5 nos ofrece mucho más de lo que muchos han explorado realmente. Pero ahora vamos a convertir lo que antes era solo un deseo en algo realizable. Vamos a programar menos y construir más. A partir de este artículo, comenzaremos a explorar el intercambio de mensajes con el fin de optimizar el funcionamiento de MetaTrader 5, mientras nos centramos solo en mantener la aplicación funcionando de manera armoniosa con otras aplicaciones del gráfico.

Y lo primero que haremos será adaptar el indicador de mouse para poder comenzar con él esta nueva etapa en el desarrollo de aplicaciones para MetaTrader 5.

Ya que el código sufrirá algunos cambios bastante radicales, será necesario modificar muchas partes del mismo. Sin embargo, si has estado siguiendo la secuencia, no tendrás grandes dificultades en hacer estos cambios.

Así que, de inmediato, crearemos un archivo común a todas las aplicaciones que vayan surgiendo a partir de ahora. Esto nos permitirá dirigir los mensajes que se tratarán mediante cualquier código que crearemos a partir de este momento. Esto nos dará cierto control sobre lo que ocurrirá cuando dos aplicaciones que tienen un manejador de mensajes estén en el gráfico. De este modo, cada aplicación podrá gestionar el mensaje de manera adecuada y correcta.

A continuación, se muestra el contenido inicial de este archivo. Deberá guardarse con el nombre Defines.mqh. El lugar donde deberá estar se verá en breve. Entonces, si no sabes absolutamente nada de programación, lo siento, pero a partir de este momento no podrás seguir lo que realmente haré. A partir de ahora, habrá una barrera que te impedirá continuar. Si realmente quieres usar lo que voy a mostrar, será necesario que aprendas lo mínimo sobre programación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                             Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay            "RePlay"
14. #define def_MaxPosSlider            400
15. //+------------------------------------------------------------------+
16. union uCast_Double
17. {
18.     double   dValue;
19.     long     _long;                                  // 1 Information
20.     datetime _datetime;                              // 1 Information
21.     int      _int[sizeof(double) / sizeof(int)];     // 2 Informations
22.     char     _char[sizeof(double) / sizeof(char)];   // 8 Informations
23. };
24. //+------------------------------------------------------------------+
25. enum EnumEvents    {
26.                     evHideMouse,        //Hide mouse price line
27.                     evShowMouse,        //Show mouse price line
28.                     evHideBarTime,      //Hide bar time
29.                     evShowBarTime,      //Show bar time
30.                     evHideDailyVar,     //Hide daily variation
31.                     evShowDailyVar,     //Show daily variation
32.                     evHidePriceVar,     //Hide instantaneous variation
33.                     evShowPriceVar,     //Show instantaneous variation
34.                     evSetServerTime,    //Replay/simulation system timer
35.                     evCtrlReplayInit    //Initialize replay control
36.                    };
37. //+------------------------------------------------------------------+

Código fuente del archivo Defines.mqh

En la línea 4 de este archivo, puedes ver que hemos definido algo. De esta forma, si deseas depurar el código en tiempo de ejecución, agrega la macro _DEBUG_MODE a tu código pasando la variable a depurar como parámetro. Así, será posible añadir y eliminar rápidamente un sistema para analizar en tiempo de ejecución lo que está ocurriendo. Basta con verificar si la línea 4 está siendo utilizada por el compilador.

En la línea 13, tenemos la definición del nombre que se usará en nuestro símbolo personalizado y que será utilizado por el servicio de repetición/simulador, como se ha hecho desde el inicio de esta secuencia de artículos. En la línea 14, declaramos algo que solo será usado por el indicador de control.

Estas dos últimas líneas, junto con el contenido presente entre las líneas 16 y 23, ya formaban parte del código que se estaba escribiendo. Sin embargo, dado que el archivo de encabezado InterProcess.mqh dejará de existir, fue necesario transferir esta información al archivo de definición.

Lo que realmente nos interesa en este archivo comienza en la línea 25. Aquí declaramos una enumeración que irá creciendo a medida que se necesiten nuevos eventos y se vayan añadiendo. Existe un pequeño peligro, pero no tan grave si se toman las precauciones adecuadas: siempre hay que añadir una enumeración al final de la lista. Si lo haces, no será necesario recompilar los códigos antiguos, pero si añades algo en medio de la lista, tendrás que recompilar todos los códigos antiguos. Así evitarás problemas.

Nota que en cada línea estamos definiendo un valor y haciendo un breve comentario sobre el evento correspondiente.

Verás que, conforme se vayan mostrando los códigos, se indicará dónde y cómo se tratará cada uno de estos eventos. Lo principal será visible solo en el código. Si planeas usar parte de lo que estoy mostrando, presta atención a los manejadores de mensajes.

Así empezamos a hacer todo el sistema completamente modular. A partir de ahora, presta mucha atención al código y a lo que estará presente en el gráfico. Ha llegado el momento de usar MetaTrader 5 como un verdadero profesional.

Como ya debes imaginar, fue necesario cambiar nuevamente el código de las clases del indicador de mouse, pero estos cambios son necesarios precisamente para promover el correcto uso e implementación de lo que realmente haremos. Antes de modificar el código del indicador de mouse, es necesario hacer un pequeño cambio en el código de la clase C_Terminal. Por tanto, en el archivo de encabezado C_Terminal.mqh, haz lo que se muestra en el fragmento siguiente.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Macros.mqh"
05. #include "..\Defines.mqh"
06. #include "Interprocess.mqh"
07. //+------------------------------------------------------------------+
08. class C_Terminal
09. {
10. //+------------------------------------------------------------------+
11.     protected:
12.             enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
13. //+------------------------------------------------------------------+
14.             struct st_Terminal

Fragmento de código del archivo C_Terminal.mqh

Observa con atención la línea 5, que indica la ubicación del archivo de encabezado Defines.mqh, mencionado anteriormente, en relación con el archivo de encabezado C_Terminal.mqh. Pero eso no es todo lo que quiero resaltar. Fíjate en que la línea 6 se ha eliminado del código, es decir, ya puedes borrar el archivo de encabezado InterProcess.mqh del proyecto, pues ya no se utilizará.

Dado que este cambio es bastante simple y no se han introducido más cambios en el código del archivo C_Terminal.mqh, no veo la necesidad de replicar todo el archivo. Aunque no ha habido grandes cambios, ha habido uno que es necesario mencionar; de lo contrario, tendrás problemas al intentar compilar los códigos que se mostrarán.

Observa que en la línea 12 hay una enumeración que ha recibido un nuevo valor. Este valor se utilizará durante la prueba para verificar si ya existe la ocurrencia de un mismo indicador en el gráfico. Por lo tanto, es necesario realizar otro pequeño cambio en el mismo archivo de encabezado C_Terminal.mqh. Este cambio se muestra en el siguiente fragmento.

157. //+------------------------------------------------------------------+
158.            bool IndicatorCheckPass(const string szShortName)
159.                    {
160.                            string szTmp = szShortName + "_TMP";
161.                            
162.                            if (_LastError != ERR_SUCCESS) return false;
163.                            IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
164.                            if (ChartWindowFind(m_Infos.ID, szShortName) != -1)
165.                            {
166.                                    ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
167.                                    Print("Only one instance is allowed...");
168.                                    SetUserError(C_Terminal::ERR_NoMoreInstance);
169.                                    
170.                                    return false;
171.                            }
172.                            IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
173.                            ResetLastError();
174.    
175.                            return true;
176.                    }
177. //+------------------------------------------------------------------+

Fragmento a modificar en el archivo C_Terminal.mqh

Deberás modificar la función original presente en el archivo C_Terminal.mqh por el código que se muestra en el fragmento 2. De esta forma, durante la prueba, el sistema ajustará correctamente la constante _LastError para indicar que ha ocurrido un error y la razón ha sido la existencia de otra instancia del mismo indicador en el gráfico. Aparte de estas dos simples modificaciones, no se han realizado más cambios, por lo que podemos continuar con nuestro trabajo y empezar a desarrollar las clases del indicador de mouse.

A continuación, puedes ver íntegramente todo el código del archivo de encabezado C_Mouse.mqh. Como algunos pueden tener dificultades para comprender adecuadamente el código, daré una breve explicación de lo que está ocurriendo. Así podrás ver mejor cómo empiezan a volverse modulares las cosas.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase_"
007. #define def_NameObjectLineH def_MousePrefixName + "H"
008. #define def_NameObjectLineV def_MousePrefixName + "TV"
009. #define def_NameObjectLineT def_MousePrefixName + "TT"
010. #define def_NameObjectStudy def_MousePrefixName + "TB"
011. //+------------------------------------------------------------------+
012. class C_Mouse : public C_Terminal
013. {
014.    public  :
015.            enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
016.            enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
017.            struct st_Mouse
018.            {
019.                    struct st00
020.                    {
021.                            int      X_Adjusted,
022.                                     Y_Adjusted,
023.                                     X_Graphics,
024.                                     Y_Graphics;
025.                            double   Price;
026.                            datetime dt;
027.                    }Position;
028.                    uint     ButtonStatus;
029.                    bool     ExecStudy;
030.                    datetime TimeDevice;
031.            };
032. //+------------------------------------------------------------------+
033.    protected:
034. //+------------------------------------------------------------------+
035.            void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
036.                    {
037.                            if (m_Mem.szShortName != NULL) return;
038.                            CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
039.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true);
040.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
041.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack);
042.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor);
043.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console");
044.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10);
045.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
046.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x);
047.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
048.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 
049.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18);
050.                    }
051. //+------------------------------------------------------------------+
052.    private :
053.            enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
054.            struct st01
055.            {
056.                    st_Mouse Data;
057.                    color    corLineH,
058.                             corTrendP,
059.                             corTrendN;
060.                    eStudy   Study;
061.            }m_Info;
062.            struct st_Mem
063.            {
064.                    bool     CrossHair,
065.                             IsFull;
066.                    datetime dt;
067.                    string   szShortName;
068.            }m_Mem;
069.            bool m_OK;
070. //+------------------------------------------------------------------+
071.            void GetDimensionText(const string szArg, int &w, int &h)
072.                    {
073.                            TextSetFont("Lucida Console", -100, FW_NORMAL);
074.                            TextGetSize(szArg, w, h);
075.                            h += 5;
076.                            w += 5;
077.                    }
078. //+------------------------------------------------------------------+
079.            void CreateStudy(void)
080.                    {
081.                            if (m_Mem.IsFull)
082.                            {
083.                                    CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
084.                                    CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
085.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
086.                                    CreateObjToStudy(0, 0, def_NameObjectStudy);
087.                            }
088.                            m_Info.Study = eStudyCreate;
089.                    }
090. //+------------------------------------------------------------------+
091.            void ExecuteStudy(const double memPrice)
092.                    {
093.                            double v1 = GetInfoMouse().Position.Price - memPrice;
094.                            int w, h;
095.                            
096.                            if (!CheckClick(eClickLeft))
097.                            {
098.                                    m_Info.Study = eStudyNull;
099.                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
100.                                    if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
101.                            }else if (m_Mem.IsFull)
102.                            {
103.                                    string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
104.                                            MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0)));
105.                                    GetDimensionText(sz1, w, h);
106.                                    ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1);                                                                                                                          
107.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
108.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w);
109.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h);
110.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w);
111.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));                          
112.                                    ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
113.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
114.                            }
115.                            m_Info.Data.ButtonStatus = eKeyNull;
116.                    }
117. //+------------------------------------------------------------------+
118.    public  :
119. //+------------------------------------------------------------------+
120.            C_Mouse(const long id, const string szShortName)
121.                    :C_Terminal(id),
122.                    m_OK(false)
123.                    {
124.                            m_Mem.szShortName = szShortName;
125.                    }
126. //+------------------------------------------------------------------+
127.            C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
128.                    :C_Terminal(id)
129.                    {
130.                            if (!(m_OK = IndicatorCheckPass(szShortName))) SetUserError(C_Terminal::ERR_Unknown);
131.                            if (_LastError != ERR_SUCCESS) return;
132.                            m_Mem.szShortName = NULL;
133.                            m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
134.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
135.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
136.                            ZeroMemory(m_Info);
137.                            m_Info.corLineH  = corH;
138.                            m_Info.corTrendP = corP;
139.                            m_Info.corTrendN = corN;
140.                            m_Info.Study = eStudyNull;
141.                            if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
142.                                    CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
143.                    }
144. //+------------------------------------------------------------------+
145.            ~C_Mouse()
146.                    {
147.                            if (!m_OK) return;
148.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
149.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
150.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
151.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
152.                    }
153. //+------------------------------------------------------------------+
154. inline bool CheckClick(const eBtnMouse value) 
155.                    {
156.                            return (GetInfoMouse().ButtonStatus & value) == value;
157.                    }
158. //+------------------------------------------------------------------+
159. inline const st_Mouse GetInfoMouse(void)
160.                    {
161.                            if (m_Mem.szShortName != NULL)
162.                            {
163.                                    double Buff[];
164.                                    uCast_Double loc;
165.                                    int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName);
166.                                    
167.                                    ZeroMemory(m_Info.Data);
168.                                    if (CopyBuffer(handle, 0, 0, 6, Buff) == 6)
169.                                    {
170.                                            m_Info.Data.Position.Price = Buff[0];
171.                                            loc.dValue = Buff[1];
172.                                            m_Info.Data.Position.dt = loc._datetime;
173.                                            loc.dValue = Buff[2];
174.                                            m_Info.Data.Position.X_Adjusted = loc._int[0];
175.                                            m_Info.Data.Position.Y_Adjusted = loc._int[1];
176.                                            loc.dValue = Buff[3];
177.                                            m_Info.Data.Position.X_Graphics = loc._int[0];
178.                                            m_Info.Data.Position.Y_Graphics = loc._int[1];
179.                                            loc.dValue = Buff[4];
180.                                            m_Info.Data.ButtonStatus = loc._char[0];
181.                                            m_Info.Data.TimeDevice = (datetime)Buff[5];
182.                                            IndicatorRelease(handle);
183.                                    }
184.                            }
185. 
186.                            return m_Info.Data;
187.                    }
188. //+------------------------------------------------------------------+
189.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
190.                    {
191.                            int w = 0;
192.                            static double memPrice = 0;
193.                            
194.                            if (m_Mem.szShortName == NULL)
195.                            {
196.                                    C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
197.                                    switch (id)
198.                                    {
199.                                            case (CHARTEVENT_CUSTOM + evHideMouse):
200.                                                    if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
201.                                                    break;
202.                                            case (CHARTEVENT_CUSTOM + evShowMouse):
203.                                                    if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
204.                                                    break;
205.                                            case (CHARTEVENT_CUSTOM + evSetServerTime):
206.                                               m_Info.Data.TimeDevice = (datetime)dparam;
207.                                               break;
208.                                            case CHARTEVENT_MOUSE_MOVE:
209.                                                    ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X_Graphics = (int)lparam, m_Info.Data.Position.Y_Graphics = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
210.                                                    if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
211.                                                    m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt);
212.                                                    ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X_Adjusted, m_Info.Data.Position.Y_Adjusted);
213.                                                    if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
214.                                                    m_Info.Data.ButtonStatus = (uint) sparam;
215.                                                    if (CheckClick(eClickMiddle))
216.                                                            if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
217.                                                    if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
218.                                                    {
219.                                                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
220.                                                            if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
221.                                                            m_Info.Study = eStudyExecute;
222.                                                    }
223.                                                    if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
224.                                                    m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
225.                                                    break;
226.                                            case CHARTEVENT_OBJECT_DELETE:
227.                                                    if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
228.                                                    break;
229.                                    }
230.                            }
231.                    }
232. //+------------------------------------------------------------------+
233. };
234. //+------------------------------------------------------------------+
235. #undef def_NameObjectLineV
236. #undef def_NameObjectLineH
237. #undef def_NameObjectLineT
238. #undef def_NameObjectStudy
239. //+------------------------------------------------------------------+

Código fuente del archivo C_Mouse.mqh

A diferencia de antes, ahora solo se incluye como necesario el archivo C_Terminal.mqh. Sin embargo, debes prestar atención a otros detalles que se han modificado en esta clase vista anteriormente.

Fíjate en que ahora, en la línea 30, tenemos una nueva variable. Esta solo tendrá valor para nosotros cuando el servicio de repetición/simulador esté en uso. Esta variable nos da acceso a un valor proporcionado por el servicio de repetición/simulador, que antes se obtenía mediante una variable global del terminal. Aunque esta variable se declara en la línea 30, no se usa aquí, sino en otro lugar. Pero, debido a otra cuestión que explicaré más adelante, fue necesario añadirla aquí, en la clase C_Mouse.

Verás que la mayor parte del código sigue siendo idéntico al anterior, hasta que en la línea 168 encontramos algo diferente. Ahora usaremos seis posiciones del buffer. La sexta posición estará ocupada por la variable declarada en la línea 30. Así, en la línea 181, rellenamos el valor cuando sea necesario leerlo y saber qué valor tiene esta variable. Aquí debo hacer un paréntesis y explicar algo. El único procedimiento que usará el contenido de la variable declarada en la línea 30 será el servicio de repetición/simulador y el indicador de mouse. Este último usará este valor para informar al usuario del tiempo que falta para que se abra la próxima barra. Sin embargo, si no deseas utilizarlo, puedes eliminarlo del indicador de mouse o del servicio de repetición/simulador. No habrá ningún problema si lo haces. Pero si deseas usar esta información, debes saber que, si no usas una variable global del terminal u otro medio para almacenar el valor del tiempo del servicio de repetición/simulación, este servicio no sabrá cuándo es necesario actualizar nuevamente este dato.

Por esta razón, colocamos el valor en el buffer del indicador de mouse. Pero entonces te preguntas: "espera un momento, ¿no se eliminará y se pondrá a cero este valor cuando MetaTrader 5 vuelva a colocar el indicador en el gráfico porque el usuario haya modificado el marco temporal del gráfico?" Entonces, guardarlo en el buffer del indicador no tiene ningún sentido. En realidad, es precisamente esto lo que queremos. Cuando el servicio detecte que el valor presente en el buffer ya no es el que debería ser, se enviará un evento al indicador de mouse para actualizar nuevamente este mismo valor. De esta manera, mantendremos las cosas como se espera.

Es decir, el simple hecho de que MetaTrader 5 fuerce al indicador a reiniciar el buffer hará que el servicio de repetición/simulación se dé cuenta de que el usuario ha hecho algún cambio que necesita ser reevaluado por el servicio de repetición/simulación. Así, nuestra aplicación detectará sin esfuerzo que ha sucedido algo, pues MetaTrader 5 hará todo el trabajo pesado por nosotros.

Pero la cuestión se vuelve aún más interesante cuando miramos el código a partir de la línea 189, que es precisamente donde se encuentra el gestor de mensajes de la clase C_Mouse.

Dentro de este procedimiento de gestión de mensajes, tenemos los tres primeros mensajes que nuestro sistema modular comenzará a tratar. Así que presta atención a cómo se hace esto. En primer lugar, para que el manejo de mensajes lo realice el indicador y no algún otro código que esté usando la clase, hacemos una prueba para verificar si nos referimos al indicador o a un traductor. Esta prueba se realiza en la línea 194. Si la prueba pasa, consideraremos que estamos tratando con el indicador, por lo que necesitamos que haya solo un indicador de mouse en el gráfico, para evitar posibles conflictos de intereses. Ya he explicado esto en otros artículos, así que pasemos a lo que realmente debe hacerse.

En la línea 199, manejamos un evento en el que el indicador de mouse debe ocultar la línea usada como línea de precio.

En la línea 202, gestionamos el evento en el que se le indica al indicador de mouse que la línea de precio debe mostrarse nuevamente en el gráfico. De esta forma, cualquier aplicación podrá indicar al indicador de mouse cuándo mostrar u ocultar la línea de precio que utiliza.

En ambos eventos no es necesario proporcionar ningún tipo de parámetro adicional, por lo que cualquier aplicación que desee ocultar o mostrar la línea del mouse solo necesita generar un evento personalizado con los valores indicados y dirigir este evento al gráfico donde se encuentra el indicador de mouse. Lo explicaré más en detalle más adelante. Por ahora, entiéndelo así: si en algún momento tienes el indicador de mouse en el gráfico y deseas ocultar la línea del mouse, debes generar un evento personalizado con el valor correcto para poder ocultar o mostrar la línea del mouse. Este evento puede ser activado, por ejemplo, por un Expert Advisor.

El tercer evento, que también se implementará aquí, se ve en la línea 205. En este evento indicaremos el valor que se colocará como el tiempo del servicio; es decir, quien realmente hará esto será el servicio de repetición/simulación. Ya que será el único que sacará provecho de verdad generando tal evento. Pero hay un detalle. Cuando se lance este evento, deberá indicar un valor de tiempo. Este valor deberá estar en el parámetro dparam, que es un valor double.

Una vez más, es necesario que veas las cosas de manera más amplia. Un valor double está compuesto por 8 bytes, igual que un valor datetime, que también está compuesto por 8 bytes. De esta forma, hacemos una conversión de tipos para que el compilador pueda comprender lo que estamos haciendo. Pero para el procesador, todo lo que estamos haciendo es colocar 8 bytes en una variable. El contenido de esos bytes no importa.

Entender esto es importante, pues hay situaciones en las que necesitamos pasar cadenas completas de valores, a veces estructuras enteras, y como en MetaTrader 5, o mejor dicho, en MQL5, no podemos usar punteros como en C/C++, necesitaremos algún tipo de artimaña para poder pasar esos datos entre nuestras aplicaciones que están corriendo en MetaTrader 5.

Muy bien, entonces la primera parte del trabajo ya está hecha. Pero aún vamos a mejorar las cosas. Si de verdad prestaste atención, debes haber notado que hay algunos otros eventos relacionados con el indicador de mouse. Solo que estos eventos no están en el archivo de encabezado C_Mouse.mqh, sino en el archivo C_Study.mqh. Entonces, para ver esos eventos y comprender lo que estará ocurriendo, vamos a ver dicho código. Se puede ver completo a continuación:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
010. //+------------------------------------------------------------------+
011. class C_Study : public C_Mouse
012. {
013.    private :
014. //+------------------------------------------------------------------+
015.            struct st00
016.            {
017.                    eStatusMarket  Status;
018.                    MqlRates       Rate;
019.                    string         szInfo;
020.                    color          corP,
021.                                   corN;
022.                    int            HeightText;
023.                    bool           bvT, bvD, bvP;
024.            }m_Info;
025. //+------------------------------------------------------------------+
026.            const datetime GetBarTime(void)
027.                    {
028.                            datetime dt;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if ((dt = GetInfoMouse().TimeDevice) == ULONG_MAX) return ULONG_MAX;
034.                            }else dt = TimeCurrent();
035.                            if (m_Info.Rate.time <= dt)
036.                                    m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
037. 
038.                            return m_Info.Rate.time - dt;
039.                    }
040. //+------------------------------------------------------------------+
041.            void Draw(void)
042.                    {
043.                            double v1;
044.                            
045.                            if (m_Info.bvT)
046.                            {
047.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18);
048.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
049.                            }
050.                            if (m_Info.bvD)
051.                            {
052.                                    v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
053.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
054.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                            }
057.                            if (m_Info.bvP)
058.                            {
059.                                    v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
060.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
061.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
062.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
063.                            }
064.                    }
065. //+------------------------------------------------------------------+
066. inline void CreateObjInfo(EnumEvents arg)
067.                    {
068.                            switch (arg)
069.                            {
070.                                    case evShowBarTime:
071.                                            C_Mouse::CreateObjToStudy(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
072.                                            m_Info.bvT = true;
073.                                            break;
074.                                    case evShowDailyVar:
075.                                            C_Mouse::CreateObjToStudy(2, 53, def_ExpansionBtn2);
076.                                            m_Info.bvD = true;
077.                                            break;
078.                                    case evShowPriceVar:
079.                                            C_Mouse::CreateObjToStudy(58, 53, def_ExpansionBtn3);
080.                                            m_Info.bvP = true;
081.                                            break;
082.                            }
083.                    }
084. //+------------------------------------------------------------------+
085. inline void RemoveObjInfo(EnumEvents arg)
086.                    {
087.                            string sz;
088.                            
089.                            switch (arg)
090.                            {
091.                                    case evHideBarTime:
092.                                            sz = def_ExpansionBtn1;
093.                                            m_Info.bvT = false;
094.                                            break;
095.                                    case evHideDailyVar:
096.                                            sz = def_ExpansionBtn2;
097.                                            m_Info.bvD      = false;
098.                                            break;
099.                                    case evHidePriceVar:
100.                                            sz = def_ExpansionBtn3;
101.                                            m_Info.bvP = false;
102.                                            break;
103.                            }
104.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
105.                            ObjectDelete(GetInfoTerminal().ID, sz);
106.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
107.                    }
108. //+------------------------------------------------------------------+
109.    public  :
110. //+------------------------------------------------------------------+
111.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
112.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
113.                    {
114.                            if (_LastError != ERR_SUCCESS) return;
115.                            ZeroMemory(m_Info);
116.                            m_Info.Status = eCloseMarket;
117.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
118.                            m_Info.corP = corP;
119.                            m_Info.corN = corN;
120.                            CreateObjInfo(evShowBarTime);
121.                            CreateObjInfo(evShowDailyVar);
122.                            CreateObjInfo(evShowPriceVar);
123.                    }
124. //+------------------------------------------------------------------+
125.            void Update(const eStatusMarket arg)
126.                    {
127.                            datetime dt;
128.                            
129.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
130.                            {
131.                                    case eCloseMarket :
132.                                            m_Info.szInfo = "Closed Market";
133.                                            break;
134.                                    case eInReplay    :
135.                                    case eInTrading   :
136.                                            if ((dt = GetBarTime()) < ULONG_MAX)
137.                                            {
138.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
139.                                                    break;
140.                                            }
141.                                    case eAuction     :
142.                                            m_Info.szInfo = "Auction";
143.                                            break;
144.                                    default           :
145.                                            m_Info.szInfo = "ERROR";
146.                            }
147.                            Draw();
148.                    }
149. //+------------------------------------------------------------------+
150. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
151.                    {
152.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
153.                            switch (id)
154.                            {
155.                                    case CHARTEVENT_CUSTOM + evHideBarTime:
156.                                            RemoveObjInfo(evHideBarTime);
157.                                            break;
158.                                    case CHARTEVENT_CUSTOM + evShowBarTime:
159.                                            CreateObjInfo(evShowBarTime);
160.                                            break;
161.                                    case CHARTEVENT_CUSTOM + evHideDailyVar:
162.                                            RemoveObjInfo(evHideDailyVar);
163.                                            break;
164.                                    case CHARTEVENT_CUSTOM + evShowDailyVar:
165.                                            CreateObjInfo(evShowDailyVar);
166.                                            break;
167.                                    case CHARTEVENT_CUSTOM + evHidePriceVar:
168.                                            RemoveObjInfo(evHidePriceVar);
169.                                            break;
170.                                    case CHARTEVENT_CUSTOM + evShowPriceVar:
171.                                            CreateObjInfo(evShowPriceVar);
172.                                            break;
173.                                    case CHARTEVENT_MOUSE_MOVE:
174.                                            Draw();
175.                                            break;
176.                            }
177.                            ChartRedraw(GetInfoTerminal().ID);
178.                    }
179. //+------------------------------------------------------------------+
180. };
181. //+------------------------------------------------------------------+
182. #undef def_ExpansionBtn3
183. #undef def_ExpansionBtn2
184. #undef def_ExpansionBtn1
185. #undef def_ExpansionPrefix
186. #undef def_MousePrefixName
187. //+------------------------------------------------------------------+

Código fuente del archivo C_Study.mqh

A diferencia de lo que fue en C_Mouse.mqh, aquí tenemos muchas diferencias. Para empezar, tenemos algunas variables nuevas, pruebas y cosas que se están haciendo. Pero, como gran parte de estas cosas son sencillas, podemos centrarnos en solo algunas más exóticas. Entre estas, está la línea 33, donde ahora podemos ver que ya no utilizamos una función para acceder a una variable global del terminal. Lo que estamos haciendo ahora es preguntar al indicador de mouse algo que antes se buscaba en una variable global del terminal. Este tipo de cosas es lo que queremos implementar; es decir, queremos hacer las cosas sin necesitar usar algo que el usuario pueda manipular, pero sin tener que recurrir a programación externa. La idea es hacer todo en MQL5, puro.

Tal vez no quede muy claro solo con observar el código que estamos implementando algo mucho más personalizable que lo que había antes. Esto tiene un propósito: hacer de nuestro indicador de mouse un tipo de estándar que se use en otras aplicaciones dentro de la plataforma. Pero hay un detalle. Tú, como programador, podrás enviar mensajes al indicador de mouse para apagar o encender algo que ya esté definido en él.

Para hacer esto, es necesario aislar algunas partes del código, de modo que, cuando un evento pida encender o apagar algo, el objeto al que nos estemos refiriendo sufra los cambios que queremos hacer. En este caso, hablamos de algo sencillo, como hacer que algo aparezca o no en el gráfico. Pero podría ser algo aún más complicado. Eso no importa. El grado de libertad que estamos comenzando a crear aquí hace posible innumerables cosas, y todas ellas serán siempre pequeñas modificaciones de lo que ya estará listo. Es decir, que el nivel de seguridad y fiabilidad de la aplicación se mantendrá siempre lo más alto posible.

Gran parte del código que ves en el archivo C_Study.mqh sirve precisamente para permitir esto. Pero lo que realmente nos interesa en este momento es lo que sucede a partir de la línea 150, donde implementaremos lo que se tratará en este archivo de encabezado. Observa que las partes pendientes que no nos interesa tratar aquí se transfieren a la clase C_Mouse, para que los eventos puedan gestionarse allí. Esto se puede observar en la línea 152.

Ahora, presta atención a una cosa. Cada uno de los eventos personalizados que pueden verse en este manejador encenderá o apagará algo en el indicador de mouse, haciendo que se vea ligeramente diferente, pero esto se hará en tiempo de ejecución, sin necesidad de recompilar el código ni forzar al usuario a configurar una lista muy grande de opciones. Presta atención a este hecho. Entonces, puedes simplemente crear pequeños archivos de script para enviar un evento personalizado al indicador de mouse, para que su apariencia sea modificada durante su uso.

Si tienes la debida creatividad, ya estarás pensando y planificando diversas cosas solo con observar este código y verlo funcionar. Pero quiero que tengas un poco de calma, debido al hecho de que en este momento aún no está totalmente claro cuál será la dirección que tomará el sistema de repetición/simulador. Esto se debe a que al convertir las cosas en módulos, se abre un gran abanico de posibilidades. Y la forma en que el sistema puede llegar a crecer hace que tomemos algunas precauciones al comenzar a pensar en nuevas posibilidades. Pero, por el momento, estoy centrado en hacer que el sistema comience a funcionar sin necesidad de usar las variables globales del terminal, aunque manteniendo las mismas cualidades que tenía cuando se empezaron a crear los módulos.

Muy bien. Tras haber mostrado todo esto, llegamos al código que realmente crea el indicador de mouse. Puede verse en su totalidad a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property description "This is an indicator for graphical studies using the mouse."
004. #property description "This is an integral part of the Replay / Simulator system."
005. #property description "However it can be used in the real market."
006. #property version "1.54"
007. #property icon "/Images/Market Replay/Icons/Indicators.ico"
008. #property link "https://www.mql5.com/pt/articles/11971"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. #property indicator_buffers 1
012. //+------------------------------------------------------------------+
013. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
014. //+------------------------------------------------------------------+
015. C_Study *Study     = NULL;
016. //+------------------------------------------------------------------+
017. input long user00  = 0;                                    //ID
018. input C_Study::eStatusMarket user01 = C_Study::eAuction;   //Market Status
019. input color user02 = clrBlack;                             //Price Line
020. input color user03 = clrPaleGreen;                         //Positive Study
021. input color user04 = clrLightCoral;                        //Negative Study
022. //+------------------------------------------------------------------+
023. C_Study::eStatusMarket m_Status;
024. int m_posBuff = 0;
025. double m_Buff[];
026. //+------------------------------------------------------------------+
027. int OnInit()
028. {
029.    ResetLastError();
030.    Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
031.    if (_LastError != ERR_SUCCESS) return INIT_FAILED;
032.    if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
033.    {
034.            MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
035.            OnBookEvent((*Study).GetInfoTerminal().szSymbol);
036.            m_Status = C_Study::eCloseMarket;
037.    }else
038.            m_Status = user01;
039.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
040.    ArrayInitialize(m_Buff, EMPTY_VALUE);
041.    
042.    return INIT_SUCCEEDED;
043. }
044. //+------------------------------------------------------------------+
045. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
046. {
047.    m_posBuff = rates_total - 6;
048.    (*Study).Update(m_Status);      
049.    
050.    return rates_total;
051. }
052. //+------------------------------------------------------------------+
053. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
054. {
055.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
056.    SetBuffer();
057.    
058.    ChartRedraw((*Study).GetInfoTerminal().ID);
059. }
060. //+------------------------------------------------------------------+
061. void OnBookEvent(const string &symbol)
062. {
063.    MqlBookInfo book[];
064.    C_Study::eStatusMarket loc = m_Status;
065.    
066.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
067.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
068.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
069.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
070.            if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
071.    if (loc != m_Status) (*Study).Update(m_Status);
072. }
073. //+------------------------------------------------------------------+
074. void OnDeinit(const int reason)
075. {
076.    if (reason != REASON_INITFAILED)
077.    {
078.            if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
079.                    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
080.    }
081.    delete Study;
082. }
083. //+------------------------------------------------------------------+
084. inline void SetBuffer(void)
085. {
086.    uCast_Double Info;
087.    
088.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
089.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
090.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
091.    m_Buff[m_posBuff + 1] = Info.dValue;
092.    Info._int[0] = (*Study).GetInfoMouse().Position.X_Adjusted;
093.    Info._int[1] = (*Study).GetInfoMouse().Position.Y_Adjusted;
094.    m_Buff[m_posBuff + 2] = Info.dValue;
095.    Info._int[0] = (*Study).GetInfoMouse().Position.X_Graphics;
096.    Info._int[1] = (*Study).GetInfoMouse().Position.Y_Graphics;
097.    m_Buff[m_posBuff + 3] = Info.dValue;
098.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
099.    m_Buff[m_posBuff + 4] = Info.dValue;
100.    m_Buff[m_posBuff + 5] = (double)(*Study).GetInfoMouse().TimeDevice;
101. }
102. //+------------------------------------------------------------------+

Código fuente del indicador de Mouse

Cabe destacar que prácticamente no hay ningún cambio significativo en el código. Solo hay una nueva adición que se puede ver en la línea 100, donde se coloca un dato en el búfer del indicador. Una vez más, este dato solo tiene utilidad en este primer momento para el servicio de repetición/simulador. No tiene ninguna otra utilidad. Bueno, al menos no una que pueda notar.


Conclusión

Para que puedas entender realmente lo que está pasando aquí, dejaré en el anexo el ejecutable del indicador, junto con algunos scripts para ser usados para modificar el indicador cuando esté en el gráfico. Pero hay quienes no quieren o no pretenden ejecutar un programa ya compilado en su plataforma. De hecho, entiendo su desconfianza. En el vídeo de la parte inferior de este artículo se puede ver lo que ocurre cuando se ejecutan los scripts.

Observa que es solo una pequeña muestra de lo que somos capaces de hacer. Muchos, con una visión más corta, dirán que es una tontería y que se podría hacer de otra manera. Pero a estos los ignoraré. Prefiero ver el brillo en los ojos de quienes piensan en las posibilidades de lo que estoy mostrando, en lo que este sistema modular puede llegar a ser y en cómo se desarrollarán las cosas de ahora en adelante. Es precisamente para ellos para quienes escribo estos artículos. Para demostrar que muchos solo están rascando la superficie de lo que realmente podemos hacer en MQL5 y que MetaTrader 5 es, de hecho, una gran plataforma. Y en las manos correctas, tiene la posibilidad de hacer cosas increíbles.

No obstante, pido disculpas una vez más a quienes de hecho no tienen conocimientos de programación. Lamento que, sin los conocimientos adecuados sobre lo que se muestra aquí, acceder al código fuente de este servicio de repetición/simulación resulte peligroso. Pero prometo no dejarte completamente solo. Prometo poner a vuestra disposición el ejecutable siempre que la versión de algún módulo esté concluida y sea estable. Dispondré el ejecutable aquí en los artículos. Sin embargo, si deseas compilar realmente el código fuente, siempre estará disponible en el artículo. Sin embargo, sé que sin los conocimientos adecuados no podrás crear la estructura de directorios necesaria para compilar el sistema.

Esta es precisamente mi intención, ya que por experiencia he visto que, en artículos anteriores, las personas intentaban compilar algo sin realmente saber de qué se trataba. Este tipo de cosas, además de ser peligrosas, son arriesgadas. No debes utilizar algo sin entender para qué sirve.


Video 01 - Demostración del funcionamiento del módulo

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

Archivos adjuntos |
Anexo.zip (420.65 KB)
Desarrollo de un sistema de repetición (Parte 55): Módulo de control Desarrollo de un sistema de repetición (Parte 55): Módulo de control
En este artículo, implementaremos el indicador de control de manera que pueda integrarse en el sistema de mensajes que está en desarrollo. Aunque no es algo muy complejo de hacer, es necesario entender algunos detalles sobre cómo inicializar este módulo. El contenido expuesto aquí tiene como objetivo, pura y simplemente, la didáctica. En ningún caso debe considerarse como una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos mostrados.
Desarrollo de un sistema de repetición (Parte 53): Esto complica las cosas (V) Desarrollo de un sistema de repetición (Parte 53): Esto complica las cosas (V)
En este artículo, presentaré un tema muy importante, que pocos comprenden realmente: Eventos personalizados. Peligros. Ventajas y fallos causados por tales elementos. Este tema es clave para quienes desean convertirse en programadores profesionales en MQL5 o en cualquier otro tipo de lenguaje. Por ello, nos centraremos en MQL5 y MetaTrader 5.
Aprendizaje automático y ciencia de datos (Parte 18): Potencie sus modelos de IA con AdaBoost Aprendizaje automático y ciencia de datos (Parte 18): Potencie sus modelos de IA con AdaBoost
AdaBoost, un potente algoritmo de refuerzo diseñado para elevar el rendimiento de sus modelos de IA. AdaBoost, abreviatura de Adaptive Boosting (refuerzo adaptativo), es una sofisticada técnica de aprendizaje por conjuntos que integra a la perfección los aprendices débiles, potenciando su fuerza predictiva colectiva.
Redes neuronales: así de sencillo (Parte 73): AutoBots para predecir la evolución de los precios Redes neuronales: así de sencillo (Parte 73): AutoBots para predecir la evolución de los precios
Seguimos hablando de algoritmos para entrenar modelos de predicción de trayectorias. En este artículo nos familiarizaremos con un método llamado "AutoBots".