Português
preview
Desarrollo de un sistema de repetición (Parte 56): Adecuación de los módulos

Desarrollo de un sistema de repetición (Parte 56): Adecuación de los módulos

MetaTrader 5Ejemplos | 30 julio 2024, 09:57
10 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior Desarrollo de un sistema de repetición (Parte 55): Módulo de control, realizamos algunas modificaciones que permitieron crear el indicador de control de modo que no fuera necesario utilizar más las variables globales del terminal. Al menos en lo que respecta al mantenimiento de la información y las configuraciones ajustadas por el usuario.

A pesar de que aquello funcionaba maravillosamente bien y era bastante estable cuando el sistema se colocaba en un gráfico que contenía un cierto número de barras, se cerraba constantemente por exceder el límite de rango al usar datos referentes a un activo personalizado.

El motivo no es que el activo personalizado sea de hecho defectuoso o genere problemas. Esto se debe a la propia base de datos que muchas veces no es suficiente para garantizar el correcto funcionamiento de todo el sistema. Básicamente, el factor problemático es el búfer.

Puede que estés pensando: "¿cómo es posible que el búfer sea el problema?" ¡El sistema funciona cuando usamos un activo real, pero cuando usamos un activo personalizado, falla, y el motivo de tal falla es el búfer del indicador!".

Sí, el motivo es el búfer. Pero no por el motivo que quizás estés imaginando. Es porque, cuando creamos un activo personalizado, tal vez el búfer no tenga un tamaño mínimo necesario para contener los datos que necesitamos colocar en él.

Al ver tal hecho, puedes pensar que bastaría con asignar más memoria y el problema se resolvería. Bueno, en lo que se refiere a MQL5, la cosa no es tan simple. La asignación de memoria para colocar datos en un búfer de indicador no se produce como probablemente estés imaginando. La asignación de memoria se basa en la cantidad de barras presentes en el gráfico. Por tanto, no sirve de nada usar alguna función para alocar memoria, pues esta de hecho no se usará como posiblemente estés imaginando o deseando hacer.


Entender el problema

El problema real no se da en el indicador de control, sino en el indicador de mouse. Pero al efectuar la corrección para solucionar la anomalía, crearemos algo que también modificará el indicador de control, aunque los cambios se verán más adelante en este mismo artículo. Pero antes, comprendamos la falla y cómo, de hecho, ocurre.

Si usas el indicador de mouse presente en los artículos anteriores y lo colocas en un gráfico de un activo personalizado que tiene, por ejemplo, 60 barras de un minuto, no tendrás problemas en los marcos temporales iguales o inferiores a 10 minutos. Pero, si intentas usar un marco temporal superior a 10 minutos, recibirás el mensaje de MetaTrader 5 "indicador de mouse: error de rango".

¿Pero por qué ocurrió esto? El motivo es que el indicador de mouse presente en el artículo anterior necesita 6 posiciones para almacenar los datos en su búfer de indicador. Entonces, las matemáticas responden a la pregunta de qué ocurrió de hecho. Con 60 barras de un minuto, podrás cambiar el marco temporal hasta el valor de 10 minutos, lo que hará que tengamos 6 barras en el gráfico. Estas seis barras harán que tengamos las seis posiciones necesarias en el búfer para poder colocar los datos en él. Sin embargo, conforme coloquemos un marco temporal mayor, el número de barras presentes en el gráfico será inferior a seis.

En este punto, el indicador de mouse generará un error de rango, ya que intentará escribir en una posición de memoria que MetaTrader 5 no alocó para que el búfer pudiera ser escrito.

Esta es la falla, y existen dos formas de corregirla. La primera es colocar una cantidad suficiente de barras en el activo personalizado, de modo que en cualquier marco temporal haya al menos seis barras en el gráfico. Esta solución, en cierto modo, no es la más adecuada, ya que podríamos acceder a un marco temporal mensual, es decir, deberíamos tener al menos seis meses de barras de un minuto cargadas en el gráfico de un activo personalizado para no tener que modificar el indicador de mouse. Esto solo es para evitar que genere el error de rango.

Personalmente, y creo que muchos estarán de acuerdo con mi visión, esto está lejos de ser una solución adecuada, especialmente tratándose de un sistema de repetición/simulador. Si el sistema estuviera enfocado únicamente en la repetición, tal vez esta solución podría ser viable, siempre y cuando se hicieran los estudios basados en solo un marco temporal o marcos temporales inferiores. Pero, dado que podemos utilizar el sistema para simular movimientos de mercado, esta solución es totalmente inviable y se necesita algo un poco más elegante.

Y es precisamente esto lo que haremos. Modificaremos el indicador de mouse de manera que la información se compacte en una única posición dentro del búfer. De esta forma, necesitaremos solo una barra en el gráfico para que el indicador pueda realizar su función.


Inicio de la implementación de la solución

El hecho de decidir usar la solución de compactar la información en una única posición se debe a que para el sistema de repetición/simulación será considerablemente más fácil agregar y mantener al menos una barra en el gráfico que hacer cualquier otra cosa.

Sin embargo, el motivo principal es que podemos desear crear una simulación de mercado y, al hacer esto, no necesitaremos utilizar una cantidad gigantesca de barras. Podremos usar solo la cantidad que necesitemos y deseemos, y el propio servicio de repetición/simulador hará que el sistema tenga el mínimo de estabilidad necesaria. Esto nos permitirá utilizar los mismos medios que en una cuenta real o en una cuenta de demostración.

Resumiendo: haremos que el búfer del indicador de mouse sea mucho más pequeño, donde solo se usará una única posición, pero aun así tendremos la misma cantidad de información que se nos devolverá cuando hagamos un requerimiento al indicador para leer el búfer. Un detalle que vale la pena mencionar al respecto aquí y ahora: si lees directamente del búfer del indicador de mouse, obtendrás solo datos en una posición de este. Estos datos deberán ser traducidos para que puedan ser realmente útiles, pero usando la clase C_Mouse, podremos usar una función que estará disponible para que esta traducción se haga de forma adecuada.

Dicho esto, pasamos a la fase de implementación real. Para comenzar realmente a hacer esto, lo primero que debe modificarse es el archivo de definición. Este tiene su código expuesto justo abajo.

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.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
22.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
23.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
24. };
25. //+------------------------------------------------------------------+
26. enum EnumEvents    {
27.          evHideMouse,               //Hide mouse price line
28.          evShowMouse,               //Show mouse price line
29.          evHideBarTime,             //Hide bar time
30.          evShowBarTime,             //Show bar time
31.          evHideDailyVar,            //Hide daily variation
32.          evShowDailyVar,            //Show daily variation
33.          evHidePriceVar,            //Hide instantaneous variation
34.          evShowPriceVar,            //Show instantaneous variation
35.          evSetServerTime,           //Replay/simulation system timer
36.          evCtrlReplayInit           //Initialize replay control
37.                   };
38. //+------------------------------------------------------------------+

Código fuente del archivo Defines.mqh

Prácticamente no hay grandes diferencias aquí, en términos generales. Pero si observas con atención, verás que ha habido cambios entre las líneas 21 y 23. Estos cambios tienen como objetivo posibilitar un mejor uso de los bits. Entonces, para facilitar la identificación del tipo de información que tratemos, usé una notación simple, en la que _32b significa 32 bits, _16b significa 16 bits y _8b significa 8 bits. Así, cuando se haga algún tipo de acceso, sabremos exactamente cuál es el número de bits que estaremos usando en realidad. Teniendo en cuenta que un valor double representa 64 bits, cada uno de los paquetes tendrá una longitud compatible para caber en estos 64 bits.

De esta forma, _32b podrá contener 2 valores, _16b podrá tener 4 valores y _8b podrá tener 8 valores. Pero presten atención al hecho de que estamos usando una unión, por lo que podemos combinar estos conjuntos. No obstante, para hacer esto de manera correcta debes entender que todo array en MQL5 se basa en el sistema de numeración usado en C/C++, es decir, siempre comenzamos en cero y cada nueva posición siempre se incrementará en una unidad.

En este punto, muchos pueden empezar a confundirse si no tienen conocimiento o una base sobre cómo funcionan las cosas en C/C++, pues el término "unidad" tal vez no describa de forma tan expresiva lo que de hecho tendremos que usar al incrementar el valor del índice para poder colocar los datos en el lugar correcto dentro del paquete. No entender este concepto puede hacer que te pierdas por completo o que, como mínimo, no comprendas cómo realmente compacto la información.

Antes de ver realmente los cambios realizados en el indicador de mouse, pasaremos rápidamente por el indicador de control. El motivo es que en el indicador de control solo ha sido necesario adecuar las funciones, procedimientos y variables a los nuevos tipos que están presentes en el archivo de cabecera Defines.mqh. Pasemos, entonces, a ver el código de la clase, que se muestra a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\C_DrawImage.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. #define def_PathBMP            "Images\\Market Replay\\Control\\"
008. #define def_ButtonPlay         def_PathBMP + "Play.bmp"
009. #define def_ButtonPause        def_PathBMP + "Pause.bmp"
010. #define def_ButtonLeft         def_PathBMP + "Left.bmp"
011. #define def_ButtonLeftBlock    def_PathBMP + "Left_Block.bmp"
012. #define def_ButtonRight        def_PathBMP + "Right.bmp"
013. #define def_ButtonRightBlock   def_PathBMP + "Right_Block.bmp"
014. #define def_ButtonPin          def_PathBMP + "Pin.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. //+------------------------------------------------------------------+
023. #define def_PrefixCtrlName    "MarketReplayCTRL_"
024. #define def_PosXObjects       120
025. //+------------------------------------------------------------------+
026. #define def_SizeButtons       32
027. #define def_ColorFilter       0xFF00FF
028. //+------------------------------------------------------------------+
029. #include "..\Auxiliar\C_Terminal.mqh"
030. #include "..\Auxiliar\C_Mouse.mqh"
031. //+------------------------------------------------------------------+
032. class C_Controls : private C_Terminal
033. {
034.    protected:
035.    private   :
036. //+------------------------------------------------------------------+
037.       enum eObjectControl {ePlay, eLeft, eRight, ePin, eNull};
038. //+------------------------------------------------------------------+
039.       struct st_00
040.       {
041.          string   szBarSlider,
042.                   szBarSliderBlock;
043.          short    Minimal;
044.       }m_Slider;
045.       struct st_01
046.       {
047.          C_DrawImage *Btn;
048.          bool         state;
049.          short        x, y, w, h;
050.       }m_Section[eObjectControl::eNull];
051.       C_Mouse   *m_MousePtr;
052. //+------------------------------------------------------------------+
053. inline void CreteBarSlider(short x, short size)
054.          {
055.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_PrefixCtrlName + "B1", OBJ_RECTANGLE_LABEL, 0, 0, 0);
056.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
057.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11);
058.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
059.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
060.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
061.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
062.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
063.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
064.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_PrefixCtrlName + "B2", OBJ_RECTANGLE_LABEL, 0, 0, 0);
065.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
066.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6);
067.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
068.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
069.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
070.          }
071. //+------------------------------------------------------------------+
072.       void SetPlay(bool state)
073.          {
074.             if (m_Section[ePlay].Btn == NULL)
075.                m_Section[ePlay].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePlay), def_ColorFilter, "::" + def_ButtonPlay, "::" + def_ButtonPause);
076.             m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, ((m_Section[ePlay].state = state) ? 0 : 1));
077.          }
078. //+------------------------------------------------------------------+
079.       void CreateCtrlSlider(void)
080.          {
081.             CreteBarSlider(77, 436);
082.             m_Section[eLeft].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eLeft), def_ColorFilter, "::" + def_ButtonLeft, "::" + def_ButtonLeftBlock);
083.             m_Section[eRight].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eRight), def_ColorFilter, "::" + def_ButtonRight, "::" + def_ButtonRightBlock);
084.             m_Section[ePin].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePin), def_ColorFilter, "::" + def_ButtonPin);
085.             PositionPinSlider(m_Slider.Minimal);
086.          }
087. //+------------------------------------------------------------------+
088. inline void RemoveCtrlSlider(void)
089.          {         
090.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
091.             for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
092.             {
093.                delete m_Section[c0].Btn;
094.                m_Section[c0].Btn = NULL;
095.             }
096.             ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName + "B");
097.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
098.          }
099. //+------------------------------------------------------------------+
100. inline void PositionPinSlider(short p)
101.          {
102.             int iL, iR;
103.             
104.             m_Section[ePin].x = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
105.             iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1);
106.             iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1);
107.             m_Section[ePin].x += def_PosXObjects;
108.              m_Section[ePin].x += 95 - (def_SizeButtons / 2);
109.              for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
110.                m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0)));
111.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
112.          }
113. //+------------------------------------------------------------------+
114. inline eObjectControl CheckPositionMouseClick(short &x, short &y)
115.          {
116.             C_Mouse::st_Mouse InfoMouse;
117.             
118.             InfoMouse = (*m_MousePtr).GetInfoMouse();
119.             x = (short) InfoMouse.Position.X_Graphics;
120.             y = (short) InfoMouse.Position.Y_Graphics;
121.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
122.             {   
123.                if ((m_Section[c0].Btn != NULL) && (m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y))
124.                   return c0;
125.             }
126.             
127.             return eNull;
128.          }
129. //+------------------------------------------------------------------+
130.    public   :
131. //+------------------------------------------------------------------+
132.       C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
133.          :C_Terminal(Arg0),
134.           m_MousePtr(MousePtr)
135.          {
136.             if ((!IndicatorCheckPass(szShortName)) || (CheckPointer(m_MousePtr) == POINTER_INVALID)) SetUserError(C_Terminal::ERR_Unknown);
137.             if (_LastError != ERR_SUCCESS) return;
138.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
139.             ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName);
140.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
141.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
142.             {
143.                m_Section[c0].h = m_Section[c0].w = def_SizeButtons;
144.                m_Section[c0].y = 25;
145.                m_Section[c0].Btn = NULL;
146.             }
147.             m_Section[ePlay].x = def_PosXObjects;
148.             m_Section[eLeft].x = m_Section[ePlay].x + 47;
149.             m_Section[eRight].x = m_Section[ePlay].x + 511;
150.             m_Slider.Minimal = SHORT_MIN;
151.          }
152. //+------------------------------------------------------------------+
153.       ~C_Controls()
154.          {
155.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn;
156.             ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName);
157.             delete m_MousePtr;
158.          }
159. //+------------------------------------------------------------------+
160.       void SetBuffer(const int rates_total, double &Buff[])
161.          {
162.             uCast_Double info;
163.             
164.             info._16b[0] = (ushort) m_Slider.Minimal;
165.             info._16b[1] = (ushort) (m_Section[ePlay].state ? SHORT_MAX : SHORT_MIN);
166.             if (rates_total > 0)
167.                Buff[rates_total - 1] = info.dValue;
168.          }
169. //+------------------------------------------------------------------+
170.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
171.          {
172.             short x, y;
173.             static short iPinPosX = -1, six = -1, sps;
174.             uCast_Double info;
175.             
176.             switch (id)
177.             {
178.                case (CHARTEVENT_CUSTOM + evCtrlReplayInit):
179.                   info.dValue = dparam;
180.                   iPinPosX = m_Slider.Minimal = (short) info._16b[0];
181.                   if (info._16b[1] == 0) SetUserError(C_Terminal::ERR_Unknown); else
182.                   {
183.                      SetPlay((short)(info._16b[1]) == SHORT_MAX);
184.                      if ((short)(info._16b[1]) == SHORT_MIN) CreateCtrlSlider();
185.                   }
186.                   break;
187.                case CHARTEVENT_OBJECT_DELETE:
188.                   if (StringSubstr(sparam, 0, StringLen(def_PrefixCtrlName)) == def_PrefixCtrlName)
189.                   {
190.                      if (sparam == (def_PrefixCtrlName + EnumToString(ePlay)))
191.                      {
192.                         delete m_Section[ePlay].Btn;
193.                         m_Section[ePlay].Btn = NULL;
194.                         SetPlay(m_Section[ePlay].state);
195.                      }else
196.                      {
197.                         RemoveCtrlSlider();
198.                         CreateCtrlSlider();
199.                      }
200.                   }
201.                   break;
202.                case CHARTEVENT_MOUSE_MOVE:
203.                   if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft))   switch (CheckPositionMouseClick(x, y))
204.                   {
205.                      case ePlay:
206.                         SetPlay(!m_Section[ePlay].state);
207.                         if (m_Section[ePlay].state)
208.                         {
209.                            RemoveCtrlSlider();
210.                            m_Slider.Minimal = iPinPosX;
211.                         }else CreateCtrlSlider();
212.                         break;
213.                      case eLeft:
214.                         PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal));
215.                         break;
216.                      case eRight:
217.                         PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider));
218.                         break;
219.                      case ePin:
220.                         if (six == -1)
221.                         {
222.                            six = x;
223.                            sps = iPinPosX;
224.                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
225.                         }
226.                         iPinPosX = sps + x - six;
227.                         PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX)));
228.                         break;
229.                   }else if (six > 0)
230.                   {
231.                      six = -1;
232.                      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                     
233.                   }
234.                   break;
235.             }
236.             ChartRedraw(GetInfoTerminal().ID);
237.          }
238. //+------------------------------------------------------------------+
239. };
240. //+------------------------------------------------------------------+
241. #undef def_PosXObjects
242. #undef def_ButtonPlay
243. #undef def_ButtonPause
244. #undef def_ButtonLeft
245. #undef def_ButtonRight
246. #undef def_ButtonPin
247. #undef def_PrefixCtrlName
248. #undef def_PathBMP
249. //+------------------------------------------------------------------+

Código fuente del archivo C_Control.mqh

Al observar el código, notarás que no existen diferencias, al menos no visibles, entre este código que se muestra arriba y el mismo código presente en el último artículo en el que se utilizó este archivo de encabezado. Sin embargo, algunas partes del código han sufrido pequeños cambios por esta razón. Puedes ver que estamos usando una conversión explícita de tipos. Uno de estos puntos en los que puedes ver esto se encuentra en las líneas 164 y 165, donde informamos al compilador de que queremos utilizar un tipo de dato de forma explícita.

Fíjate en el cambio que hay en la línea 165. Antes usábamos constantes enteras; ahora usamos constantes cortas. Pero, a pesar de estar usando constantes señalizadas, es decir, que pueden ser representadas negativamente, el valor que se debe colocar en el array será del tipo no señalizado. En este momento, puedes pensar que esto provocará un error de comprensión de los valores. Pero si has pensado esto es porque no entiendes realmente cómo se representan los valores en términos binarios. En este caso, te sugiero que estudies la representación binaria de valores, así comprenderás por qué podemos colocar valores negativos en un sistema que no los representaría y, aun así, transferir información sin pérdida de calidad.

En este mismo código, en la línea 180, puedes ver otra conversión explícita de tipos. Ahora, un valor que está en una variable no señalizada pasa a ser colocado en una variable señalizada. Esta conversión de tipos permite representar de forma adecuada los valores negativos. Observa que, en todo el código de tratamiento del evento personalizado, de inicialización del indicador de control, se utiliza este tipo de modelado, así que estudia con calma este fragmento presente entre las líneas 178 y 186, pues aquí se hace un uso intensivo de este tipo de conversión.

Es cierto que la compactación realizada aquí, en este indicador de control, no fue tan profunda como para usar de forma más adecuada los bits presentes en un double, pero esto se debe a que la información aquí no requiere un grado tan avanzado. Ahora, una última cosa sobre el código del indicador de control. Ya que solo se han realizado cambios referentes al número de la versión y al enlace en el código del indicador mismo, no repetiré aquí nuevamente el mismo código. Por tanto, todo lo que necesitarás hacer será modificar el archivo de encabezado visto en el artículo anterior por el presente en este artículo. El código del indicador permanecerá igual, sin ninguna otra diferencia además de las mencionadas. Podrá usarse sin problemas.

En cuanto al indicador de mouse, la cosa se complica un poco. Por esta razón, necesitaremos realmente los tres archivos que se utilizarán para crear el indicador. Estos se verán y explicarán en el tema siguiente.


Implementar la solución en el indicador de mouse

Como pudiste ver en el tema anterior, fue necesario hacer algunas pequeñas modificaciones en el código del indicador de control, pero tales modificaciones solo fueron necesarias en el archivo de encabezado. Sin embargo, aquí en el indicador de mouse la situación es bastante diferente y considerablemente más complicada.

Veamos, pues, los cambios. En primer lugar, veamos cómo quedó la nueva clase C_Mouse, que puede verse a continuación.

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

Código fuente del archivo C_Mouse.mqh

Tal vez no percibas de inmediato los cambios que se han producido. La mayoría de ellos solo afectará a la tipificación de las variables. Pero, aun así, merecen ser mencionados, y comprenderlos correctamente te ayudará a identificar las limitaciones de este indicador de mouse. Sí, tiene limitaciones, pero pronto entenderás cuáles son y así podrás usarlo de forma adecuada, de modo que tales limitaciones no afecten a su uso.

En primer lugar, puedes ver de inmediato, entre las líneas 21 y 24, que estamos usando el tipo SHORT, antes era el tipo INT. También se ha modificado el tipo de la variable en la línea 28.

Antes de que entres en pánico al imaginar que estamos generando un caos total al hacer tales cambios en los tipos, debo recordarte algunas características y peculiaridades entre el tipo SHORT y el INT.

El tipo INT, en MQL5, es un tipo de 32 bits, es decir, el valor puede ir de -2,147,483,648 a 2,147,483,647, considerando este como un valor señalizado. Si no está señalizado, es decir, si es solo positivo, el valor puede ir de 0 a 4,294,967,295. El tipo SHORT es un valor del tipo 16 bits, es decir, puede ir de -32,768 a 32,767 si es un valor señalizado, o de 0 a 65,535 en caso de no estar señalizado.

Ahora quiero que presten atención a un hecho y me respondan: "¿cuál es la dimensión en términos de píxel de su monitor? ¿Y por qué estoy preguntando esto?" El motivo es que usar un INT para representar la dimensión cartesiana (X e Y) en un monitor es pura ignorancia. No me entiendas mal, pero estás literalmente desperdiciando mucha información. Para que te hagas una idea, un monitor de resolución 8K, que es un gran monitor, tiene 7680 píxeles horizontales por 4320 píxeles verticales. Es decir, un monitor con una resolución increíblemente alta tiene menos de 2 elevado a 13 posiciones tanto en la horizontal como en la vertical. Este valor puede representarse perfectamente con una variable del tipo SHORT, incluso con una señalizada en la que tenemos 2 elevados a 16 posiciones posibles. Así, todavía nos sobran 3 bits libres para hacer cualquier cosa.

Pero como no necesitamos tantos bits, una simple optimización, como la que se está llevando a cabo, ya será más que suficiente. Recordemos una vez más que si usáramos INT, solo podríamos usar 2, pero si usáramos SHORT, podríamos usar 4. Esto dentro de una misma longitud double, que es de 64 bits. Como solo necesitamos 2 SHORT para representar las posiciones (X e Y) que indicarán la posición del ratón en el monitor, entonces nos sobran otros 2 SHORT para hacer otras cosas. Recordemos que tenemos 3 bits sobrantes en cada uno de los SHORT utilizados en la representación de las coordenadas del ratón en el monitor, y esto para una pantalla de 8K.

Como puedes ver, la posición no es nuestro límite. Nuestra limitación es otra. Si has estado siguiendo esta serie de artículos, debes haber notado que en varios momentos mencioné que permitiríamos que el indicador de mouse nos proporcionara algunas informaciones para usar en estudios de forma más sencilla y rápida. Y aquí es donde reside la limitación: los valores de precio y tiempo no podrán ponerse directamente en el búfer del indicador de mouse. Esto se debe a que tales valores exigirían 64 bits para cada uno y no podemos permitirnos agregar dos posiciones para eso. Por esta razón, necesitamos hacer algo un poco diferente. Así nacen algunos cambios en el código de la clase C_Mouse.

El primer cambio que notarás se encuentra en la línea 117. Presta atención al siguiente hecho: Para no tener que modificar profundamente todos los tipos presentes en el indicador de mouse, he decidido mantener las posiciones en INT, al menos dentro de la clase. Pero una vez fuera de la clase, las cosas se ajustarán a lo que realmente necesitamos.

Entonces, este procedimiento de la línea 117 traducirá las coordenadas de pantalla, que son informadas por el sistema operativo, en coordenadas ajustadas por MQL5, hasta el punto de ser compatibles con lo que se ve en el gráfico. Presta mucha atención a esto. Este procedimiento es privado de la clase, es decir, ningún código fuera de la clase tendrá acceso a él. Sin embargo, realiza la traducción de los datos, haciendo que el funcionamiento continúe siendo idéntico al que había antes. Es decir, los cambios serán transparentes para el sistema. Si ya estabas usando este indicador y haciendo uso de la clase para traducir las cosas, no tendrás problemas con respecto a los datos.

Para entender esto, debes mirar la línea 170. Esta función es la que traducirá los datos del indicador. Observa que no ha cambiado nada, aunque el código interno de esta función ha sufrido modificaciones, ya que antes esperábamos seis valores devueltos y ahora solo esperamos uno. Pero presta mucha atención a cómo trabajamos ahora. Fíjate en la línea 179, donde buscamos el valor en el búfer del indicador. Si se retorna algún valor, en la línea 181, lo asignaremos al sistema de traducción. Observa que en la línea 182 capturamos y traducimos el valor para conocer el estado de los botones. Presta atención al índice que se está utilizando y a la longitud en términos de bits que estamos usando. En la línea 183, enviamos los datos al procedimiento que convertirá las coordenadas de pantalla, es decir, X e Y, en otros tipos que utilizaremos, esto dentro del MetaTrader 5 o a través de cualquier otra aplicación que use el indicador de mouse como auxiliar. Verás que, a pesar de los cambios, estos se han quedado solo en la función, sin afectar a nada fuera de la clase.

Ahora vamos a una cuestión algo más complicada, pero primordial para que todo funcione. Esto está en la línea 191, donde declaramos un procedimiento que colocará los datos en el búfer del indicador. Antes, este mismo procedimiento formaba parte del código del indicador. Sin embargo, por motivos prácticos, he decidido ponerlo aquí, en el código de la clase, y el motivo principal es limitar el acceso a los datos de la clase.

Entonces, veamos cómo quedaron las cosas. En la línea 193, declaramos nuestra variable de compactación. En la línea 195, iniciamos la compactación de los datos. Ahora, presta mucha atención a lo que realmente va a suceder para que entiendas los índices que se están usando en el acceso a los arrays.

El array _8b contiene 8 posiciones, mientras que el array _16b contiene 4 posiciones. Ambos comienzan por el índice cero. Pero presta atención al siguiente detalle: el índice cero del array _16b forma parte de los índices cero y uno del array _8b. Es decir, cuando en la línea 195 usamos el índice cero en el array de 8 bits, estamos ocupando el índice cero del array de 16 bits, pero como la información que necesitamos colocar solo requiere del índice cero, el índice uno del array de 8 bits quedará vacío. Sin embargo, aunque el índice uno esté vacío, no podrás usar el índice cero en el array de 16 bits, precisamente porque este necesitará dos arrays de 8 bits para su construcción, como expliqué hace poco.

Entonces, nos queda el índice uno del array _8b vacío, pero a partir del índice dos del array _8b comenzamos a almacenar los valores de posición del ratón. Como el índice 2 hará referencia al índice 1 del array _16b, en la línea 196 referenciamos este índice, de modo que todo queda separado de una forma lógica y funcional. La línea 197 sigue el mismo principio de indexación. Pero puedes pensar que estamos ocupando todos los bits, pero, en realidad, no es así. Tenemos los bits del índice 3 del array _16b totalmente libres, así como el índice 1 del array _8b también libre, sin contar los 6 bits que también estarán libres dentro de los dos SHORT que estamos usando para almacenar las posiciones X e Y. Pero, como el sistema nos proporciona todo lo que necesitamos, solo informo de lo que realmente tenemos en términos de uso o no, por si alguien quiere hacer otro tipo de uso de dichos bits.

Puedes entender esto viendo la imagen a continuación.

Image

En esta imagen puedes observar el contenido de lo que habrá en el búfer, byte a byte. Ten en cuenta que cada byte representa 8 bits. Fíjate en que las zonas pintadas de azul estarán ocupadas por alguna información, mientras que las áreas en blanco están libres para recibir más datos o información en el futuro. La X indica la coordenada gráfica de X, y lo mismo se aplica a la Y. Creo que así, viendo esta imagen, comprenderás mejor lo que realmente estoy haciendo.

En cuanto al resto del código del indicador de mouse, prácticamente no será necesario dar grandes explicaciones, ya que el código en sí será bastante fácil de comprender. Sin embargo, dado que también ha sufrido modificaciones, quiero registrar el nuevo código. Mira cómo debe ser para que las cosas funcionen correctamente.

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

Código fuente del archivo C_Study.mqh

Cabe destacar que la clase C_Study apenas sufrió cambios. Por tanto, no veo motivo para más explicaciones sobre esta. Veamos ahora el código del indicador, que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.56"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12000"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
14. //+------------------------------------------------------------------+
15. C_Study *Study       = NULL;
16. //+------------------------------------------------------------------+
17. input long   user00   = 0;                                //ID
18. input C_Study::eStatusMarket user01 = C_Study::eAuction;  //Market Status
19. input color user02   = clrBlack;                          //Price Line
20. input color user03   = clrPaleGreen;                      //Positive Study
21. input color user04   = clrLightCoral;                     //Negative Study
22. //+------------------------------------------------------------------+
23. C_Study::eStatusMarket m_Status;
24. int m_posBuff = 0;
25. double m_Buff[];
26. //+------------------------------------------------------------------+
27. int OnInit()
28. {
29.    ResetLastError();
30.    Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
31.    if (_LastError != ERR_SUCCESS) return INIT_FAILED;
32.    if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
33.    {
34.       MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
35.       OnBookEvent((*Study).GetInfoTerminal().szSymbol);
36.       m_Status = C_Study::eCloseMarket;
37.    }else
38.       m_Status = user01;
39.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
40.    ArrayInitialize(m_Buff, EMPTY_VALUE);
41.    
42.    return INIT_SUCCEEDED;
43. }
44. //+------------------------------------------------------------------+
45. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
46. {
47.    m_posBuff = rates_total;
48.    (*Study).Update(m_Status);   
49.    
50.    return rates_total;
51. }
52. //+------------------------------------------------------------------+
53. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
54. {
55.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
56.    (*Study).SetBuffer(m_posBuff, m_Buff);
57.    
58.    ChartRedraw((*Study).GetInfoTerminal().ID);
59. }
60. //+------------------------------------------------------------------+
61. void OnBookEvent(const string &symbol)
62. {
63.    MqlBookInfo book[];
64.    C_Study::eStatusMarket loc = m_Status;
65.    
66.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
67.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
68.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
69.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
70.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
71.    if (loc != m_Status) (*Study).Update(m_Status);
72. }
73. //+------------------------------------------------------------------+
74. void OnDeinit(const int reason)
75. {
76.    if (reason != REASON_INITFAILED)
77.    {
78.       if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
79.          MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
80.    }
81.    delete Study;
82. }
83. //+------------------------------------------------------------------+

Código fuente del indicador de Mouse

Observa que aquí tenemos algunas diferencias respecto al código que había antes. Estas diferencias no son tan dramáticas como para que no puedas entender el código. Sin embargo, hay dos puntos que creo que merecen un breve comentario. Estos puntos son las líneas 47 y 56.

En la línea 47, puedes ver que, a diferencia de lo que se hacía antes, ahora solo almacenamos el valor que nos informará MetaTrader 5; no hacemos ningún cambio en él, ya que esto se hará en otro lugar.

Ya en la línea 56, transferimos la información a la clase C_Mouse, donde ajustaremos las cosas y grabaremos en el búfer del indicador. Observa que aquí todo se ha vuelto mucho más sencillo, precisamente porque estamos trasladando toda la complejidad a la clase.


Conclusión

Gracias a estas modificaciones y adaptaciones del código, tenemos la posibilidad de utilizar de verdad el indicador de mouse en el sistema de repetición/simulador que estamos desarrollando. Ahora podemos usar el sistema de envío de mensajes entre las aplicaciones implicadas y, al mismo tiempo, hacer uso de la lectura del búfer para transferir información entre ellas. La única limitación que tenemos es que siempre debemos usar una única posición de búfer. Así no se sobrecargará el sistema de eventos. Pero eso será tema para el futuro.

En el video a continuación, puedes ver una demostración del sistema en funcionamiento. En el anexo tendrás acceso a los códigos ya compilados para que puedas hacer las pruebas y comprender qué es lo que realmente sucede. Antes de que olvide mencionarlo, puedes verificar la falla de rango cambiando el indicador de mouse que estará en este anexo por el que se encuentra en el artículo anterior y ejecutando el servicio de este artículo. Al hacer esto, verás que el error se manifiesta.

Un último detalle: Aunque no se incluye en este artículo, explicaré el código del servicio que se estará utilizando en el próximo, ya que usaremos este mismo código como trampolín para lo que realmente estamos desarrollando. Así que ten un poco de paciencia y espera el próximo artículo, pues las cosas se están poniendo cada día más interesantes.


Video de demostración


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

Archivos adjuntos |
Anexo.zip (420.65 KB)
Desarrollo de un sistema de repetición (Parte 57): Diseccionamos el servicio de prueba Desarrollo de un sistema de repetición (Parte 57): Diseccionamos el servicio de prueba
Un último detalle: Aunque no se incluye en este artículo, explicaré el código del servicio que se estará utilizando en el próximo, ya que usaremos este mismo código como trampolín para lo que realmente estamos desarrollando. Así que ten un poco de paciencia y espera el próximo artículo, pues las cosas se están poniendo cada día más interesantes.
Desarrollamos de un asesor multidivisa (Parte 3): Revisión de la arquitectura Desarrollamos de un asesor multidivisa (Parte 3): Revisión de la arquitectura
Ya hemos avanzado bastante en el desarrollo del asesor multidivisa con varias estrategias funcionando en paralelo. Basándonos en nuestra experiencia, revisaremos la arquitectura de nuestra solución y trataremos de mejorarla antes de avanzar demasiado.
Desarrollo de un sistema de repetición (Parte 58): Volvemos a trabajar en el servicio Desarrollo de un sistema de repetición (Parte 58): Volvemos a trabajar en el servicio
Después de haber tomado un descanso en el desarrollo y perfeccionamiento del servicio usado en la repetición/simulación, retomaremos el trabajo en él. Ahora que no utilizaremos algunos recursos, como las variables globales del terminal, es necesario reestructurar por completo algunas partes de él. No se preocupen, este proceso se explicará adecuadamente para que todos puedan seguir el desarrollo del servicio.
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.