Português
preview
Desarrollo de un sistema de repetición (Parte 51): Esto complica las cosas (III)

Desarrollo de un sistema de repetición (Parte 51): Esto complica las cosas (III)

MetaTrader 5Ejemplos | 24 julio 2024, 14:04
17 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior Desarrollo de un sistema de repetición (Parte 50): Esto complica las cosas (II), comenzamos a modificar más profundamente el indicador de control, hasta lograr que se mantuviera dentro del gráfico. Pero no en cualquier gráfico, sino en el que fue abierto por el servicio de repetición/simulación. Lo principal que se implementó allí es la posibilidad de utilizar una plantilla personal, en lugar de la plantilla que el sistema exigía.

Permitir este tipo de cosas hace que el uso de todo el sistema de repetición/simulación sea mucho más agradable y adecuado para quienes realmente desean usar el sistema para generar algún tipo de estudio. Ya que puedes crear una plantilla, usarla en el sistema de repetición/simulación y luego usar la misma plantilla en una cuenta real. Por esta razón, el sistema está sufriendo los cambios que estoy mostrando.

Sin embargo, la interacción entre el indicador de control y el usuario no ha sido muy buena. Esto se debe a que el sistema tiene algunos puntos duplicados. Pero el principal problema es que el sistema no era tan seguro ni estable como parecía. Todo esto era así porque durante la fase de desarrollo, que tuvo lugar en la primera fase, no se tuvo en cuenta que algunos usuarios a menudo intentan utilizar el sistema de una manera diferente a la prevista. Pero con el cambio de dirección y posicionamiento actuales, el nivel de seguridad y estabilidad comenzará a ser el adecuado.

Aunque en este momento aún estamos lidiando con un sistema inestable en lo que se refiere a la interacción con el usuario, esto pronto se corregirá. Una vez realizada esta corrección, todo el sistema se beneficiará.

Si leíste, y te aconsejo que lo hagas, el artículo anterior, habrás notado que comenzamos a usar el indicador de control directamente a través del servicio. Entonces, este indicador dejó de estar disponible libremente para que el usuario lo coloque en cualquier gráfico.

Y así, analizando con calma el código, noté que podemos hacer algunos cambios para que las cosas sean aún más adecuadas. Sin embargo, será necesario realizar algunas modificaciones en otros módulos que también serán utilizados por el sistema de repetición/simulación. Por este motivo, este artículo estará enfocado en explicar los cambios que ocurrirán. Esto se debe a que algunos de estos módulos podrán ser accesibles por el usuario. Por ello, es adecuado que comprendas qué puede o no ser manipulado, ya que eres el usuario de este sistema y el programador que está siguiendo su desarrollo. Esto es para evitar inestabilidades en el uso de cualquiera de los módulos. Recuerda que algunos módulos se podrán utilizar en cuenta REAL y cuenta DEMO, no solo en el sistema de repetición/simulador.

Tal vez pienses que los cambios que se verán aquí no son tan grandes. Pero antes de promover nuevas modificaciones, tenemos que estabilizar el código. Por este motivo, el artículo podría parecer poco enfocado. Pero repito: debes entender cómo se está desarrollando el sistema. Si esto no se comprende correctamente, no podrás usar el sistema adecuadamente.

Sin más preámbulos, comencemos con las modificaciones.


Uso masivo de los módulos

El primer cambio se hará en el código fuente del indicador de control. El código visto hasta el artículo anterior no utilizaba algunos de los módulos que ya fueron diseñados y desarrollados. Por esto, había una cierta disparidad entre lo que ocurría en el indicador de control y otros módulos del sistema. 

Uno de estos módulos es precisamente el Indicador de mouse. Este módulo fue desarrollado para acumular todo lo relacionado con el mouse. Así, cualquier otro código que se desarrolle no tendrá que preocuparse por realizar ciertos ciertas pruebas y análisis. Todo esto lo hará el módulo del indicador de mouse.

Este indicador se desarrolló hace algún tiempo en esta misma serie de artículos sobre el sistema de repetición/simulación. Sin embargo, se había concebido para trabajar inicialmente con un sistema de cuenta real o cuenta demo, y no es suficientemente adecuado para trabajar como lo hacemos ahora. Pero esto se verá en detalle pronto.

La forma de integrar el módulo del indicador de mouse con el indicador de control se puede ver en el código a continuación, donde ya se han realizado algunas correcciones y cambios para garantizar que esta integración ocurra.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.51"
07. #property link "https://www.mql5.com/es/articles/11877"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Controls *control = NULL;
14. //+------------------------------------------------------------------+
15. input long user00 = 0;   //ID
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     u_Interprocess Info;
20. 
21.     ResetLastError();       
22.     if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", "Indicator Mouse Study")) == POINTER_INVALID)
23.             SetUserError(C_Terminal::ERR_PointerInvalid);
24.     if (_LastError != ERR_SUCCESS)
25.     {
26.             Print("Control indicator failed on initialization.");
27.             return INIT_FAILED;
28.     }       
29.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
30.     EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
31.     (*control).Init(Info.s_Infos.isPlay);
32.     
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     static bool bWait = false;
39.     u_Interprocess Info;
40.     
41.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
42.     if (!bWait)
43.     {
44.             if (Info.s_Infos.isWait)
45.             {
46.                     EventChartCustom(user00, C_Controls::ev_WaitOn, 1, 0, "");
47.                     bWait = true;
48.             }
49.     }else if (!Info.s_Infos.isWait)
50.     {
51.             EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
52.             bWait = false;
53.     }
54. 
55.     return rates_total;
56. }
57. //+------------------------------------------------------------------+
58. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
59. {
60.     (*control).DispatchMessage(id, lparam, dparam, sparam);
61. }
62. //+------------------------------------------------------------------+
63. void OnDeinit(const int reason)
64. {
65.     switch (reason)
66.     {
67.             case REASON_TEMPLATE:
68.                     Print("Modified template. Replay/simulation system shutting down.");
69.             case REASON_PARAMETERS:
70.             case REASON_REMOVE:
71.             case REASON_CHARTCLOSE:
72.                     if (ChartSymbol(user00) != def_SymbolReplay) break;
73.                     GlobalVariableDel(def_GlobalVariableReplay);
74.                     ChartClose(user00);
75.                     break;
76.     }
77.     delete control;
78. }
79. //+------------------------------------------------------------------+

Código fuente del Indicador de control

Notarás que este código es muy diferente al que existía antes. Pero la diferencia más notable está en la función OnInit, donde en la línea 22 tenemos una declaración completamente diferente a la que había antes para que se referencie la clase de control.

Es posible que te sientas un poco perdido al mirar y tratar de entender lo que hace esta línea 22. Pero en esta línea estamos haciendo dos cosas:

  • Primero, convirtiendo el indicador de control en un módulo que puede ser usado por cualquier otro módulo que necesite ver lo que este indicador está haciendo.
  • Segundo, le estamos diciendo al indicador de control que ahora no usaremos una codificación para analizar el mouse. Es decir, ahora el indicador de control preguntará al indicador de mouse lo que el usuario está haciendo o pretende hacer. Con base en esta información, el indicador de control hará lo que sea más adecuado.

Este tipo de cambio, o decisión, de hacer que el indicador de mouse sea responsable de la interacción entre el usuario, el mouse y el gráfico, es, sin duda, la mejor manera de hacer las cosas. No tiene sentido duplicar las cosas, ya que al hacerlo, corregimos el problema en el indicador de mouse, pero generamos problemas en el indicador de control, y cuando ponemos a ambos a trabajar juntos, uno interfiere con el otro. No queremos eso. Queremos que ambos trabajen en armonía y en perfecta sincronía. De esta manera, evitamos perder tiempo corrigiendo las cosas, ya que basta con corregir la forma en que se está produciendo la interacción.

Muy bien, pero si prestas atención, notarás que el indicador de control ya no menciona la clase C_Terminal como antes. ¿Por qué? El motivo es la herencia. Decidí hacer que la clase de control heredara la clase C_Terminal. El motivo de hacer esto es un poco complicado de explicar en este momento, pero fue necesario por algo que haremos después.

Dada esta primera explicación sobre el código del indicador, echemos un vistazo al código de la clase de control.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP           "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
008. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft        def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock   def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight       def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock  def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait        def_PathBMP + "Wait.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. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName    "Market Replay _ "
025. #define def_NameObjectsSlider   def_PrefixObjectName + "Slider"
026. #define def_PosXObjects         120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. class C_Controls : private C_Terminal
032. {
033.    protected:
034.            enum EventCustom {ev_WaitOn, ev_WaitOff};
035.    private :
036. //+------------------------------------------------------------------+
037.            string  m_szBtnPlay;
038.            bool    m_bWait;
039.            struct st_00
040.            {
041.                    string  szBtnLeft,
042.                            szBtnRight,
043.                            szBtnPin,
044.                            szBarSlider,
045.                            szBarSliderBlock;
046.                    int     posPinSlider,
047.                            posY,
048.                            Minimal;
049.            }m_Slider;
050.            C_Mouse *m_MousePtr;
051. //+------------------------------------------------------------------+
052. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
053.                    {
054.                            ObjectCreate(GetInfoTerminal().ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
055.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
056.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, y);
057.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
058.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
059.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_ZORDER, 1);
060.                    }
061. //+------------------------------------------------------------------+
062. inline void CreteBarSlider(int x, int size)
063.                    {
064.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
065.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
066.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
067.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
068.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
069.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
070.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
071.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
072.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
073.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
074.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
075.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
076.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
077.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
078.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
079.                    }
080. //+------------------------------------------------------------------+
081.            void CreateBtnPlayPause(bool state)
082.                    {
083.                            m_szBtnPlay = def_PrefixObjectName + "Play";
084.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
085.                            ObjectSetInteger(GetInfoTerminal().ID, m_szBtnPlay, OBJPROP_STATE, state);
086.                    }
087. //+------------------------------------------------------------------+
088.            void CreteCtrlSlider(void)
089.                    {
090.                            u_Interprocess Info;
091.                            
092.                            m_Slider.szBarSlider       = def_NameObjectsSlider + " Bar";
093.                            m_Slider.szBarSliderBlock  = def_NameObjectsSlider + " Bar Block";
094.                            m_Slider.szBtnLeft         = def_NameObjectsSlider + " BtnL";
095.                            m_Slider.szBtnRight        = def_NameObjectsSlider + " BtnR";
096.                            m_Slider.szBtnPin          = def_NameObjectsSlider + " BtnP";
097.                            m_Slider.posY = 40;
098.                            CreteBarSlider(77, 436);
099.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
100.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
101.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
102.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
103.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
104.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
105.                            PositionPinSlider(Info.s_Infos.iPosShift);
106.                    }
107. //+------------------------------------------------------------------+
108. inline void RemoveCtrlSlider(void)
109.                    {                       
110.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
111.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_NameObjectsSlider);
112.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
113.                    }
114. //+------------------------------------------------------------------+
115. inline void PositionPinSlider(int p, const int minimal = 0)
116.                    {
117.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
118.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
119.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
120.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
121.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
122.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
123.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
124.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
125.                            ChartRedraw(GetInfoTerminal().ID);
126.                    }
127. //+------------------------------------------------------------------+
128.    public  :
129. //+------------------------------------------------------------------+
130.            C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
131.                    :C_Terminal(Arg0),
132.                     m_bWait(false),
133.                     m_MousePtr(MousePtr)
134.                    {
135.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
136.                            m_szBtnPlay          = NULL;
137.                            m_Slider.szBarSlider = NULL;
138.                            m_Slider.szBtnPin    = NULL;
139.                            m_Slider.szBtnLeft   = NULL;
140.                            m_Slider.szBtnRight  = NULL;
141.                    }
142. //+------------------------------------------------------------------+
143.            ~C_Controls()
144.                    {
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
146.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixObjectName);
147.                    }
148. //+------------------------------------------------------------------+
149.            void Init(const bool state)
150.                    {
151.                            CreateBtnPlayPause(state);
152.                            GlobalVariableTemp(def_GlobalVariableReplay);
153.                            if (!state) CreteCtrlSlider();
154.                            ChartRedraw(GetInfoTerminal().ID);
155.                    }
156. //+------------------------------------------------------------------+
157.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
158.                    {
159.                            u_Interprocess Info;
160.                            
161.                            switch (id)
162.                            {
163.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
164.                                            if (lparam == 0) break;
165.                                            m_bWait = true;
166.                                            CreateBtnPlayPause(true);
167.                                            break;
168.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
169.                                            if (lparam == 0) break;
170.                                            m_bWait = false;
171.                                            Info.df_Value = dparam;
172.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
173.                                            break;
174.                                    case CHARTEVENT_OBJECT_DELETE:
175.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
176.                                            {
177.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
178.                                                    {
179.                                                            RemoveCtrlSlider();
180.                                                            CreteCtrlSlider();
181.                                                    }else
182.                                                    {
183.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
184.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
185.                                                    }
186.                                                    ChartRedraw(GetInfoTerminal().ID);
187.                                            }
188.                                            break;
189.                            }
190.                    }
191. //+------------------------------------------------------------------+
192. };
193. //+------------------------------------------------------------------+
194. #undef def_PosXObjects
195. #undef def_ButtonPlay
196. #undef def_ButtonPause
197. #undef def_ButtonLeft
198. #undef def_ButtonRight
199. #undef def_ButtonPin
200. #undef def_NameObjectsSlider
201. #undef def_PrefixObjectName
202. #undef def_PathBMP
203. //+------------------------------------------------------------------+

Código fuente de la clase C_Control

Tal vez no percibas grandes cambios en el código. De hecho, son bastante sutiles. La primera es que la clase de control está heredando de forma privada la clase C_Terminal. Esto puede verse en la línea 31. Con esto, ya no necesitaremos usar un puntero para acceder a la clase C_Terminal. Es decir, los procedimientos para acceder masivamente a la clase C_Terminal se pueden realizar directamente, y en algunos casos, un poco más rápido, debido a ciertos detalles de compilación. Pero este tipo de cosas no vienen al caso.

La cuestión es que en la línea 130, donde se declara el constructor de la clase, tenemos algo bastante curioso. Esto, teniendo en cuenta que, en este primer momento, solo el servicio accederá a este indicador de control. Observa la línea 135. De hecho, no es tan necesario. Pero como siempre es adecuado reutilizar el código, aseguramos que el indicador siga las premisas que necesitamos. Es decir, solo un único indicador por gráfico. Nuevamente, esto no sería realmente necesario, ya que en este primer momento, solo el servicio tiene acceso a este indicador y puede colocarlo en el gráfico. Hay otras formas de hacer esto, pero no entraré en detalles, ya que no quiero motivar a nadie a usar un recurso sin entender realmente cómo funciona.

Otra cosa que merece ser mencionada en este constructor es que en la línea 133 estamos almacenando en una variable global privada de la clase un puntero para acceder al indicador de mouse. Pero esto se entenderá después.

En este mismo código, si prestas atención, notarás una corrección que se realizó, pero, al igual que todo el código, es algo muy sutil que, sin embargo, cambia mucho las cosas. No para el código, sino para MetaTrader 5. Es el hecho de que las llamadas a ChartRedraw reciben un valor. Normalmente no pasamos ningún valor a esta llamada. ¿Por qué lo estoy haciendo ahora? El motivo es que existe una diferencia en el valor del ID del gráfico.

En el artículo anterior, expliqué esta diferencia. Sin embargo, tal vez no haya quedado lo suficientemente claro. Así que vamos reforzar la idea. El gran problema con el ID del gráfico no es quién abrirá el gráfico, sino quién colocará los objetos en él. Vamos a entender esto con calma para ver por qué a veces necesitamos pasar un valor a la llamada ChartRedraw y en otros momentos no es necesario.

Cuando el servicio abre el gráfico (aunque podría ser cualquier otra cosa), este gráfico recibirá un ID por parte de MetaTrader 5. Si verificas este ID, notarás un valor al analizar el valor retornado por ChartOpen. Bien. Ahora, para colocar, por ejemplo, un indicador en este gráfico, el programa debe utilizar este mismo ID, que será devuelto por la función ChartOpen. Hasta aquí, todo está claro. Pero es precisamente en este punto donde surge el problema.

Cuando tú, usuario, colocas el mismo indicador en el gráfico, no recibirás el mismo ID que el programa obtuvo usando ChartOpen. Bien, ahora todo parece confuso. Puede parecer que estoy loco o que no sé lo que digo, pero es así. Cuando el servicio abra el gráfico, recibirás un valor de ID a través de ChartOpen, y deberás usar este valor para crear un handle de indicador y así poder colocar el indicador en el gráfico correcto. Si el indicador, como en el caso del indicador de control, utiliza funciones para colocar objetos en el gráfico mediante la llamada ObjectCreate, necesitarás proporcionar un ID para que MetaTrader 5 sepa cuál es el gráfico correcto.

El ID que deberás proporcionar NO, y repito, NO debe ser el ID obtenido por ChartID si el gráfico fue abierto por ChartOpen. Si usas el ID de gráfico proporcionado por ChartID en una llamada ObjectCreate en un gráfico abierto por ChartOpen, el objeto no se mostrará en el gráfico. Pero si este mismo código que usa la función ObjectCreate se coloca en el gráfico a través del usuario o mediante una plantilla, deberás usar como ID el valor proporcionado por ChartID.

Sé que esto parece muy confuso y desorientador, pero es precisamente lo que debe hacerse. Por esta razón, la clase C_Terminal sufrió una actualización hace algunos artículos, precisamente para que pudiera manejar esto.

Pero dado que nuestro código no sabe exactamente quién lo ejecutó en el gráfico, usamos una llamada a la clase C_Terminal para que se retorne el ID correcto. Así, la función ChartRedraw ejecutará la actualización del gráfico correctamente, como se espera.

A pesar de toda esta complicación, puedes notar en la línea 157, donde comienza el manejador de mensajes, que es el código responsable de tratar los eventos informados por MetaTrader 5. Los eventos que analizan el movimiento del mouse y los eventos de clic fueron eliminados. Esto se debe a que vamos a manejar este tipo de eventos de una forma un poco diferente, aunque dentro de la misma función. 

Sin embargo, antes de que este tratamiento se realice de hecho, necesitamos hacer algunos cambios adicionales. Mas ahora, no en este código del indicador, o del servicio. Vamos a tener que volver al código del indicador de mouse, y hacer algunos cambios allí.


actualización del código del Indicador de Mouse

Esta actualización que haremos ahora, no contradice lo que ya se usa en el indicador de mouse. Necesitamos hacer esta actualización precisamente por el motivo mencionado arriba. Ya que en el momento en que usemos el servicio de repetición/simulación, este iniciará en el gráfico el indicador de mouse, para que podamos interactuar con el sistema de repetición/simulación. Pero no necesitas preocuparte por ciertas cosas. Ya que el propio código hará los ajustes necesarios, para garantizar que el indicador de mouse funcione de la misma forma como lo haría si tú, usuario, lo colocases manualmente en el gráfico, o usando una plantilla.

El gran problema surge cuando usamos los medios que estamos empleando para inicializar correctamente el sistema de repetición/simulación. Entonces, la primera cosa que podemos ver y que ha sido actualizada es el código fuente del indicador de mouse. Este se puede ver en su totalidad 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.51"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/es/articles/11877"
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 - 4;
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.     SetBuffer();
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.             delete Study;
81.     }
82. }
83. //+------------------------------------------------------------------+
84. inline void SetBuffer(void)
85. {
86.     uCast_Double Info;
87.     
88.     m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
89.     m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
90.     Info._datetime = (*Study).GetInfoMouse().Position.dt;
91.     m_Buff[m_posBuff + 1] = Info.dValue;
92.     Info._int[0] = (*Study).GetInfoMouse().Position.X;
93.     Info._int[1] = (*Study).GetInfoMouse().Position.Y;
94.     m_Buff[m_posBuff + 2] = Info.dValue;
95.     Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
96.     m_Buff[m_posBuff + 3] = Info.dValue;
97. }
98. //+------------------------------------------------------------------+

Código fuente del Indicador Mouse

Atención: Aunque puedas pensar que no existen diferencias entre este código y el que se vio en el pasado. Esto no es un hecho. Existen diferencias. Y debes tener el máximo cuidado con ellas.

Para el usuario, la principal diferencia está en la línea 17. Quiero enfatizar que esta es quizás la línea más complicada de todo el código. Lo ideal sería que el usuario NUNCA pudiera de hecho manipular, o ver esta línea en la interfaz del indicador. Esto se debe a que todo usuario principiante se sentirá tentado a modificarla. Ya que se trata de un input. Sin embargo, tú, que estás leyendo este artículo, sabrás que NUNCA, pero NUNCA debes cambiar este valor. Este valor debe ser ajustado, o por el programa que llama al indicador, o por el propio indicador. Pero tú, usuario, -repito- NUNCA debes cambiar este valor. Los demás valores pueden ser ajustados y manipulados por el usuario sin problemas, pero el valor de ID NUNCA.

Muy bien, además de este hecho, lo siguiente que llama la atención es el contenido de la línea 30. Allí inicializamos el puntero de la clase de estudios. Antes, el constructor recibía 3 valores, que básicamente eran los colores a ser utilizados en el indicador de mouse. Pero ahora el constructor recibirá dos valores extras. El primer valor es el ID del gráfico, y el segundo valor es el nombre del indicador. Este nombre será usado para acceder al buffer del indicador después. Por tanto, siempre que el indicador de mouse esté en el gráfico y quieras leer su buffer, utiliza el nombre indicado aquí.

Existen algunas otras pocas diferencias en el código, pero como no son complicadas de entender, no entraré en detalles. Entonces, podemos pasar al código de la clase C_Study. Se muestra completo a continuación, y el motivo es que no utilizaremos un puntero para acceder a la clase C_Terminal. Usaremos la herencia.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix "MouseExpansion_"
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.            }m_Info;
024. //+------------------------------------------------------------------+
025.            const datetime GetBarTime(void)
026.                    {
027.                            datetime dt;
028.                            u_Interprocess info;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if (!GlobalVariableGet(def_GlobalVariableServerTime, info.df_Value)) return ULONG_MAX;
034.                                    if ((dt = info.ServerTime) == 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.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 18);
047.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1);
048.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1);
049.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
050.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
051.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
052.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
053.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
054.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                    }
057. //+------------------------------------------------------------------+
058.    public  :
059. //+------------------------------------------------------------------+
060.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
061.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
062.                    {
063.                            if (_LastError != ERR_SUCCESS) return;
064.                            ZeroMemory(m_Info);
065.                            m_Info.Status = eCloseMarket;
066.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
067.                            m_Info.corP = corP;
068.                            m_Info.corN = corN;
069.                            CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
070.                            CreateObjectInfo(2, 53, def_ExpansionBtn2);
071.                            CreateObjectInfo(58, 53, def_ExpansionBtn3);
072.                    }
073. //+------------------------------------------------------------------+
074.            ~C_Study()
075.                    {
076.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_ExpansionPrefix);
077.                    }
078. //+------------------------------------------------------------------+
079.            void Update(const eStatusMarket arg)
080.                    {
081.                            datetime dt;
082.                            
083.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
084.                            {
085.                                    case eCloseMarket : m_Info.szInfo = "Closed Market";
086.                                            break;
087.                                    case eInReplay    :
088.                                    case eInTrading   :
089.                                            if ((dt = GetBarTime()) < ULONG_MAX)
090.                                            {
091.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
092.                                                    break;
093.                                            }
094.                                    case eAuction     : m_Info.szInfo = "Auction";
095.                                            break;
096.                                    default           : m_Info.szInfo = "ERROR";
097.                            }
098.                            Draw();
099.                    }
100. //+------------------------------------------------------------------+
101. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
102.                    {
103.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
104.                            if (id == CHARTEVENT_MOUSE_MOVE) Draw();
105.                    }
106. //+------------------------------------------------------------------+
107. };
108. //+------------------------------------------------------------------+
109. #undef def_ExpansionBtn3
110. #undef def_ExpansionBtn2
111. #undef def_ExpansionBtn1
112. #undef def_ExpansionPrefix
113. //+------------------------------------------------------------------+

Código fuente de la clase C_Study

Básicamente, apenas hay una diferencia entre este código y el antiguo. La diferencia estriba en que estamos buscando el ID del gráfico directamente en la clase C_Terminal. Antes utilizábamos un puntero, pero ahora usamos la herencia. ¿Pero de dónde viene esta herencia? Esta proviene de la clase C_Mouse. Entonces vamos a ver el código de la clase C_Mouse para entender mejor esta historia de herencia.

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. class C_Mouse : public C_Terminal
014. {
015.    public  :
016.            enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
017.            enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
018.            struct st_Mouse
019.            {
020.                    struct st00
021.                    {
022.                            int      X,
023.                                     Y;
024.                            double   Price;
025.                            datetime dt;
026.                    }Position;
027.                    uint    ButtonStatus;
028.                    bool    ExecStudy;
029.            };
030. //+------------------------------------------------------------------+
031.    protected:
032.            enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
033. //+------------------------------------------------------------------+
034.            void CreateObjectInfo(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. //+------------------------------------------------------------------+
069.            void GetDimensionText(const string szArg, int &w, int &h)
070.                    {
071.                            TextSetFont("Lucida Console", -100, FW_NORMAL);
072.                            TextGetSize(szArg, w, h);
073.                            h += 5;
074.                            w += 5;
075.                    }
076. //+------------------------------------------------------------------+
077.            void CreateStudy(void)
078.                    {
079.                            if (m_Mem.IsFull)
080.                            {
081.                                    CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
082.                                    CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
083.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
084.                                    CreateObjectInfo(0, 0, def_NameObjectStudy);
085.                            }
086.                            m_Info.Study = eStudyCreate;
087.                    }
088. //+------------------------------------------------------------------+
089.            void ExecuteStudy(const double memPrice)
090.                    {
091.                            double v1 = GetInfoMouse().Position.Price - memPrice;
092.                            int w, h;
093.                            
094.                            if (!CheckClick(eClickLeft))
095.                            {
096.                                    m_Info.Study = eStudyNull;
097.                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
098.                                    if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
099.                            }else if (m_Mem.IsFull)
100.                            {
101.                                    string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
102.                                            MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 /memPrice) * 100.0)));
103.                                    GetDimensionText(sz1, w, h);
104.                                    ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1);                                                                                                                          
105.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
106.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w);
107.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h);
108.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X - w);
109.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - (v1 < 0 ? 1 : h));                           
110.                                    ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
111.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
112.                            }
113.                            m_Info.Data.ButtonStatus = eKeyNull;
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Mouse(const long id, const string szShortName)
119.                    :C_Terminal(id)
120.                    {
121.                            m_Mem.szShortName = szShortName;
122.                    }
123. //+------------------------------------------------------------------+
124.            C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
125.                    :C_Terminal(id)
126.                    {
127.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
128.                            m_Mem.szShortName = NULL;
129.                            if (_LastError != ERR_SUCCESS) return;
130.                            m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
131.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
132.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
133.                            ZeroMemory(m_Info);
134.                            m_Info.corLineH  = corH;
135.                            m_Info.corTrendP = corP;
136.                            m_Info.corTrendN = corN;
137.                            m_Info.Study = eStudyNull;
138.                            if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
139.                                    CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
140.                    }
141. //+------------------------------------------------------------------+
142.            ~C_Mouse()
143.                    {
144.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false);
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
146.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
147.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
148.                    }
149. //+------------------------------------------------------------------+
150. inline bool CheckClick(const eBtnMouse value) 
151.                    {
152.                            return (GetInfoMouse().ButtonStatus & value) == value;
153.                    }
154. //+------------------------------------------------------------------+
155. inline const st_Mouse GetInfoMouse(void)
156.                    {
157.                            if (m_Mem.szShortName != NULL)
158.                            {
159.                                    double Buff[];
160.                                    uCast_Double loc;
161.                                    int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName);
162.                                    
163.                                    ZeroMemory(m_Info.Data);
164.                                    if (CopyBuffer(handle, 0, 0, 4, Buff) == 4)
165.                                    {
166.                                            m_Info.Data.Position.Price = Buff[0];
167.                                            loc.dValue = Buff[1];
168.                                            m_Info.Data.Position.dt = loc._datetime;
169.                                            loc.dValue = Buff[2];
170.                                            m_Info.Data.Position.X = loc._int[0];
171.                                            m_Info.Data.Position.Y = loc._int[1];
172.                                            loc.dValue = Buff[3];
173.                                            m_Info.Data.ButtonStatus = loc._char[0];
174.                                            IndicatorRelease(handle);
175.                                    }
176.                            }
177. 
178.                            return m_Info.Data;
179.                    }
180. //+------------------------------------------------------------------+
181. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
182.                    {
183.                            int w = 0;
184.                            static double memPrice = 0;
185.                            
186.                            if (m_Mem.szShortName == NULL) switch (id)
187.                            {
188.                                    case (CHARTEVENT_CUSTOM + ev_HideMouse):
189.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
190.                                            break;
191.                                    case (CHARTEVENT_CUSTOM + ev_ShowMouse):
192.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
193.                                            break;
194.                                    case CHARTEVENT_MOUSE_MOVE:
195.                                            ChartXYToTimePrice(GetInfoTerminal().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);
196.                                            if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
197.                                            m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt);
198.                                            ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X, m_Info.Data.Position.Y);
199.                                            if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
200.                                            m_Info.Data.ButtonStatus = (uint) sparam;
201.                                            if (CheckClick(eClickMiddle))
202.                                                    if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
203.                                            if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
204.                                            {
205.                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
206.                                                    if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
207.                                                    m_Info.Study = eStudyExecute;
208.                                            }
209.                                            if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
210.                                            m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
211.                                            break;
212.                                    case CHARTEVENT_OBJECT_DELETE:
213.                                            if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
214.                                            break;
215.                            }
216.                    }
217. //+------------------------------------------------------------------+
218. };
219. //+------------------------------------------------------------------+
220. #undef def_MousePrefixName
221. #undef def_NameObjectLineV
222. #undef def_NameObjectLineH
223. #undef def_NameObjectLineT
224. #undef def_NameObjectStudy
225. //+------------------------------------------------------------------+

Código fuente de la clase C_Mouse

Del mismo modo que venía ocurriendo, la gran diferencia entre este código y el antiguo es que ya no se utiliza un puntero para acceder a la clase C_Terminal. El responsable de esto está en la línea 13, donde hacemos la herencia de la clase C_Terminal para la clase C_Mouse. De esta forma, todos los procedimientos y funciones públicas de la clase C_Terminal, pasan a extender la clase C_Mouse. Todo el código básicamente ya fue explicado en los artículos sobre el Mouse. 

El último artículo sobre el asunto fue Desarrollo de un sistema de repetición (Parte 31): Proyecto Expert Advisor — Clase C_Mouse (V), puedes usar este artículo para llegar al primero sobre este indicador de mouse, ya que hay un enlace al principio del artículo, como en todos mis artículos, para que puedas volver y seguir la evolución del desarrollo del sistema.

Si tienes alguna duda de cómo el indicador de mouse funciona, o deseas adaptarlo a tus necesidades personales; vuelve a ese punto, en el que expliqué cómo se desarrolló el indicador. Intenta entender cómo funciona y, luego, regresa a este punto y añade las modificaciones que se están mostrando. Y, por supuesto, junto con las cosas que necesitas y te interesa colocar en el indicador de mouse. Si haces todo correctamente y con calma, podrás usar tu propio indicador de mouse, en este sistema de repetición/simulador que estoy mostrando cómo desarrollar.

Ahí va el consejo para quien de hecho desea aprender a programar. Intenta añadir alguna nueva funcionalidad al indicador de mouse y usa esto en el sistema de repetición/simulador. Pero no te olvides de indicar la fuente de tu conocimiento. Esto me traería mucha satisfacción. Además de motivarme, me gusta mostrar cómo consigo resolver problemas que muchos consideran insuperables.

Pero volvamos a nuestra explicación del código. En el constructor de la clase C_Mouse, puedes notar que en la línea 125, hacemos la inicialización de la clase C_Terminal. El valor que se debe utilizar en la inicialización se informa en el código del indicador, en un input denominado user00. De acuerdo, si el usuario deja este input en paz, sin tocarlo, la clase C_Terminal determinará cuál es el ID del gráfico, y pasará a usar este ID, donde sea necesario para poder colocar los objetos en el gráfico correcto.

Además de este hecho, en la línea 127 se bloquea el indicador para evitar que se coloque más de una vez en el gráfico. De este modo, se evita que el usuario coloque otro indicador de mouse en el gráfico, ya que el usuario tendrá acceso a este indicador. Él estará presente en la lista de indicadores del MetaTrader 5.

Todo el resto del código funciona de la misma manera que antes. Antes de que empieces a preguntar algunas cosas al mirar el código de los próximos módulos: Este indicador de mouse será el encargado de decirle a MetaTrader 5 que active eventos de mouse y los envíe al gráfico donde se encuentra el indicador. Atención a esto: Deberás tener el indicador de mouse en el gráfico para recibir eventos de mouse de MetaTrader 5; no sirve intentar acceder al indicador desde otro gráfico. Aunque tengas la posibilidad de leer lo que el indicador de mouse en otro gráfico está haciendo, MetaTrader 5 no generará eventos de mouse para el gráfico que no contenga tal indicador.

La línea 131 es la responsable de hacer esto. Por tanto, cualquier EA u otro indicador recibirá eventos de mouse siempre que el indicador de mouse esté en el gráfico. Y esto se mantendrá mientras el indicador de mouse esté en el gráfico. Recuerda este hecho porque los próximos códigos necesitarán que el indicador de mouse esté presente en el gráfico para que los eventos relacionados con el mouse sean disparados por MetaTrader 5 hacia los demás códigos, que los tratarán de manera específica.

Antes de pasar a la etapa de modificar el indicador de control, creo que es válido dar una explicación un poco mejor de lo que realmente está sucediendo. Así se podrá asimilar correctamente todo el contenido de este artículo.


Comprensión y asimilación de conocimientos

Puede que pienses que todo lo que digo en este artículo es una tontería, ya que muchos deben tener una vasta experiencia en MQL5 o conocen a un amigo que sea experto en este sistema de codificación. Pero para dejar las cosas bien claras, en el anexo de este artículo publicaré el ejecutable del indicador de mouse. Esto es para que sea más sencillo; sin embargo, puedes usar el contenido de esta serie y obtendrás el mismo código, ya que estoy colocando el código completo dentro de los artículos. Así podrás probar las cosas y ver lo que sucede al usar el siguiente código, que es considerablemente más simple que el sistema de repetición/simulación, pero que funciona siguiendo los mismos principios.

01. //+------------------------------------------------------------------+
02. #property service
03. #property copyright "Daniel Jose"
04. #property version   "1.00"
05. //+------------------------------------------------------------------+
06. input string user00 = "EURUSD"; // Symbol
07. //+------------------------------------------------------------------+
08. void OnStart()
09. {
10.    long id;
11.    int handle;
12.    
13.    SymbolSelect(user00, true);
14.    id = ChartOpen(user00, PERIOD_M5);
15.    handle = iCustom(ChartSymbol(id), ChartPeriod(id), "\\Indicators\\Replay\\Mouse Study.ex5", id);
16.    ChartIndicatorAdd(id, 0, handle);
17.    IndicatorRelease(handle);
18. 
19.    Print("ID: ", id);
20.     
21.    while ((!_StopFlag) && (ChartSymbol(id) == user00)) Sleep(300);
22.     
23.    ChartClose(id);
24.    SymbolSelect(user00, false);    
25. }
26. //+------------------------------------------------------------------+

Código fuente del servicio de prueba

En la línea 06, damos la posibilidad al usuario de indicar un activo. Este será usado para abrir el gráfico. Pero para abrir el gráfico, el símbolo deberá estar en la ventana 'Market Watch'. Para hacer esto, usamos la línea 13. En la línea 14, pedimos a MetaTrader 5 que abra el gráfico para nosotros. En la línea 15, creamos un handle que añadirá un indicador en el gráfico; en este caso, haremos que MetaTrader 5 cargue el indicador de mouse. En la línea 16, añadimos el indicador en el gráfico. En la línea 17, liberamos el handle, ya que no lo necesitamos más. En la línea 19 imprimimos en el terminal un mensaje que informa la ID del gráfico que fue abierto por el servicio. Ahora en la línea 21 esperamos que el usuario cierre el gráfico o termine el servicio.

Si el usuario cierra el gráfico, la línea 23 no hará nada. Sin embargo, si el usuario finaliza el servicio, la línea 23 cerrará el gráfico que fue abierto antes. La línea 24 elimina el activo de la observación de mercado. Un detalle: Para que el activo sea eliminado, no debe tener ningún tipo de elemento asociado a él. En este caso, es solo para pruebas, así que no tendrá nada relacionado con él.

Podrías pensar que este código no tiene mucho sentido. ¿Por qué me tomaría la molestia de construir algo así y tratar de explicar el funcionamiento de las cosas? Pues bien, justamente eso es lo que todos deben hacer. Buscar explorar más allá de los límites que muchos exploran cómodamente. Solo así realmente comprenderás cómo funcionan las cosas.

Para entender la idea y esencia de lo que quiero mostrar, ya que esto es de suma importancia para los próximos artículos. Puedes ver el video 01 y observar cómo se realizaron las pruebas, y probar tú mismo, usando tus propios criterios. Pero debo resaltar: No aceptes la verdad de alguien solo porque se cree mejor o más capaz. Prueba tú mismo, cuestiona la autoridad, pues solo así el verdadero conocimiento saldrá a la luz.


Video 01


Conclusión

Este artículo ha sido uno de los más difíciles de crear hasta ahora. Esto se debe al nivel de complejidad de la información que se está transmitiendo. Aunque la programación en sí misma me parece bastante simple. Explicar para ti, querido lector, lo que sucede es algo muy complicado. El motivo es que puede haber personas con un nivel de conocimiento mucho mayor, pero también personas y entusiastas que están comenzando a intentar comprender este mundo llamado: PROGRAMACIÓN.

En el próximo artículo, mostraré cómo generar la interacción entre el indicador de mouse, el indicador de control, el sistema de repetición/simulación y tú, querido usuario del sistema. Hasta pronto.

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

Archivos adjuntos |
Anexo.zip (420.65 KB)
Desarrollo de un sistema de repetición (Parte 52): Esto complica las cosas (IV) Desarrollo de un sistema de repetición (Parte 52): Esto complica las cosas (IV)
En este artículo vamos a cambiar el indicador de mouse para poder interactuar con el indicador de control, ya que esta se está realizando de forma errática.
Redes neuronales: así de sencillo (Parte 72): Predicción de trayectorias en entornos ruidosos Redes neuronales: así de sencillo (Parte 72): Predicción de trayectorias en entornos ruidosos
La calidad de las predicciones de los estados futuros desempeña un papel importante en el método Goal-Conditioned Predictive Coding, del que hablamos en el artículo anterior. En este artículo quiero presentarte un algoritmo que puede mejorar significativamente la calidad de la predicción en entornos estocásticos, como los mercados financieros.
Desarrollamos de un asesor multidivisa (Parte 2): Transición a posiciones virtuales de estrategias comerciales Desarrollamos de un asesor multidivisa (Parte 2): Transición a posiciones virtuales de estrategias comerciales
Hoy continuaremos con el desarrollo de un asesor multidivisa con varias estrategias funcionando en paralelo. Intentaremos transferir todo el trabajo relacionado con la apertura de posiciones de mercado desde el nivel de las estrategias al nivel de un experto que gestiona estas. Las propias estrategias solo negociarán virtualmente, sin abrir posiciones de mercado.
Asesor Experto Grid-Hedge Modificado en MQL5 (Parte II): Creación de un EA de cuadrícula simple Asesor Experto Grid-Hedge Modificado en MQL5 (Parte II): Creación de un EA de cuadrícula simple
En este artículo, exploramos la estrategia de cuadrícula (grid) clásica, detallando su automatización mediante un Asesor Experto (EA) en MQL5 y analizando los resultados iniciales del backtest. Destacamos la necesidad de que la estrategia tenga una gran capacidad de retención y esbozamos planes para optimizar parámetros clave como la distancia, el takeProfit y el tamaño de los lotes en futuras entregas. La serie pretende mejorar la eficacia de las estrategias de negociación y su adaptabilidad a las distintas condiciones del mercado.