Português
preview
Desarrollo de un sistema de repetición (Parte 41): Inicio de la segunda fase (II)

Desarrollo de un sistema de repetición (Parte 41): Inicio de la segunda fase (II)

MetaTrader 5Ejemplos | 9 abril 2024, 09:53
225 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior "Desarrollo de un sistema de repetición (Parte 40): Inicio de la segunda fase (I)", empezamos a construir un indicador para sostener el sistema de estudio y el ratón. Pero antes de empezar este artículo, me gustaría hacerte una pregunta: ¿Te imaginas por qué estamos creando este indicador? ¿Qué hay detrás de mi intención de demostrar cómo crear algo así? ¿O qué estoy intentando hacer, crear un indicador para el ratón?

Muy bien. Quizá estas preguntas no tengan mucho sentido. Pero, ¿alguna vez te has parado a mirar tus propios códigos? ¿Alguna vez has prestado atención y te has dado cuenta de cuántas veces repites las mismas cosas en códigos diferentes? Como resultado, gran parte de tu código no tiene consistencia ni estabilidad alguna. Por favor, no me malinterpretes. Lejos de mí decir que tú, que a menudo puedes tener años de experiencia en programación, no deberías hacerlo. Que de hecho no estás explotando adecuadamente el lenguaje MQL5, o cualquier otro lenguaje en el que estés especializado.

Solo quiero obligarte, de alguna manera, con años de experiencia en programación, a dejar de hacer siempre las mismas cosas. Tienes que mirar más de cerca tu propio trabajo. Deja de tener que hacer siempre lo mismo por falta de reflexión antes de planificar las cosas.

Hay, de hecho, un motivo oculto detrás del inicio de una nueva fase en el sistema de repetición/simulador. Esto ya se ha tratado en otro artículo. Estoy cansado de hacer las cosas de la misma manera en MQL5. Mis códigos privados siguen unas pautas determinadas. Sin embargo, los códigos que vienen para los artículos seguían otro. Siempre intento que sean lo más sencillas y fáciles de entender posible. Pero siempre veo gente diciendo esto o aquello, ya sea sobre MQL5 o MetaTrader 5.

Quiero demostrar que se puede hacer mucho, mucho más de lo que la mayoría ha conseguido. Así que prepárate. Basta de burlas. A partir de ahora, verás cómo es realmente mi código. No voy a mostrarte cosas particulares sobre ciertos activos o indicadores. Pero voy a mostrarte que MQL5, al igual que MetaTrader 5, puede hacer mucho más de lo que has visto.

Empecemos haciendo lo siguiente: En el artículo anterior mencioné que quedaría una cuestión pendiente por resolver. Resolvámoslo aquí y ahora.


Modificación de la clase C_Mouse

La cuestión es cómo interactuar con el puntero del ratón. Se ha finalizado el código visto en el artículo anterior. El indicador no cambiará, al menos durante mucho tiempo. Pero hay una cuestión que complica bastante las cosas cuando utilizamos sus datos.

Para que entiendas en profundidad lo que voy a hacer, tendrás que abandonar la idea y los conceptos de todo lo que ya sabes sobre MetaTrader 5 y el lenguaje MQL5. Olvida todo lo que has visto sobre cómo hacer las cosas en él. Pensemos en ello de una manera totalmente diferente. Si puedes entender y asimilar esto, podrás realizar un tipo de codificación en el que la productividad y la estabilidad de tu código serán cada vez mayores.

En artículos anteriores, he hablado sobre el hecho de que debes mirar a los procesos que se ejecutan en MetaTrader 5, no como indicadores, Expert Advisors, scripts o servicios, sino como funciones. La idea de funciones quizá no sea la mejor definición para esto. Pero hay una que encaja más perfectamente: DLL. Sí, piensa en todos los procesos que se ejecutan en MetaTrader 5, especialmente los indicadores, como DLLs.

¿Y por qué es esto importante? Porque los mismos conceptos implicados en las DLL se aplican a lo que voy a mostrarte aquí. De momento, el único indicador que tenemos es el ratón. De este modo, no tendrás una comprensión total y completa de lo que va a ocurrir. Pero, como cualquier gran construcción, siempre empieza en el momento en que ponemos la primera piedra. Lo haremos ahora. Pero debido a algunos problemas, tenemos que facilitar el trabajo con el conjunto para no crear un caos total.

Si te fijas en el artículo "Desarrollo de un sistema de repetición (Parte 31): Proyecto EA - Clase C_Mouse (V)", notarás que la clase C_Mouse, al igual que la clase C_Study, funciona de cierta manera. Pero la forma en que funcionan NO nos permite utilizar una programación sencilla hasta el punto de aprovechar el indicador que utiliza estas clases. Atención: No digo que no podamos aprovecharlo al máximo. Digo que para ello necesitaremos una programación extremadamente compleja.

La idea es simplificar este programa. Para ello, eliminaremos algunas cosas de la clase C_Study y las trasladaremos a la clase C_Mouse. Pero también vamos a hacer un añadido, en cuanto a programación y estructuración, a la clase C_Mouse, para facilitar el uso del indicador, como veremos en los próximos artículos.

Lo que vamos a eliminar en la clase C_Study puede verse en el fragmento siguiente:

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #include "..\C_Mouse.mqh"
04. #include "..\..\Auxiliar\C_Mouse.mqh"
05. #include "..\..\Auxiliar\Interprocess.mqh"
06. //+------------------------------------------------------------------+
07. #define def_ExpansionPrefix "MouseExpansion_"
08. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
09. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
10. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
11. //+------------------------------------------------------------------+
12. #define def_AcessTerminal (*Terminal)
13. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
14. //+------------------------------------------------------------------+
15. class C_Study : public C_Mouse
16. {
17.     protected:
18.     private :
19. //+------------------------------------------------------------------+
20.             enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
21. //+------------------------------------------------------------------+
22.             struct st00
23.             {
24.                     eStatusMarket   Status;
25.                     MqlRates        Rate;
26.                     string          szInfo;
27.                     color           corP,
28.                                     corN;
29.                     int             HeightText;
30.             }m_Info;

Fragmento de código de la clase C_Study

Observa el fragmento a continuación: Porque la ubicación del archivo de cabecera C_Study.mqh ha cambiado. La línea 04 ha sido sustituida por la línea 03, y como aquí ya no enlazamos con el fichero de cabecera InterProcess.mqh, la línea 05 también ha sido eliminada.

Se ha eliminado la línea 20 y se ha incluido en la clase C_Mouse. El motivo de esta transferencia es facilitar la programación que se llevará a cabo posteriormente. Ahora que hemos visto los cambios realizados en la clase C_Study, pasemos a la clase C_Mouse, donde los cambios son mucho más profundos.

A continuación puedes ver el nuevo código de la clase C_Mouse en su totalidad.

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

Código del archivo C_Mouse.mqh

La razón de publicar el código completo es que no habrá archivos en el anexo, al menos de momento. Así pues, quien desee utilizar las mejoras puede hacerlo utilizando los códigos indicados. Cuando el sistema esté en una fase más avanzada, devolveré los archivos adjuntos. Por el momento no estarán disponibles. 

Si miras el código, verás que hay puntos resaltados. Son puntos que, aunque tienen cierta relevancia, no voy a tratar en detalle. La razón es que la mayoría de las veces se explican por sí solas.

El código eliminado de la línea 20 del fragmento de la clase C_Study acabó en la línea 19 de la clase C_Mouse. Pero mira la línea 18 del archivo C_Mouse.mqh. Declaro una cláusula pública. ¿Pero por qué pongo esta cláusula? La razón es que sin ella, cualquier información no tendrá el nivel de acceso que necesitamos. Realmente necesitamos que estos datos sean públicos. Normalmente solo usamos la cláusula una vez, pero debido a mi costumbre de lanzar primero los datos protegidos, luego los privados y en último lugar los públicos, verás esta misma cláusula en la línea 122. Pero eso se debe a la forma en que estoy acostumbrado a programar.

En la línea 20 también tenemos otra enumeración, que ya existía en la clase C_Mouse, pero que antes no era pública, así como la estructura que comienza en la línea 21 y llega hasta la línea 32. En este caso concreto de la estructura, nótese que no se está utilizando en ninguna variable global pública. No es una buena práctica declarar variables globales públicas en las clases; cualquier variable global de clase debe ser siempre privada o protegida, nunca público.

A partir de la línea 34, los datos dejarán de ser públicos. En este punto tenemos una cláusula protegida, que hará que los datos, funciones y procedimientos estén protegidos. Ya lo he explicado en artículos anteriores, pero esa no es la cuestión. La cuestión es la prueba de la línea 39. Esta prueba no existía antes. Cuando la variable que se está comprobando tiene el valor true, no será posible crear los objetos que se están creando en este procedimiento CreateObjectInfo. Pero, ¿por qué lo hacemos? Para entenderlo, tendremos que profundizar un poco más en el código.

Avanzando un poco más, encontramos dos líneas donde se declaran dos variables, líneas 68 y 70. Estas no existían originalmente en el código que se estaba publicando. Sin embargo, estas variables son necesarias en este caso, entre las razones está precisamente lo que se vio en la línea 39: permitir o denegar la ejecución de un procedimiento o función. Lo mismo ocurre en las líneas 165 y 194. En el caso de la línea 165 hay una razón de más peso, que explicaré más adelante. Sin embargo, en la línea 194 el motivo es el mismo que en la línea 39: evitar ejecutar la función o el procedimiento, ya que en realidad no funcionarán ni se ejecutarán correctamente cuando estemos en modo traducción.

Como es de esperar e imaginar, estas variables se inicializan en el constructor de la clase. Esto es un hecho. Pero mientras tanto, ya no contamos con un constructor de clase. Ahora tenemos dos constructores para la clase C_Mouse 😱😵😲. Si te estás iniciando en la programación orientada a objetos, probablemente esto te aterrorice. Pero esto es perfectamente normal en POO (programación orientada a objetos). Uno de los constructores se está codificando entre las líneas 124 y 129. El otro constructor se codifica entre las líneas 131 y 147. Aunque tenemos dos constructores, sólo tenemos un destructor.

Debido a que uno de los constructores satisfará un determinado tipo de demanda y el otro constructor satisfará otro tipo de demanda, tenemos que separarlos de alguna manera. Para ello se utilizan variables globales privadas. Sin ellas, sería muy improbable que pudiéramos hacer algún tipo de separación.

Así que ahora vamos a entender qué y por qué tenemos dos constructores. La primera razón es que no tiene sentido crear una segunda clase solo para cubrir la demanda que necesitamos. La segunda razón es que podemos dirigir el flujo de ejecución de forma que se reutilice el código existente. Todo ello sin tener que crear realmente una herencia ni manipular datos, funciones y procedimientos.

Si te fijas, verás que lo único que hemos hecho hasta ahora es aislar dos códigos: uno es público y el otro está protegido. Así que si estamos utilizando la clase C_Mouse para satisfacer una demanda particular, tendremos un modelo de uso. Pero cuando vayamos a satisfacer otro tipo de demanda, tendremos un modelo diferente. Pero la forma de programar utilizando la clase siempre será la misma. Es entonces cuando la utilizamos en nuestras rutinas.

Mira el constructor que está codificado entre las líneas 131 y 147. Observarás que es prácticamente el mismo que el visto en "Desarrollo de un sistema de repetición (Parte 31): Proyecto Expert Advisor — Clase C_Mouse (V)". Esto es a propósito, precisamente porque este constructor será el responsable de satisfacer las demandas de la clase cuando el código esté realmente haciendo uso de los objetos presentes en la clase. La diferencia entre este y el constructor original es la línea 133, que inicializa la nueva variable para indicar que vamos a utilizar la clase en su forma original.

El hecho de que esto ocurra significa que gran parte del código sigue siendo el mismo, y cualquier explicación dada en ese artículo específico será válida. A continuación, podemos centrarnos en la segunda forma de utilizar la clase. Para ello, veamos primero el código del constructor que se encuentra entre las líneas 124 y 129. A pesar de la simplicidad, hay que tener en cuenta que inicializamos todas las variables que son realmente necesarias.

Esto simplifica el uso de la clase como traductor. Quizá no hayas entendido los cómo y los porqué de hacerlo. Pero utilizaremos la clase C_Mouse como traductor del puntero del ratón. Esto parece bastante complejo y difícil de hacer, ¿no es cierto? Pero no, esto no es nada complejo de hacer. Hay una razón por la que lo hacemos así.

Para entenderlo, hay que pensar un poco: cuando creas programas, ya sean indicadores, EAs, scripts o cualquier otra cosa, sueles añadir cosas que se repiten mucho. Una de estas cosas son precisamente las rutinas de manejo del ratón. Si en cada EA que creas, tienes que añadir rutinas para generar estudios o analizar el ratón, tu código nunca será realmente robusto. Sin embargo, cuando creamos algo que perdurará en el tiempo, puede mejorarse independientemente del resto del código. Entonces tendremos un nuevo tipo de programa. Naturalmente, el código será mucho más robusto y eficaz.

En esta clase, siempre supondremos que el indicador del ratón estará presente en el gráfico. Recuerda: nunca debemos asumir algo, pero aquí asumiremos que el indicador estará en el gráfico. Cualquiera que utilice la clase C_Mouse como traductor debe ser consciente de este hecho. La clase supondrá que el indicador está en el gráfico.

Una vez comprendido este hecho, podemos pasar a otra pregunta. En realidad no necesitamos que la clase C_Mouse traduzca el indicador por nosotros. Puede hacerlo directamente en el programa que está creando. Sin embargo, es mucho más cómodo y sencillo dejar que la clase haga esta traducción por nosotros. La razón es que si no deseas utilizar el traductor, todo lo que tenemos que hacer es modificar el constructor de la clase y añadir la llamada de manejo de eventos a nuestro programa.

Te darás cuenta de lo fácil que te resultará hacer las cosas. Pero aún así quiero que utilices el sistema de traducción. Para entender cómo funciona el sistema de traducción, basta con ver la rutina GetInfoMouse. Está en la línea 163. Al principio era una rutina constante. Pero ahora necesitamos que deje de ser así. Aunque no se pueden cambiar los datos fuera de la rutina, necesitamos que se cambien dentro de la rutina.

En el artículo anterior "Desarrollo de un sistema de repetición (Parte 40): Inicio de la segunda fase (I)", verás que es muy complicado interpretar, o mejor dicho, mantener un estándar en la codificación para interpretar los datos del buffer de indicadores. Precisamente por eso, te muestro cómo modificar la clase C_Mouse para crear esta estandarización. Para que te hagas una idea de lo complicado que sería, piensa que cada vez que necesitaras utilizar el puntero del ratón, tendrías que codificar lo mismo que se ve en las rutinas CheckClick y GetInfoMouse. En resumen: Una bendita molestia.

Veamos qué ocurre aquí, empezando por la rutina CheckClick. En esta rutina simplemente cargamos y probamos el valor en los datos del ratón, ya sea como indicador o como traductor. En cualquier caso, ya sea para analizar el indicador o para utilizar el propio indicador. En la línea 160, comprobaremos si el botón a analizar se ha activado o no, es decir, si se ha pulsado o no.

He aquí una cuestión que debes comprender. Independientemente de si estamos utilizando la clase C_Mouse como intérprete o como puntero, siempre obtendremos algún tipo de respuesta de las funciones CheckClick y GetInfoMouse. Siempre. Y esta respuesta siempre reflejará lo que se está haciendo como un ratón, independientemente de dónde utilicemos la información.

Para entender la respuesta dada por CheckClick, tenemos que observar y entender cómo funciona la función GetInfoMouse. Esta función se ve a partir de la línea 163.

En la línea 165 de la función, realizamos una prueba. Su finalidad es comprobar si estamos utilizando la clase como intérprete o como indicador. Si la prueba pasa, la clase estará en modo intérprete, en cuyo caso necesitaremos acceder al buffer del indicador. He aquí una cuestión complicada y sencilla a la vez. Veamos primero la parte sencilla.

En la línea 171 reseteamos los datos de la estructura de retorno. Esta estructura es en realidad una variable global privada de la clase, que se declara en la línea 58. Una vez hecho esto, pedimos a MetaTrader 5 que lea el buffer del indicador. Si tenemos éxito en la lectura, que se realiza en la línea 172, empezaremos a traducir los datos a la estructura de retorno.

La forma en que se traducen los datos sigue exactamente la misma lógica utilizada para codificarlos. Esta codificación puede verse observando la rutina SetBuffer del artículo anterior. Para facilitar las cosas, puedes ver la misma rutina en el fragmento siguiente:

102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }

Fragmento del código del indicador

Observe que la información colocada en la línea 107 del fragmento del indicador se descodifica en la línea 174 de la clase. La mayoría de las veces la traducción es sencilla. Pero debes atenerte a las normas establecidas durante la codificación. Esto puede entenderse mejor cuando almacenamos los datos en la línea 112 del fragmento del indicador. Ten en cuenta que en este caso estamos trabajando con 2 valores comprimidos en un valor doble. A la hora de traducirlo, tenemos que hacer lo contrario. Esto se hace en la línea 177 de la clase, donde capturamos el valor y en las líneas 178 y 179, colocamos los valores en el lugar correcto para su uso posterior.

Lo mismo ocurre en la línea 113 del fragmento del indicador, donde almacenamos el valor del clic del ratón. Y lo traducimos en la línea 181 de la clase. Pero miremos de nuevo, en la línea 113 del fragmento del indicador. Ten en cuenta que el operador ternario almacenará un valor de cero si estamos en modo de estudio. Es importante que lo entiendas. Porque si estamos haciendo un estudio a través de un indicador, y la clase se está utilizando para traducirlo, al comprobar si se ha producido un clic mediante la función CheckClick, ésta devolverá false si estamos ejecutando un estudio. Este tipo de cosas siempre ocurrirán, siempre claro está, que utilicemos el indicador y la clase como traductor.

Esta era la parte sencilla y fácil de entender. Pero como se ha dicho antes, hay otra parte: la parte difícil y complicada.

Esta parte complicada ocurre cuando utilizamos la clase como intérprete. Sin embargo, no tenemos acceso a la memoria intermedia del indicador. Esto suele ocurrir cuando se retira el indicador del gráfico. Cuando esto ocurra, la línea 169 generará un manejador nulo, por lo que no tendremos búfer que leer. Aún así, tendremos que ejecutar la línea 171, que restablecerá los datos por nosotros.

Esto puede provocar diversas perturbaciones y fallos a la hora de interpretar o intentar hacer algo con los datos del indicador. Aunque el sistema siempre informe cero, en realidad no tendremos ninguna prueba positiva de clic o movimiento, seguimos teniendo problemas con esto. No en este caso concreto, sino en otros casos que también nos supondrán un problema. Cuando esto ocurra, volveré sobre este tema.


Uso de la clase C_Mouse como traductor

Al principio con fines demostrativos, ya que ampliaremos la información más adelante.

Veamos el código para un EA, que funcionará de la misma manera que lo visto en el artículo "Desarrollando un sistema de repetición (Parte 31): Proyecto Expert Advisor - Clase C_Mouse (V)", pero vamos a codificarlo de forma diferente.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Generic EA for use on Demo account, replay system/simulator and Real account."
04. #property description "This system has means of sending orders using the mouse and keyboard combination."
05. #property description "For more information see the article about the system."
06. #property version   "1.41"
07. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
08. #property link "https://www.mql5.com/en/articles/11607"
09. //+------------------------------------------------------------------+
10. #include <Market Replay\Auxiliar\C_Mouse.mqh>
11. //+------------------------------------------------------------------+
12. C_Mouse *mouse = NULL;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.     mouse = new C_Mouse("Indicator Mouse Study");
17.     
18.     return INIT_SUCCEEDED;
19. }
20. //+------------------------------------------------------------------+
21. void OnTick() 
22. { }
23. //+------------------------------------------------------------------+
24. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
25. {
26.     C_Mouse::st_Mouse Infos;
27.     
28.     switch (id)
29.     {
30.             case CHARTEVENT_MOUSE_MOVE:
31.                     Infos = (*mouse).GetInfoMouse();
32.                     Comment((string)Infos.Position.Price + " :: [" + TimeToString(Infos.Position.dt), "]");
33.                     break;
34.     }
35. }
36. //+------------------------------------------------------------------+
37. void OnDeinit(const int reason)
38. {
39.     delete mouse;
40. }
41. //+------------------------------------------------------------------+

Código del EA

Aunque no lo parezca, el código visto anteriormente tiene el mismo comportamiento que el visto en el artículo que acabamos de mencionar, con la ventaja de ser considerablemente más sencillo, práctico y robusto. La razón de ello no es fácil de entender en este momento, pero te darás cuenta poco a poco a medida que avancemos con la codificación.

Puede que te des cuenta de que en realidad no estamos haciendo nada extremadamente complicado. Simplemente asumimos que lo que se indica estará en el gráfico. En la línea 16 le decimos cuál es el nombre corto del indicador, y en la línea 39 eliminamos la clase C_Mouse.

Ahora quiero que mires las líneas 31 y 32, que arrojarán los datos al ratón sobre el gráfico para que puedas observarlos.

Lo bueno de esto es que si declaras en la línea 16 que estás usando la clase como indicador, todo lo que tienes que hacer es añadir una llamada a la función DispatchMessage en la rutina OnChatEvent para que tenga el mismo efecto que usar el indicador gráfico. En otras palabras, el programa no cambiará; se adaptará a lo que queremos y lo usaremos.

Si quieres utilizar, y te lo aconsejo, la rutina como traductor, el EA analizará hasta el punto de saber dónde y qué está haciendo el ratón, siempre informando correctamente.


Conclusión

Es importante que entiendas cómo suceden las cosas. De lo contrario, te perderás por completo en los próximos artículos, donde ya no usaremos las cosas como antes. Vamos a utilizarlas de una forma mucho más sofisticada. Aunque lo que se ha mostrado en los dos últimos artículos parece muy complejo y extremadamente avanzado, esto es para aquellos que no tienen muchos conocimientos de programación. Todo lo que hemos visto en los dos últimos artículos es sólo una pequeña preparación para añadir la aplicación Chart Trader. En los próximos artículos empezaremos a añadir y desarrollar Chart Trader para nuestro sistema. Esta aplicación se utiliza precisamente para que podamos operar directamente en el mercado, algo muy útil y necesario, sobre todo porque no podemos confiar en el sistema de órdenes de mercado en MetaTrader 5. Así que tenemos que crear la nuestra. Y esto es Chart Trader.

Independientemente de esto, aquí todavía estamos trabajando en un nivel básico de MQL5. Aunque imagines y sientas que este material es avanzado, sigue estando en su nivel más básico. Pero no te sientas mal por ver las cosas de otra manera. De hecho, comprendo si piensas que esto puede ser demasiado avanzado. Pero eso es porque no estabas explorando MQL5 en profundidad. Siempre que camines por la superficie pensarás que estás en una colina alta, hasta que alguien venga y te muestre que solo has estado caminando a nivel del mar todo este tiempo.Así que créeme, pronto considerarás que todo el material que has visto en los últimos artículos es un juego de niños, algo que cualquier junior podría hacer. Así que prepárate. Hay mucho plomo por delante. Y cuando llegue, volveremos a estos temas de este indicador de ratón.


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

Archivos adjuntos |
Anexo.zip (420.65 KB)
Algoritmos de optimización de la población: Algoritmo de gotas de agua inteligentes (Intelligent Water Drops, IWD) Algoritmos de optimización de la población: Algoritmo de gotas de agua inteligentes (Intelligent Water Drops, IWD)
El artículo analiza un interesante algoritmo, las gotas de agua inteligentes, IWD, presente en la naturaleza inanimada, que simula el proceso de formación del cauce de un río. Las ideas de este algoritmo han permitido mejorar significativamente el anterior líder de la clasificación, el SDS, y el nuevo líder (SDSm modificado); como de costumbre, se puede encontrar en el archivo del artículo.
Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con integración RestAPI  (Parte 4): Organización de funciones en clases en MQL5 Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con integración RestAPI (Parte 4): Organización de funciones en clases en MQL5
Este artículo examina la transición de la codificación procedimental a la programación orientada a objetos (POO) en MQL5, enfocándose en la integración con REST APIs. Discutimos la organización de funciones de solicitudes HTTP (GET y POST) en clases y destacamos ventajas como el encapsulamiento, la modularidad y la facilidad de mantenimiento. La refactorización de código se detalla, y se muestra la sustitución de funciones aisladas por métodos de clases. El artículo incluye ejemplos prácticos y pruebas.
Validación cruzada simétrica combinatoria en MQL5 Validación cruzada simétrica combinatoria en MQL5
El artículo muestra la implementación de la validación cruzada simétrica combinatoria en MQL5 puro para medir el grado de ajuste tras optimizar la estrategia usando el algoritmo completo lento del simulador de estrategias.
Desarrollo de un sistema de repetición (Parte 40): Inicio de la segunda fase (I) Desarrollo de un sistema de repetición (Parte 40): Inicio de la segunda fase (I)
Esta es la nueva fase del sistema de repetición/simulación. En esta etapa, la conversación será realmente una conversación, y el contenido se volverá bastante denso. Les insto a leer el artículo con atención y a utilizar siempre las referencias que se proporcionen. Esto les ayudará a comprender mejor lo que se les está explicando.