Português
preview
Simulación de mercado: La unión hace la fuerza (I)

Simulación de mercado: La unión hace la fuerza (I)

MetaTrader 5Probador |
85 0
Daniel Jose
Daniel Jose

Introducción

Hola a todos y bienvenidos a un nuevo artículo de la serie dedicada a la construcción de un sistema de repetición/simulación.

En el artículo anterior, Simulación de mercado: Position View (XX), mostré cómo modificar el indicador de posición para crear una sombra en el nivel en el que se encontraba el precio. Sin embargo, aquella implementación presenta un pequeño fallo. El fallo se produce cuando, mientras desplazas el take profit o el stop loss, decides cambiar el modo de visualizar el resultado de la operación, por ejemplo, del resultado monetario al porcentaje. Aunque algunos operadores pueden querer realizar este cambio durante el desplazamiento, no es posible. Primero es necesario finalizar el movimiento y, solo entonces, cambiar el modo de visualización de los datos. Después habrá que iniciar un nuevo desplazamiento para que la información se presente de la forma esperada.

Aunque resolver este problema sería bastante sencillo, personalmente no considero necesario resolverlo ni mostrar cómo hacerlo. Creo que quienes siguen esta serie ya estarán deseando ver este sistema terminado. Por este motivo, en este artículo se decidió pasar directamente a la parte en la que integraremos las cuatro aplicaciones en el sistema de repetición/simulador, pues ya cuentan con el desarrollo suficiente para utilizarlas en el sistema de repetición/simulador.

Evidentemente, todavía falta desarrollar el sistema de órdenes pendientes. Sin embargo, como este sistema utiliza ampliamente el indicador de posición y solo requiere algunos cambios en el indicador, crearemos el sistema de órdenes pendientes directamente en el sistema de repetición/simulador.

Una vez tomada esta decisión, en este artículo realizaremos las modificaciones correspondientes para comenzar a utilizar el indicador de posición en el sistema de repetición/simulador, al menos en su forma más básica. Así que pongámonos manos a la obra y comencemos a implementar los cambios.


Comprender lo que tenemos hasta ahora

Para empezar, quiero que observes cómo ha quedado el código de la clase C_ElementsTrade después de eliminar el código duplicado que aparecía en el artículo anterior. Puedes ver el código completo a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_NameHLine      m_Info.szPrefixName + "#HLINE"
005. #define def_NameBtnClose   m_Info.szPrefixName + "#CLOSE"
006. #define def_NameBtnMove    m_Info.szPrefixName + "#MOVE"
007. #define def_NameInfoDirect m_Info.szPrefixName + "#DIRECT"
008. #define def_NameObjLabel   m_Info.szPrefixName + "#PROFIT"
009. #define def_NameBackGround m_Info.szPrefixName + "#BACKGROUND"
010. #define def_NameVolume     m_Info.szPrefixName + "#VOLUME"
011. //+------------------------------------------------------------------+
012. #define def_GhostColor     clrDarkGray
013. #define macro_GhostName(A) (m_Info.szPrefixName + "[GHOST]" + (A != "" ? StringSubstr(A, StringFind(A, "#")) : ""))
014. //+------------------------------------------------------------------+
015. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1));
016. //+------------------------------------------------------------------+
017. #define def_PathBtns "Images\\Market Replay\\Orders\\"
018. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
019. #resource "\\" + def_Btn_Close;
020. //+------------------------------------------------------------------+
021. #include "..\Auxiliar\C_Mouse.mqh"
022. //+------------------------------------------------------------------+
023. #ifdef def_FontName
024.     "Why are you trying to do this?"
025. #else 
026.     #define def_FontName "Lucida Console"
027.     #define def_FontSize 10
028. #endif 
029. //+------------------------------------------------------------------+
030. class C_ElementsTrade : private C_Mouse
031. {
032.     private    :
033. //+------------------------------------------------------------------+
034.         struct stInfos
035.         {
036.             struct st_01
037.             {
038.                 short   Width,
039.                         Height,
040.                         digits;
041.             }Text;
042.             ulong       ticket;
043.             string      szPrefixName,
044.                         szDescr,
045.                         szSymbol,
046.                         szMsgGhost;
047.             EnumEvents  ev;
048.             double      price,
049.                         open,
050.                         volume,
051.                         var,
052.                         tickSize,
053.                         tpsl,
054.                         limit,
055.                         priceGhost;
056.             bool        bClick,
057.                         bIsBuy;
058.             char        weight;
059.             color       _color;
060.             int         sizeText;
061.             enum e1 {eValue, eFinance, eTicks, ePercentage} ViewMode;
062.         }m_Info;
063. //+------------------------------------------------------------------+
064.         short UpdateViewPort(const double price, short size = 0, uint ui = 0)
065.         {
066. #define macro_Local(A) {                                                                                                                                                                                                    \
067.             ChartTimePriceToXY(0, 0, 0, (A ? price : m_Info.priceGhost), x, y);                                                                                                                                \
068.             x = 125 + (m_Info.ev == evMsgClosePositionEA ? 0 : _Width + (m_Info.ev == evMsgCloseTakeProfit ? _SizeControls : (_SizeControls * 2)));                             \
069.             ObjectSetInteger(0, (A ? def_NameHLine : macro_GhostName(def_NameHLine)), OBJPROP_XDISTANCE, x);                                                                    \
070.             ObjectSetInteger(0, (A ? def_NameHLine : macro_GhostName(def_NameHLine)), OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0));               \
071.             ObjectSetInteger(0, (A ? def_NameObjLabel : macro_GhostName(def_NameObjLabel)), OBJPROP_XDISTANCE, x + 10 + (m_Info.ev == evMsgClosePositionEA ? _Width + 2 : 0));  \
072.             ObjectSetInteger(0, (A ? def_NameObjLabel : macro_GhostName(def_NameObjLabel)), OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2));                                   \
073.             ObjectSetInteger(0, (A ? def_NameBtnMove : macro_GhostName(def_NameBtnMove)), OBJPROP_XDISTANCE, x + _Width + 20);                                                  \
074.             ObjectSetInteger(0, (A ? def_NameBtnMove : macro_GhostName(def_NameBtnMove)), OBJPROP_YDISTANCE, y);                                                                \
075.             ObjectSetInteger(0, (A ? def_NameBackGround : macro_GhostName(def_NameBackGround)), OBJPROP_XDISTANCE, x - 10);                                                     \
076.             ObjectSetInteger(0, (A ? def_NameBackGround : macro_GhostName(def_NameBackGround)), OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); }        
077. 
078.             static short _SizeControls;
079.             static short _Width;
080.             uint x, y;
081.             
082.             if (size > 0)
083.             {
084.                 size += (short)(ui + 8);
085.                 _SizeControls = (_SizeControls > size ? _SizeControls : size);
086.                 size = (short)(_SizeControls - ui - 12);
087.                 _Width = (_Width > size ? _Width : size);
088.             }else
089.             {
090.                 macro_Local(true);
091.                 ObjectSetInteger(0, def_NameVolume, OBJPROP_XDISTANCE, x + 10);
092.                 ObjectSetInteger(0, def_NameVolume, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2));
093.                 ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x);
094.                 ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y);
095.                 ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + (_Width * 2) + 20);
096.                 ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y);
097.                 if (m_Info.szMsgGhost != "")
098.                     macro_Local(false)
099.             }
100.             return _Width;
101. #undef macro_Local
102.         }
103. //+------------------------------------------------------------------+
104. inline void CreateLinePrice(void)
105.         {
106. #define macro_Local(A)                                                                                              \
107.             CreateObjectGraphics(szObj = A, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault));  \
108.             ObjectSetInteger(0, A, OBJPROP_BGCOLOR, m_Info._color);                                                 \
109.             ObjectSetInteger(0, A, OBJPROP_BORDER_TYPE, BORDER_FLAT);                                               \
110.             ObjectSetInteger(0, A, OBJPROP_CORNER, CORNER_LEFT_UPPER);
111. 
112.             string szObj;
113.             
114.             macro_Local(def_NameHLine);
115.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH));
116.             ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr);
117.             macro_LineInFocus(false);
118.             macro_Local(def_NameBackGround);
119. #undef macro_Local            
120.         }
121. //+------------------------------------------------------------------+
122. inline void CreateBoxInfo(const bool bMove)
123.         {
124.             string szObj;
125.             const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0};
126.             
127.             CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
128.             ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings");
129.             ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c));
130.             ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? (m_Info.ev == evMsgCloseTakeProfit ? clrDarkGreen : clrMaroon) : (m_Info.bIsBuy ? clrDarkGreen : clrMaroon)));
131.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15));
132.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
133.         }
134. //+------------------------------------------------------------------+
135. inline void CreateObjectInfoText(const string szObj, const color _color)
136.         {
137.             CreateObjectGraphics(szObj, OBJ_EDIT, clrNONE, (EnumPriority)(ePriorityDefault));
138.             ObjectSetString(0, szObj, OBJPROP_FONT, def_FontName);
139.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, def_FontSize);
140.             ObjectSetInteger(0, szObj,    OBJPROP_COLOR, clrBlack);
141.             ObjectSetInteger(0, szObj, OBJPROP_BORDER_COLOR, _color);
142.             ObjectSetInteger(0, szObj, OBJPROP_ALIGN, ALIGN_CENTER);
143.             ObjectSetInteger(0, szObj, OBJPROP_READONLY, true);
144.         }
145. //+------------------------------------------------------------------+
146. inline void CreateButtonClose(void)
147.         {
148.             string szObj;
149.             
150.             CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
151.             ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
152.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
153.         }
154. //+------------------------------------------------------------------+
155. inline void AdjustDinamic(const string szObj, const string szTxt)
156.         {
157. #define macro_Local(A)                                                                                                                      \
158.             ObjectSetInteger(0, A, OBJPROP_XSIZE, m_Info.Text.Width + h + (m_Info.ev == evMsgClosePositionEA ? m_Info.Text.Width + 8 : 0)); \
159.             ObjectSetInteger(0, A, OBJPROP_YSIZE, m_Info.Text.Height + 5);
160. 
161.             uint w, h;
162. 
163.             TextSetFont(def_FontName, def_FontSize * -10);
164.             TextGetSize(szTxt, w, h);
165.             m_Info.Text.Height = (uchar) h + 4;
166.             m_Info.Text.Width = (uchar) w + 4;
167.             m_Info.Text.Width = UpdateViewPort(0, m_Info.Text.Width, h = 32);
168.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, m_Info.Text.Width);
169.             ObjectSetInteger(0, szObj, OBJPROP_YSIZE, m_Info.Text.Height);
170.             macro_Local(def_NameBackGround);
171.             if (m_Info.szMsgGhost != "")
172.             {
173.                 macro_Local(macro_GhostName(def_NameBackGround));
174.                 ObjectSetString(0, macro_GhostName(def_NameObjLabel), OBJPROP_TEXT, m_Info.szMsgGhost);
175.             }
176. #undef macro_Local
177.         }
178. //+------------------------------------------------------------------+
179. inline void ChartChange(void)
180.         {
181.             UpdateViewPort(MathAbs(m_Info.price));
182.             m_Info.limit = (m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price);
183.             if (m_Info.ev != evMsgClosePositionEA)
184.                 ViewValue(m_Info.limit);
185.         }
186. //+------------------------------------------------------------------+
187.         void CreateGhost(void)
188.         {
189.             ObjectSetInteger(0, def_NameHLine, OBJPROP_BGCOLOR, def_GhostColor);
190.             ObjectSetInteger(0, def_NameHLine, OBJPROP_COLOR, def_GhostColor);
191.             ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, def_GhostColor);
192.             ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BORDER_COLOR, def_GhostColor);
193.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_COLOR, def_GhostColor);
194.             ObjectSetInteger(0, def_NameBackGround, OBJPROP_BGCOLOR, def_GhostColor);
195.             ObjectSetInteger(0, def_NameBackGround, OBJPROP_COLOR, def_GhostColor);            
196.             ObjectSetString(0, def_NameHLine, OBJPROP_NAME, macro_GhostName(def_NameHLine));
197.             ObjectSetString(0, def_NameBtnMove, OBJPROP_NAME, macro_GhostName(def_NameBtnMove));
198.             ObjectSetString(0, def_NameObjLabel, OBJPROP_NAME, macro_GhostName(def_NameObjLabel));
199.             ObjectSetString(0, def_NameBackGround, OBJPROP_NAME, macro_GhostName(def_NameBackGround));
200.             m_Info.priceGhost = MathAbs(m_Info.price);
201.             m_Info.szMsgGhost = "";
202.         }
203. //+------------------------------------------------------------------+
204.     public    :
205. //+------------------------------------------------------------------+
206.         C_ElementsTrade(const ulong ticket, string szSymbol, const EnumEvents ev, color _color, char digits, double ticksize, string szDescr = "\n", const bool IsBuy = true)
207.             :C_Mouse(0, "")
208.         {
209.             ZeroMemory(m_Info);
210.             m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev));
211.             m_Info._color = _color;
212.             m_Info.szDescr = szDescr;
213.             m_Info.bIsBuy = IsBuy;
214.             m_Info.Text.digits = digits;
215.             m_Info.tickSize = ticksize;
216.             m_Info.szSymbol = szSymbol;
217.         }
218. //+------------------------------------------------------------------+
219.         ~C_ElementsTrade()
220.         {
221.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
222.             ObjectsDeleteAll(0, m_Info.szPrefixName);
223.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
224.         }
225. //+------------------------------------------------------------------+
226. inline void UpdatePrice(const double open, const double price, const double vol = 0, const double var = 0, const double special = -1)
227.         {
228.             m_Info.sizeText = 0;
229.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
230.             ObjectsDeleteAll(0, m_Info.szPrefixName + "#");
231.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
232.             m_Info.volume = (vol > 0 ? vol : m_Info.volume);
233.             m_Info.var = (var > 0 ? var : m_Info.var);
234.             m_Info.tpsl = (special >= 0 ? special : m_Info.tpsl);
235.             if (price > 0)
236.             {
237.                 CreateLinePrice();
238.                 CreateButtonClose();
239.                 CreateObjectInfoText(def_NameObjLabel, m_Info._color);
240.             }
241.             CreateBoxInfo(m_Info.ev != evMsgClosePositionEA);
242.             m_Info.open = open;
243.             m_Info.price = (price > 0 ? price : -open);
244.             if (m_Info.ev == evMsgClosePositionEA)
245.             {
246.                 CreateObjectInfoText(def_NameVolume, clrBlack);
247.                 ObjectSetInteger(0, def_NameVolume, OBJPROP_BGCOLOR, clrViolet);                
248.                 ObjectSetString(0, def_NameVolume, OBJPROP_TEXT, DoubleToString(m_Info.volume, (MathRound(m_Info.volume) != m_Info.volume ? 2 : 0)));
249.                 AdjustDinamic(def_NameVolume, "88888" + (MathRound(m_Info.volume) != m_Info.volume ? ".88" : ""));
250.             };
251.             ChartChange();
252.         }
253. //+------------------------------------------------------------------+
254.         void ViewValue(const double profit, const bool Enabled = true)
255.         {
256.             string szTxt;
257.             color  _cor;
258.             static double memSL, memTP;
259.             
260.             if (Enabled)
261.             {
262.                 switch (m_Info.ViewMode)
263.                 {
264.                     case stInfos::eValue:
265.                         szTxt = StringFormat("%." + (string)m_Info.Text.digits + "f", MathAbs(profit));
266.                         break;
267.                     case stInfos::eFinance:
268.                         szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f", (MathAbs(profit) / m_Info.var) * m_Info.volume);
269.                         break;
270.                     case stInfos::eTicks:
271.                         szTxt = StringFormat("%d", (uint)MathRound(MathAbs(profit) / m_Info.tickSize));
272.                         break;
273.                     case stInfos::ePercentage:
274.                         szTxt = StringFormat("%.2f%%", NormalizeDouble((MathAbs(profit) / (m_Info.open ? m_Info.open : m_Info.price)) * 100, 2));
275.                         break;
276.                 }
277.                 m_Info.szMsgGhost = ((m_Info.szMsgGhost == "") && (m_Info.ev != evMsgClosePositionEA) ? szTxt : m_Info.szMsgGhost);
278.                 ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt);
279.                 if (StringLen(szTxt) != m_Info.sizeText)
280.                 {
281.                     AdjustDinamic(def_NameObjLabel, szTxt);
282.                     m_Info.sizeText = StringLen(szTxt);
283.                 }
284.             }
285.             _cor = (m_Info.limit >= 0 ? clrPaleGreen : clrCoral);
286.             switch (m_Info.ev)
287.             {
288.                 case evMsgCloseTakeProfit:
289.                     memTP = (Enabled ? memTP : profit);
290.                     _cor = (m_Info.limit < memTP ? clrYellow : _cor);
291.                     break;
292.                 case evMsgCloseStopLoss:
293.                     memSL = (Enabled ? memSL : profit);
294.                     _cor = (m_Info.limit > memSL ? clrYellow : _cor);
295.                     break;
296.                 case evMsgClosePositionEA:
297.                     _cor = (profit >= 0 ? clrPaleGreen : clrCoral);
298.                     break;
299.             }
300.             ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, _cor);
301.         }
302. //+------------------------------------------------------------------+
303.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
304.         {
305.             string sz0;
306.             long _lparam = lparam;
307.             double _dparam = dparam;
308.             
309.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
310.             switch (id)
311.             {
312.                 case (CHARTEVENT_KEYDOWN):
313.                     if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break;
314.                     _lparam = (long) m_Info.ticket;
315.                     _dparam = 0;
316.                     EventChartCustom(0, evUpdate_Position, _lparam, 0, "");
317.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
318.                     if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev))
319.                     {
320.                         CreateGhost();
321.                         UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price);
322.                     }else
323.                     {
324.                         ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
325.                         ObjectsDeleteAll(0, macro_GhostName(""));
326.                         ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
327.                     }
328.                     macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev));
329.                     EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, "");
330.                     m_Info.bClick = false;
331.                 case CHARTEVENT_CHART_CHANGE:
332.                     ChartChange();
333.                     break;
334.                 case CHARTEVENT_CUSTOM + evMsgSwapViewModePosition:
335.                     m_Info.ViewMode = (stInfos::e1)((((stInfos::e1)_dparam) + 1) & 0x03);
336.                     EventChartCustom(0, evUpdate_Position, m_Info.ticket, 0, NULL);
337.                     break;
338.                 case CHARTEVENT_OBJECT_CLICK:
339.                     sz0 = GetPositionsMouse().szObjNameClick;
340.                     if (m_Info.bClick)
341.                     {
342.                         if (sz0 == def_NameBtnMove)
343.                             EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, "");
344.                         if (sz0 == def_NameBtnClose)
345.                             EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, m_Info.tpsl, m_Info.szSymbol);
346.                         if (sz0 == def_NameObjLabel)
347.                             EventChartCustom(0, evMsgSwapViewModePosition, m_Info.ticket, (double)m_Info.ViewMode, NULL);
348.                     }
349.                     m_Info.bClick = false;
350.                     break;
351.                 case CHARTEVENT_MOUSE_MOVE:
352.                     m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick);
353.                     if (m_Info.weight > 1)
354.                     {
355.                         UpdateViewPort(_dparam = GetPositionsMouse().Position.Price);
356.                         ViewValue(m_Info.limit = (m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam));
357.                         if (m_Info.bClick)
358.                         {
359.                             if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss))
360.                                 EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, m_Info.szSymbol);
361.                             EventChartCustom(0, evMsgSetFocus, 0, 0, "");
362.                         }
363.                     }
364.                     break;
365.                 case CHARTEVENT_OBJECT_DELETE:
366.                     if (StringFind(sparam, m_Info.szPrefixName) < 0) break;
367.                     UpdatePrice(m_Info.open, m_Info.price);
368.                     break;
369.             }
370.         }
371. //+------------------------------------------------------------------+
372. };
373. //+------------------------------------------------------------------+
374. #undef def_GhostColor
375. #undef macro_GhostName
376. //+------------------------------------------------------------------+
377. #undef macro_LineInFocus
378. //+------------------------------------------------------------------+
379. #undef def_Btn_Close
380. #undef def_PathBtns
381. #undef def_FontName
382. #undef def_FontSize
383. //+------------------------------------------------------------------+
384. #undef def_NameVolume
385. #undef def_NameBackGround
386. #undef def_NameObjLabel
387. #undef def_NameInfoDirect
388. #undef def_NameBtnMove
389. #undef def_NameBtnClose
390. #undef def_NameHLine
391. //+------------------------------------------------------------------+

C_ElementsTrade

Creo que no tendrás dificultades para comprender este código, ya que corresponde exactamente a lo que hemos visto hasta ahora. Sin embargo, aquí se ha eliminado todo aquel código duplicado y se ha sustituido por macros. Por tanto, aunque para el compilador el código siga estando duplicado, para nosotros, que necesitamos leer y modificar el código, esa duplicación dejará de existir en la práctica. Si cambiamos el código de la macro, solo tendremos que efectuar el cambio una vez.

Para comprender mejor este uso de las macros, observa la línea 66, donde se define una macro. Esta macro es de uso local, por lo que se elimina en la línea 101. Quiero que prestes atención al hecho de que utilizamos la macro en dos puntos: en las líneas 90 y 98. En esos lugares, el compilador toma el código de la macro y lo inserta directamente. Por tanto, desde el punto de vista del compilador, el código sí queda duplicado, pero para nosotros no. Si deseas modificar la posición inicial en la coordenada X, bastará con efectuar esa modificación una sola vez, concretamente en la línea 68. El cambio se reflejará tanto en la sombra como en el segmento que se esté mostrando. Así de sencillo.

Observa también que, como hemos eliminado cualquier dependencia del símbolo o cualquier información relacionada con el símbolo, ya no necesitamos preocuparnos por la clase C_ElementsTrade al portar el indicador al sistema de repetición/simulador. A partir de ahora, centraremos toda la atención en el código principal del indicador, que aparece completo a continuación. Ten en cuenta que se trata de la versión anterior, la misma que vimos hasta el artículo pasado.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Positions.ico"
004. #property description "Indicator for tracking an open position on the server."
005. #property description "This should preferably be used together with an Expert Advisor."
006. #property description "For more details see the same article."
007. #property version "1.132"
008. #property link "https://www.mql5.com/pt/articles/13523"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. //+------------------------------------------------------------------+
012. #define def_ShortName "Position View"
013. //+------------------------------------------------------------------+
014. #include <Market Replay\Order System\C_ElementsTrade.mqh>
015. #include <Market Replay\Defines.mqh>
016. //+------------------------------------------------------------------+
017. input ulong user00 = 0;        //For Expert Advisor use
018. //+------------------------------------------------------------------+
019. struct st00
020. {
021.     ulong     ticket;
022.     string    szShortName,
023.               szSymbol;
024.     double    priceOpen,
025.               var,
026.               tickSize;
027.     char      digits;
028.     bool      bIsBuy;
029. }m_Infos;
030. //+------------------------------------------------------------------+
031. C_ElementsTrade *Open = NULL, *Stop = NULL, *Take = NULL;
032. //+------------------------------------------------------------------+
033. bool CheckCatch(ulong ticket)
034. {
035.     double vv;
036.     
037.     ZeroMemory(m_Infos);
038.     m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket);
039.     if (!PositionSelectByTicket(m_Infos.ticket)) return false;
040.     if (ChartWindowFind(0, m_Infos.szShortName) >= 0)
041.     {
042.         m_Infos.ticket = 0;
043.         return false;
044.     }
045.     m_Infos.szSymbol = PositionGetString(POSITION_SYMBOL);
046.     m_Infos.digits = (char)SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
047.     m_Infos.tickSize = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
048.     vv = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
049.     m_Infos.var = m_Infos.tickSize / vv;
050.     IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
051.     EventChartCustom(0, evUpdate_Position, ticket, 0, "");
052.             
053.     return true;
054. }
055. //+------------------------------------------------------------------+
056. inline void ProfitNow(void)
057. {
058.     double ask, bid, value;
059.     
060.     ask = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_ASK);
061.     bid = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_BID);    
062.     if (Open != NULL)
063.     {
064.         (*Open).ViewValue(value = (m_Infos.bIsBuy ? bid - m_Infos.priceOpen : m_Infos.priceOpen - ask));
065.         (*Take).ViewValue(value, false);
066.         (*Stop).ViewValue(value, false);
067.     }
068. }
069. //+------------------------------------------------------------------+
070. int OnInit()
071. {
072.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
073.     if (!CheckCatch(user00))
074.     {
075.         ChartIndicatorDelete(0, 0, def_ShortName);
076.         return INIT_FAILED;
077.     }
078. 
079.     return INIT_SUCCEEDED;
080. }
081. //+------------------------------------------------------------------+
082. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
083. {
084.     ProfitNow();
085.     
086.     return rates_total;
087. }
088. //+------------------------------------------------------------------+
089. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
090. {
091.     double volume;
092.     
093.     if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam);
094.     if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam);
095.     if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam);
096.     switch (id)
097.     {
098.         case CHARTEVENT_CUSTOM + evUpdate_Position:
099.             if (lparam != m_Infos.ticket) break;
100.             if (!PositionSelectByTicket(m_Infos.ticket))
101.             {
102.                 ChartIndicatorDelete(0, 0, m_Infos.szShortName);
103.                 return;
104.             };
105.             if (Open == NULL) Open = new C_ElementsTrade(
106.                                                          m_Infos.ticket,
107.                                                          m_Infos.szSymbol,
108.                                                          evMsgClosePositionEA, 
109.                                                          clrRoyalBlue, 
110.                                                          m_Infos.digits, 
111.                                                          m_Infos.tickSize, 
112.                                                          StringFormat("%I64u : Position opening price.", m_Infos.ticket), 
113.                                                          m_Infos.bIsBuy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
114.                                                          );
115.             if (Take == NULL) Take = new C_ElementsTrade(
116.                                                          m_Infos.ticket,
117.                                                          m_Infos.szSymbol,
118.                                                          evMsgCloseTakeProfit, 
119.                                                          clrForestGreen, 
120.                                                          m_Infos.digits, 
121.                                                          m_Infos.tickSize, 
122.                                                          StringFormat("%I64u : Take Profit price.", m_Infos.ticket),
123.                                                          m_Infos.bIsBuy
124.                                                          );
125.             if (Stop == NULL) Stop = new C_ElementsTrade(
126.                                                          m_Infos.ticket, 
127.                                                          m_Infos.szSymbol,
128.                                                          evMsgCloseStopLoss, 
129.                                                          clrFireBrick, 
130.                                                          m_Infos.digits, 
131.                                                          m_Infos.tickSize, 
132.                                                          StringFormat("%I64u : Stop Loss price.", m_Infos.ticket), 
133.                                                          m_Infos.bIsBuy
134.                                                          );
135.             volume = PositionGetDouble(POSITION_VOLUME);
136.             (*Open).UpdatePrice(0, m_Infos.priceOpen = PositionGetDouble(POSITION_PRICE_OPEN), volume, m_Infos.var);
137.             (*Take).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_TP), volume, m_Infos.var, PositionGetDouble(POSITION_SL));
138.             (*Stop).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_SL), volume, m_Infos.var, PositionGetDouble(POSITION_TP));
139.             ProfitNow();
140.             break;
141.     }
142.     ChartRedraw();
143. };
144. //+------------------------------------------------------------------+
145. void OnDeinit(const int reason)
146. {
147.     delete Open;
148.     delete Take;
149.     delete Stop;
150. }
151. //+------------------------------------------------------------------+

Indicador de posición

Como puedes observar, aquí se encuentra todo el conjunto de dependencias. Este código se explicó detalladamente en los artículos anteriores hasta llegar a su estado actual. A partir de este momento, todos los cambios se realizarán en el código mostrado arriba. El objetivo es aislar o, mejor dicho, separar el sistema de repetición/simulador del mercado real. Sin embargo, aunque llevemos a cabo esta separación, mantendremos el indicador de posición preparado para funcionar en ambas situaciones. El propio indicador no podrá distinguir si se está ejecutando en el sistema de repetición/simulador o si está conectado al servidor real de negociación, ya sea mediante una cuenta de demostración o una cuenta real.

Lo primero es obtener el nombre del símbolo que utilizaremos en el sistema de repetición/simulador. Así comenzaremos a separar las cosas. Este nombre está declarado en el archivo de cabecera Defines.mqh. Consulta los artículos anteriores para obtener más detalles. Solo tenemos que utilizar la definición incluida en ese archivo, que ya se encuentra incorporado mediante la línea 15 del código principal.

A primera vista, esta tarea parece relativamente sencilla, ¿verdad? Sin embargo, resulta bastante más laboriosa de lo que podría parecer. La razón es que todas las referencias a PositionGetDouble, PositionGetInteger y PositionSelectByTicket constituyen puntos de bloqueo para el uso del sistema de repetición/simulador. Precisamente estos son los puntos que debemos sortear para que el indicador de posición pueda utilizarse realmente en el sistema de repetición/simulador.

Por tanto, debemos encontrar una solución que nos permita comenzar a trabajar de forma efectiva. En este punto, muchos se quedarían bloqueados sin poder avanzar al siguiente paso de la implementación, porque parece imposible. ¿Pero realmente lo es? Recordemos algo que expliqué hace tiempo en esta misma serie de artículos: el uso de SQL y la comunicación entre MetaTrader 5 y Excel. Cuando traté esos temas, quizá pensaste que no tenían ninguna utilidad dentro de una serie dedicada a construir un replay/simulador. ¿Por qué explicar aquello en este contexto? Pues bien, si no estudiaste esos contenidos, te recomiendo que los busques dentro de esta serie y los revises, porque ha llegado el momento de aplicar esos conocimientos.


Comprender la elección de la solución

Por razones prácticas, y no necesariamente por dificultades de programación, utilizaremos una solución basada en SQL en el sistema de repetición/simulador. La razón es que MQL5 ya ofrece recursos para trabajar con SQL sin recurrir a programación externa. Recuerda que mi objetivo es demostrar que podemos crear un replay/simulador íntegramente en MQL5. No obstante, si lo necesitas o lo prefieres, puedes adaptar las cosas para utilizar Excel junto con MetaTrader 5 y obtener así las métricas de tus operaciones y estudios en el sistema de repetición/simulador. Sin embargo, no explicaré aquí cómo hacerlo. Utilizaremos directamente una base de datos SQL.

En este punto puede surgir una pregunta: ¿por qué no emplear un archivo específico, ya sea de texto o binario, para simular el servidor? ¿No sería más sencillo que utilizar una base de datos SQL? Es una pregunta pertinente y, en principio, sí sería más sencillo. Sin embargo, considera lo siguiente: si las operaciones realizadas en el sistema de repetición/simulador se almacenaran en un archivo creado exclusivamente para ese fin, posteriormente tendríamos que realizar trabajo adicional para analizarlas. Al guardar todo en una base de datos SQL, ese trabajo adicional desaparece. Resulta mucho más sencillo leer el contenido de una base de datos SQL que convertir el contenido de un archivo creado específicamente para almacenar las operaciones realizadas en el sistema de repetición/simulador.

Por tanto, la cuestión no radica en el uso dentro del sistema de repetición/simulador, sino en la facilidad para leer posteriormente lo que se hizo en el sistema de repetición/simulador. Ese es el motivo por el que utilizaremos una base de datos SQL.

A partir de aquí, las cosas toman otro rumbo. El indicador de posición no debe crear el archivo de la base de datos; únicamente debe leer ese archivo. La creación del archivo quedará a cargo del Asesor Experto, que será quien simule realmente el servidor de negociación. El indicador de posición se limitará a mostrar lo que se encuentra en la base de datos creada por el Asesor Experto.

Bien. Esta es la base inicial. Sin embargo, antes de comenzar debemos revisar algunos aspectos. Desde la última actualización del servicio de repetición/simulador, las clases de apoyo han experimentado cambios importantes. Este es uno de los problemas que complica el desarrollo de proyectos de mayor tamaño. Muchos programadores principiantes simplemente terminan abandonando esos proyectos. Otros, aunque no desistan, entran en un ciclo que impide retomar realmente el proyecto original.

Si intentas utilizar ahora el servicio de repetición/simulador, después de tanto tiempo, observarás que el ejecutable no funciona como debería. Y, si vuelves a compilar el servicio a partir del código antiguo, comprobarás que no es posible iniciar la reproducción del sistema. Incluso si consigues forzar la reproducción, no podrás interactuar con el indicador de control. Esto se debe a que hay varias cosas desactualizadas en el código del sistema de repetición/simulador. Por tanto, antes de continuar, debemos actualizar el código antiguo del sistema de repetición/simulador. No hay motivo para preocuparse: la actualización se realizará de una forma sencilla y fácil de comprender.


Actualizar el sistema de repetición/simulador

La primera actualización debe realizarse en la clase C_DrawImage, como se muestra en el siguiente fragmento.

138. //+------------------------------------------------------------------+
139.         C_DrawImage(C_Terminal *ptr, string szObjName, const color cFilter, const string szFile1, const string szFile2, const bool r180 = false)
140.             {
141.                 (*ptr).CreateObjectGraphics(m_szObjName = szObjName, OBJ_BITMAP_LABEL, ePriorityDefault);
142.                 ObjectSetString(m_ID = (*ptr).GetInfoTerminal().ID, m_szObjName, OBJPROP_BMPFILE, m_szRecName = "::" + m_szObjName);
143.                 Initilize(0, cFilter, szFile1, r180);
144.                 if (szFile2 != NULL) Initilize(1, cFilter, szFile2, r180);
145.             }
146. //+------------------------------------------------------------------+

Fragmento de C_DrawImage

La modificación se encuentra en la línea 141 y establece el valor de la propiedad OBJPROP_ZORDER. Sin este ajuste, los objetos creados por esta clase tendrían el mismo valor de ZORDER que los de la clase C_Mouse. Como C_DrawImage se utiliza en el indicador de control, debemos aumentar el valor de OBJPROP_ZORDER para que la clase C_Controls pueda capturar correctamente los clics realizados sobre los objetos.

La siguiente actualización requiere algo más de trabajo, ya que afecta a la clase C_Controls. Aunque muchos puedan pensar que estas modificaciones no son necesarias, lo cierto es que MetaTrader 5 también ha recibido numerosas actualizaciones y mejoras durante este tiempo. Estas actualizaciones y mejoras han aumentado la estabilidad del programa, además de mejorar muchas otras cosas. Por tanto, debemos actualizar el sistema de repetición/simulador. No obstante, creo que esta será la última vez que necesitaremos actualizar el sistema de repetición/simulador. A continuación puedes ver el nuevo código de la clase C_Controls, ya actualizado.

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

C_Controls

No entraré en demasiados detalles sobre los cambios. Quedarán como ejercicio para que estudies el código e intentes identificar qué se ha modificado exactamente. Sin embargo, te daré una pista: observa el constructor de la línea 138 y la declaración de la clase situada en la línea 27. Ambos han cambiado, al igual que algunos otros puntos del código. Todas estas modificaciones se deben al cambio realizado en la herencia. Como el constructor ha cambiado, el código principal también ha cambiado. A continuación se muestra únicamente el fragmento que realmente tuvo que actualizarse.

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.133"
07. #property link "https://www.mql5.com/pt/articles/13531"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. #property indicator_buffers 1
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Service Graphics\C_Controls.mqh>
13. //+------------------------------------------------------------------+
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. input long user00 = 0;        //ID
17. //+------------------------------------------------------------------+
18. double m_Buff[];
19. int    m_RatesTotal = 0;
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.     if (CheckPointer(control = new C_Controls(user00, "Market Replay Control")) == POINTER_INVALID)
24.         SetUserError(C_Terminal::ERR_PointerInvalid);
25.     if ((_LastError >= ERR_USER_ERROR_FIRST) || (user00 == 0))
26.     {
27.         Print("Control indicator failed on initialization.");
28.         return INIT_FAILED;
29.     }
30.     SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
31.     ArrayInitialize(m_Buff, EMPTY_VALUE);
32.         
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+

Fragmento del indicador de control

Observa que únicamente fue necesario modificar la línea 23. Por supuesto, también cambié la versión indicada en la línea 6 y el enlace a este artículo, que aparece en la línea 7. Con esto, el servicio de repetición/simulador vuelve a estar actualizado y preparado para la siguiente etapa, en la que añadiremos el indicador de posición al sistema de repetición/simulador.

Sin embargo, esta actualización resultó tan provechosa que se observó que también podía realizarse otra en el indicador de mouse. Esta modificación es opcional: no estás obligado a aplicarla. No obstante, el código compilado que encontrarás en los archivos adjuntos de este artículo y de los siguientes ya incluirá la actualización que se muestra a continuación. Observa que se presentan tres archivos completos, ya que no explicaré en detalle dónde se realizaron los cambios.

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.132"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/13531"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. //+------------------------------------------------------------------+
12. double GL_PriceClose;
13. datetime GL_TimeAdjust;
14. //+------------------------------------------------------------------+
15. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
16. //+------------------------------------------------------------------+
17. C_Study *Study         = NULL;
18. //+------------------------------------------------------------------+
19. input color user01    = clrBlack;                     //Price Line
20. input color user02    = clrPaleGreen;                 //Positive Study
21. input color user03    = clrLightCoral;                //Negative Study
22. //+------------------------------------------------------------------+
23. C_Study::eStatusMarket m_Status;
24. //+------------------------------------------------------------------+
25. int OnInit()
26. {
27.     Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03);
28.     if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
29.     MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
30.     OnBookEvent((*Study).GetInfoTerminal().szSymbol);
31.     m_Status = C_Study::eCloseMarket;
32.     
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 
37.                 const double& high[], const double& low[], const double& close[], const long& tick_volume[], 
38.                 const long& volume[], const int& spread[]) 
39. {
40.     GL_PriceClose = close[rates_total - 1];
41.     if (_Symbol == def_SymbolReplay)
42.         GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService);
43.     (*Study).Update(m_Status);    
44.     
45.     return rates_total;
46. }
47. //+------------------------------------------------------------------+
48. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
49. {
50.     (*Study).DispatchMessage(id, lparam, dparam, sparam);
51. 
52.     ChartRedraw();
53. }
54. //+------------------------------------------------------------------+
55. void OnBookEvent(const string &symbol)
56. {
57.     MqlBookInfo book[];
58.     C_Study::eStatusMarket loc = m_Status;
59.    
60.     if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
61.     MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
62.     m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading));
63.     for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
64.         if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
65.     if (loc != m_Status) (*Study).Update(m_Status);
66. }
67. //+------------------------------------------------------------------+
68. void OnDeinit(const int reason)
69. {
70.     MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
71. 
72.     delete Study;
73. }
74. //+------------------------------------------------------------------+

Indicador de mouse

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

C_Study

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_"
007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0))
008. //+------------------------------------------------------------------+
009. class C_Mouse : public C_Terminal
010. {
011.     public    :
012.         enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
013.         enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
014. //+------------------------------------------------------------------+
015.     protected:
016. //+------------------------------------------------------------------+
017.         void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
018.             {
019.                if (!m_Mem.IsInitOk) return;
020.                CreateObjectGraphics(szName, OBJ_BUTTON);
021.                ObjectSetInteger(0, szName, OBJPROP_STATE, true);
022.                ObjectSetInteger(0, szName, OBJPROP_BORDER_COLOR, clrBlack);
023.                ObjectSetInteger(0, szName, OBJPROP_COLOR, clrBlack);
024.                ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, backColor);
025.                ObjectSetString(0, szName, OBJPROP_FONT, "Lucida Console");
026.                ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, 10);
027.                ObjectSetInteger(0, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
028.                ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x);
029.                ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
030.                ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 
031.                ObjectSetInteger(0, szName, OBJPROP_YSIZE, 18);
032.             }
033. //+------------------------------------------------------------------+
034.     private    :
035.         enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
036.         struct st01
037.         {
038.             st_Mouse Data;
039.             color    corLineH,
040.                      corTrendP,
041.                      corTrendN;
042.             eStudy   Study;
043.         }m_Info;
044.         struct st_Mem
045.         {
046.             bool     CrossHair,
047.                      IsFull,
048.                      IsInitOk;
049.             datetime dt;
050.             string   szShortName,
051.                      szLineH,
052.                      szLineV,
053.                      szLineT,
054.                      szBtnS;
055.         }m_Mem;
056. //+------------------------------------------------------------------+
057.         void GetDimensionText(const string szArg, int &w, int &h)
058.             {
059.                 TextSetFont("Lucida Console", -100, FW_NORMAL);
060.                 TextGetSize(szArg, w, h);
061.                 h += 5;
062.                 w += 5;
063.             }
064. //+------------------------------------------------------------------+
065.         void CreateStudy(void)
066.             {
067.                 if (m_Mem.IsFull)
068.                 {
069.                     CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH);
070.                     CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH);
071.                     ObjectSetInteger(0, m_Mem.szLineT, OBJPROP_WIDTH, 2);
072.                     CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy);
073.                 }
074.                 m_Info.Study = eStudyCreate;
075.             }
076. //+------------------------------------------------------------------+
077.         void ExecuteStudy(const double memPrice)
078.             {
079.                 double v1 = GetPositionsMouse().Position.Price - memPrice;
080.                 int w, h;
081.                 
082.                 if (!CheckClick(eClickLeft))
083.                 {
084.                     m_Info.Study = eStudyNull;
085.                     ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
086.                     if (m_Mem.IsFull) ObjectsDeleteAll(0, def_MousePrefixName + "T");
087.                 }else if (m_Mem.IsFull)
088.                 {
089.                     string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
090.                     MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetPositionsMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0));
091.                     GetDimensionText(sz1, w, h);
092.                     ObjectSetString(0, m_Mem.szBtnS, OBJPROP_TEXT, sz1);                                                                
093.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
094.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_XSIZE, w);
095.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_YSIZE, h);
096.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetPositionsMouse().Position.X_Adjusted - w);
097.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));                
098.                     ObjectMove(0, m_Mem.szLineT, 1, GetPositionsMouse().Position.dt, GetPositionsMouse().Position.Price);
099.                     ObjectSetInteger(0, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetPositionsMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
100.                 }
101.                 m_Info.Data.ButtonStatus = eKeyNull;
102.             }
103. //+------------------------------------------------------------------+
104.     public    :
105. //+------------------------------------------------------------------+
106.         C_Mouse(const long id, const string szShortName)
107.             :C_Terminal(id)
108.             {
109.                 m_Mem.IsInitOk = false;
110.                 m_Mem.szShortName = szShortName;
111.             }
112. //+------------------------------------------------------------------+
113.         C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
114.             :C_Terminal(id)
115.             {
116.                 if (!(m_Mem.IsInitOk = IndicatorCheckPass(m_Mem.szShortName = szShortName))) return;
117.                 m_Mem.CrossHair = (bool)ChartGetInteger(0, CHART_CROSSHAIR_TOOL);
118.                 ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
119.                  ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
120.                 ZeroMemory(m_Info);
121.                 m_Info.corLineH  = corH;
122.                 m_Info.corTrendP = corP;
123.                 m_Info.corTrendN = corN;
124.                 m_Info.Study = eStudyNull;
125.                 if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
126.                     CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH);
127.                 ChartRedraw(0);
128.             }
129. //+------------------------------------------------------------------+
130.         ~C_Mouse()
131.             {
132.                 long loc = ChartGetInteger(0, CHART_EVENT_OBJECT_DELETE);
133.                 if (!m_Mem.IsInitOk) return;
134.                 ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
135.                 ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(0, m_Mem.szShortName) != -1);
136.                 ChartSetInteger(0, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
137.                 ObjectsDeleteAll(0, def_MousePrefixName);
138.                 ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, loc);
139.             }
140. //+------------------------------------------------------------------+
141. inline bool CheckClick(const eBtnMouse value) 
142.             {
143.                 return (m_Info.Data.ButtonStatus & value) == value;
144.             }
145. //+------------------------------------------------------------------+
146.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
147.             {
148.                 int w = 0;
149.                 static double memPrice = 0;
150.         
151.                 C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
152.                 switch (id)
153.                 {
154.                     case (CHARTEVENT_CUSTOM + evHideMouse):
155.                         if (m_Mem.IsFull) ObjectSetInteger(0, m_Mem.szLineH, OBJPROP_COLOR, clrNONE);
156.                         break;
157.                     case (CHARTEVENT_CUSTOM + evShowMouse):
158.                         if (m_Mem.IsFull) ObjectSetInteger(0, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH);
159.                         break;
160.                     case CHARTEVENT_MOUSE_MOVE:
161.                         m_Info.Data = GetPositionsMouse();
162.                         if (m_Mem.IsFull) ObjectMove(0, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price);
163.                         if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(0, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0);
164.                         m_Info.Data.ButtonStatus = (uchar) sparam;
165.                         if (CheckClick(eClickMiddle) && (m_Info.Study != eStudyCreate))
166.                             if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(0, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
167.                         if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
168.                         {
169.                             ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
170.                             if (m_Mem.IsFull)    ObjectMove(0, m_Mem.szLineT, 0, m_Mem.dt = m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
171.                             m_Info.Study = eStudyExecute;
172.                         }
173.                         if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
174.                         m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
175.                         break;
176.                     case CHARTEVENT_OBJECT_DELETE:
177.                         if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH))
178.                             CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH);
179.                         break;
180.                 }
181.             }
182. //+------------------------------------------------------------------+
183. };
184. //+------------------------------------------------------------------+
185. #undef macro_NameObjectStudy
186. //+------------------------------------------------------------------+

C_Mouse

Con esto, hemos terminado de actualizar por completo el sistema. Finalmente podemos comenzar a trabajar en el sistema de repetición/simulador para utilizar el indicador de posición con el símbolo personalizado. Este símbolo es precisamente el que utiliza el sistema de repetición/simulador.


Actualizar el código del indicador de posición

Muy bien. Ahora que ya hemos actualizado todos los sistemas, podemos comenzar a adaptar el código del indicador de posición para utilizarlo en el sistema de repetición/simulador. Para adaptar el código, partiremos inicialmente del supuesto de que aún no sabemos cómo se creará la base de datos SQL. A continuación se muestra el nuevo código completo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Positions.ico"
004. #property description "Indicator for tracking an open position on the server."
005. #property description "This should preferably be used together with an Expert Advisor."
006. #property description "For more details see the same article."
007. #property version   "1.133"
008. #property link "https://www.mql5.com/pt/articles/13531"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. //+------------------------------------------------------------------+
012. #define def_ShortName "Position View"
013. //+------------------------------------------------------------------+
014. #include <Market Replay\Order System\C_ElementsTrade.mqh>
015. #include <Market Replay\Defines.mqh>
016. //+------------------------------------------------------------------+
017. input ulong user00 = 0;        //For Expert Advisor use
018. //+------------------------------------------------------------------+
019. struct st00
020. {
021.     ulong   ticket;
022.     string  szShortName,
023.             szSymbol;
024.     double  priceOpen,
025.             var,
026.             tickSize;
027.     char    digits;
028.     bool    bIsBuy;
029. }m_Infos;
030. //+------------------------------------------------------------------+
031. C_ElementsTrade *Open = NULL, *Stop = NULL, *Take = NULL;
032. //+------------------------------------------------------------------+
033. inline const double _PositionGetDouble(ENUM_POSITION_PROPERTY_DOUBLE arg)
034. {
035.     if (_Symbol != def_SymbolReplay)
036.         return PositionGetDouble(arg);
037.     
038.     return 0;
039. }
040. //+------------------------------------------------------------------+
041. inline const long _PositionGetInteger(ENUM_POSITION_PROPERTY_INTEGER arg)
042. {
043.     if (_Symbol != def_SymbolReplay)
044.         return PositionGetInteger(arg);
045.         
046.     return 0;
047. }
048. //+------------------------------------------------------------------+
049. inline const bool _PositionSelectByTicket(ulong arg)
050. {
051.     if (_Symbol != def_SymbolReplay)
052.         return PositionSelectByTicket(arg);
053.         
054.     return false;
055. }
056. //+------------------------------------------------------------------+
057. bool CheckCatch(ulong ticket)
058. {
059.     double vv;
060.     
061.     ZeroMemory(m_Infos);
062.     m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket);
063.     if (!_PositionSelectByTicket(m_Infos.ticket)) return false;
064.     if (ChartWindowFind(0, m_Infos.szShortName) >= 0)
065.     {
066.         m_Infos.ticket = 0;
067.         return false;
068.     }
069.     m_Infos.szSymbol = (_Symbol == def_SymbolReplay ? def_SymbolReplay : PositionGetString(POSITION_SYMBOL));
070.     m_Infos.digits = (char)SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
071.     m_Infos.tickSize = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
072.     vv = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
073.     m_Infos.var = m_Infos.tickSize / vv;
074.     IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
075.     EventChartCustom(0, evUpdate_Position, ticket, 0, "");
076.             
077.     return true;
078. }
079. //+------------------------------------------------------------------+
080. inline void ProfitNow(void)
081. {
082.     double ask, bid, value;
083.     
084.     ask = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_ASK);
085.     bid = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_BID);    
086.     if (Open != NULL)
087.     {
088.         (*Open).ViewValue(value = (m_Infos.bIsBuy ? bid - m_Infos.priceOpen : m_Infos.priceOpen - ask));
089.         (*Take).ViewValue(value, false);
090.         (*Stop).ViewValue(value, false);
091.     }
092. }
093. //+------------------------------------------------------------------+
094. int OnInit()
095. {
096.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
097.     if (!CheckCatch(user00))
098.     {
099.         ChartIndicatorDelete(0, 0, def_ShortName);
100.         return INIT_FAILED;
101.     }
102. 
103.     return INIT_SUCCEEDED;
104. }
105. //+------------------------------------------------------------------+
106. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
107. {
108.     ProfitNow();
109.     
110.     return rates_total;
111. }
112. //+------------------------------------------------------------------+
113. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
114. {
115.     double volume;
116.     
117.     if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam);
118.     if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam);
119.     if (Stop != NULL)    (*Stop).DispatchMessage(id, lparam, dparam, sparam);
120.     switch (id)
121.     {
122.         case CHARTEVENT_CUSTOM + evUpdate_Position:
123.             if (lparam != m_Infos.ticket) break;
124.             if (!_PositionSelectByTicket(m_Infos.ticket))
125.             {
126.                 ChartIndicatorDelete(0, 0, m_Infos.szShortName);
127.                 return;
128.             };
129.             if (Open == NULL) Open = new C_ElementsTrade(
130.                                                         m_Infos.ticket,
131.                                                         m_Infos.szSymbol,
132.                                                         evMsgClosePositionEA, 
133.                                                         clrRoyalBlue, 
134.                                                         m_Infos.digits, 
135.                                                         m_Infos.tickSize, 
136.                                                         StringFormat("%I64u : Position opening price.", m_Infos.ticket), 
137.                                                         m_Infos.bIsBuy = (_PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
138.                                                         );
139.             if (Take == NULL) Take = new C_ElementsTrade(
140.                                                         m_Infos.ticket,
141.                                                         m_Infos.szSymbol,
142.                                                         evMsgCloseTakeProfit, 
143.                                                         clrForestGreen, 
144.                                                         m_Infos.digits, 
145.                                                         m_Infos.tickSize, 
146.                                                         StringFormat("%I64u : Take Profit price.", m_Infos.ticket),
147.                                                         m_Infos.bIsBuy
148.                                                         );
149.             if (Stop == NULL) Stop = new C_ElementsTrade(
150.                                                         m_Infos.ticket, 
151.                                                         m_Infos.szSymbol,
152.                                                         evMsgCloseStopLoss, 
153.                                                         clrFireBrick, 
154.                                                         m_Infos.digits, 
155.                                                         m_Infos.tickSize, 
156.                                                         StringFormat("%I64u : Stop Loss price.", m_Infos.ticket), 
157.                                                         m_Infos.bIsBuy
158.                                                         );
159.             volume = _PositionGetDouble(POSITION_VOLUME);
160.             (*Open).UpdatePrice(0, m_Infos.priceOpen = _PositionGetDouble(POSITION_PRICE_OPEN), volume, m_Infos.var);
161.             (*Take).UpdatePrice(m_Infos.priceOpen, _PositionGetDouble(POSITION_TP), volume, m_Infos.var, _PositionGetDouble(POSITION_SL));
162.             (*Stop).UpdatePrice(m_Infos.priceOpen, _PositionGetDouble(POSITION_SL), volume, m_Infos.var, _PositionGetDouble(POSITION_TP));
163.             ProfitNow();
164.             break;
165.     }
166.     ChartRedraw();
167. };
168. //+------------------------------------------------------------------+
169. void OnDeinit(const int reason)
170. {
171.     delete Open;
172.     delete Take;
173.     delete Stop;
174. }
175. //+------------------------------------------------------------------+


Indicador de posición

Sé que puede parecer una locura volver a colocar todo el código completo. Sin embargo, quiero que observes atentamente lo que estoy haciendo, porque se trata de algo extremadamente importante que podría resultarte útil en el futuro. Presta atención al código anterior. He añadido tres funciones nuevas cuyo objetivo es permitirnos crear en el Asesor Experto todo lo necesario para que el propio Asesor Experto construya una base de datos con las posiciones que vayan surgiendo. Estas posiciones se crearán a medida que se ejecute el sistema de repetición/simulador y tú, mediante el indicador Chart Trade y el indicador de mouse, solicites al Asesor Experto que compre o venda. El resto de la interacción se conseguirá mediante eventos personalizados disparados por el Asesor Experto. Estos eventos servirán para actualizar, colocar o eliminar del gráfico el indicador de posición en el símbolo de repetición/simulador.

Bien. La finalidad básica de estas tres funciones, situadas en las líneas 33, 41 y 49, es conseguir que el indicador de posición funcione del mismo modo en cualquier escenario. Si estamos conectados al servidor real de negociación, ya sea mediante una cuenta de demostración o una cuenta real, las comprobaciones de las líneas 35, 43 y 51 permitirán que las funciones originales de MQL5 obtengan y actualicen los valores de la posición para que los datos se muestren correctamente en el gráfico. Y no pienses que he olvidado PositionGetString. Observa la línea 69 y verás cómo resolví el acceso a PositionGetString. Como se trata de algo muy sencillo, no fue necesario crear un procedimiento adicional.

Esta es la primera parte de lo que necesitamos. Observa ahora que los nombres de las funciones situadas en las líneas mencionadas son muy similares a los utilizados en la biblioteca de MQL5. La similitud de los nombres es intencionada, ya que, al leer el código, aparentemente no encontrarás ninguna diferencia. Sin embargo, si prestas atención, verás que los nombres de las funciones destinadas a obtener los datos de la posición difieren ligeramente por el primer carácter. De este modo, podemos limitar todo el trabajo necesario a implementar el código contenido en esas funciones modificadas.

Naturalmente, muchos interpretarían esta forma de programar como una sobrecarga de funciones. Sin embargo, en este caso no existe una sobrecarga propiamente dicha, porque los nombres son ligeramente diferentes.

Con esto hemos completado los cambios necesarios en el indicador de posición. Ahora debemos pasar al Asesor Experto, donde realizaremos modificaciones algo más profundas.


La unión hace la fuerza

Durante algún tiempo centraremos la atención en el Asesor Experto. Por los comentarios, sé que muchos siguen esta serie con la intención de utilizar su propio Asesor Experto. Por eso te mostraré cómo integrar tu código con lo que desarrollaremos aquí. El objetivo es que el sistema de repetición/simulador pueda resultarte útil en la práctica, ya que no tendría sentido limitarte exclusivamente a las aplicaciones que estoy explicando. Por este motivo, no colocaremos el código directamente en el Asesor Experto. Crearemos un archivo de cabecera para alojar el código, aunque quizá sea necesario más de uno.

En este primer momento crearemos un archivo que nos proporcionará el soporte inicial para trabajar con SQLite. Ya expliqué cómo hacer algunas de estas cosas en otros artículos, pero aquí haremos algo mucho más específico. El código inicial ya se presentó en otro artículo de esta misma serie, donde hablé de SQL, por lo que no volveré a explicar ese código. Me limitaré a mostrar su estado inicial. Puedes verlo completo a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_FileDataBase "Replay.db"
005. //+------------------------------------------------------------------+
006. #include "..\Service Graphics\Support\C_Array.mqh"
007. //+------------------------------------------------------------------+
008. class C_ReplayDataBase
009. {
010.     private    :
011. //+------------------------------------------------------------------+
012.         struct st_00
013.         {
014.             int     handle,
015.                     request;
016.         }m_Infos;
017.         C_Array     m_Arr;
018. //+------------------------------------------------------------------+
019.         void Convert(const char &buff[], const int size)
020.         {
021.             string sz0 = "";
022.             bool b0, b1, bs1, bs2, bc0, bc1, bc;
023.             int nLine = 1;
024.             
025.             b0 = b1 = bs1 = bs2 = bc0 = bc1 = bc = false;
026.             for (int count = 0, nC0 = nLine; count < size; count++)
027.             {
028.                 switch (buff[count])
029.                 {
030.                     case '\t':
031.                         sz0 += (bs1 || bs2 ? "\t" : "");
032.                         break;
033.                     case '\n':
034.                         nC0++;
035.                     case '\r':
036.                         bc0 = false;
037.                         break;
038.                     case ';':
039.                         b0 = (bs1 || bs2 || bc0 || bc1 ? b0 : true);
040.                     default:
041.                         switch (buff[count])
042.                         {
043.                             case '"':
044.                                 bs1 = (bs2 || bc0 || bc1 ? bs1 : !bs1);
045.                                 break;
046.                             case '\'':
047.                                 bs2 = (bs1 || bc0 || bc1 ? bs2 : !bs2);
048.                                 break;
049.                         }
050.                         if (((count + 1) < size) && (!bs1) && (!bs2))
051.                         {
052.                             if (bc = ((buff[count] == '-') && (buff[count + 1] == '-'))) bc0 = true;
053.                             if (bc = ((buff[count] == '/') && (buff[count + 1] == '*'))) bc1 = true;
054.                             if (bc = ((buff[count] == '*') && (buff[count + 1] == '/'))) bc1 = false;
055.                             if (bc)
056.                             {
057.                                 count += 1;
058.                                 bc = false;
059.                                 continue;
060.                             }
061.                         }
062.                         if (!(bc0 || bc1))
063.                         {
064.                             if ((!b1) && (buff[count] > ' '))
065.                             {
066.                                 b1 = true;
067.                                 nLine = nC0;
068.                             }
069.                             sz0 += (b1 ? StringFormat("%c", buff[count]) : "");
070.                         }
071.                 }
072.                 if (b0)
073.                 {
074.                     m_Arr.Add(sz0, nLine);
075.                     sz0 = "";
076.                     b0 = b1 = false;
077.                 }
078.             }
079.         }
080. //+------------------------------------------------------------------+
081.         const string ExecSQL(void)
082.         {
083.             string szCmd;
084.             
085.             for (int count = 0, nLine; count >= 0; count++)
086.             {
087.                 szCmd = m_Arr.At(count, nLine);
088.                 if (nLine < 0) break;
089.                 if (!ExecCommandSQL(szCmd))
090.                     return StringFormat("Execution of line %d of the SQL script failed...", nLine);
091.             }
092.             
093.             return NULL;
094.         }
095. //+------------------------------------------------------------------+
096.     public    :
097. //+------------------------------------------------------------------+
098.         C_ReplayDataBase()
099.         {
100.             ZeroMemory(m_Infos);
101.             m_Infos.handle = DatabaseOpen(def_FileDataBase, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE);
102.             m_Infos.request = INVALID_HANDLE;
103.         }
104. //+------------------------------------------------------------------+
105.         ~C_ReplayDataBase()
106.         {
107.             DatabaseClose(m_Infos.handle);
108.         }
109. //+------------------------------------------------------------------+
110.         const string ExecResourceSQL(const string szResource)
111.         {
112.             char buff[];
113.             int size;
114.             
115.             ArrayResize(buff, size = StringLen(szResource));
116.             StringToCharArray(szResource, buff);
117.             Convert(buff, size);
118.             ArrayFree(buff);
119.             
120.             return ExecSQL();
121.         }
122. //+------------------------------------------------------------------+
123.         bool ExecCommandSQL(const string szCmd)
124.         {
125.             return (m_Infos.handle == INVALID_HANDLE ? false : DatabaseExecute(m_Infos.handle, szCmd));
126.         }
127. //+------------------------------------------------------------------+
128.         bool ExecRequestOfData(const string szCmd)
129.         {
130.             if (m_Infos.request != INVALID_HANDLE) DatabaseFinalize(m_Infos.request);
131.             return ((m_Infos.request = DatabasePrepare(m_Infos.handle, szCmd)) != INVALID_HANDLE);
132.         }
133. //+------------------------------------------------------------------+
134.         template <typename T> bool GetRegisterOfRequest(T &stObject, const bool Finish = true)
135.         {
136.             if (DatabaseReadBind(m_Infos.request, stObject)) return true;
137.             if (Finish)
138.             {
139.                 DatabaseFinalize(m_Infos.request);
140.                 m_Infos.request = INVALID_HANDLE;
141.             }
142.             return false;
143.         }
144. //+------------------------------------------------------------------+
145. };
146. //+------------------------------------------------------------------+
147. #undef def_FileDataBase
148. //+------------------------------------------------------------------+

C_ReplayDataBase

Aunque este código difiere ligeramente del original, funciona de la misma manera. Es algo más sencillo porque aquí estamos creando algo específico, distinto de lo que hicimos anteriormente. No obstante, todo lo explicado sobre este código en el artículo en el que apareció continúa siendo válido. La única diferencia relevante se encuentra en la línea 4, donde definimos el nombre del archivo de la base de datos.

Muy bien. Ya podemos comenzar a utilizar una base de datos SQLite. Ahora debemos preparar el Asesor Experto para que cree los datos que utilizará el indicador de posición. Pero, antes de comenzar, debemos comprender algunos detalles. Para comprenderlos, observa el esqueleto del Asesor Experto que utilizaremos, presentado en forma de fragmento. El código aparece a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
004. #property description "Demo version between interaction"
005. #property description "of Chart Trade and Expert Advisor"
006. #property version   "1.133"
007. #property link "https://www.mql5.com/pt/articles/13531"
008. //+------------------------------------------------------------------+
009. #include <Market Replay\Order System\C_Orders.mqh>
010. #include <Market Replay\Auxiliar\C_Terminal.mqh>
011. #include <Market Replay\SQL\C_ReplayDataBase.mqh>
012. //+------------------------------------------------------------------+
013. enum eTypeContract {MINI, FULL};
014. //+------------------------------------------------------------------+
015. input eTypeContract user00 = MINI;         //Cross order in contract
016. //+------------------------------------------------------------------+
017. C_Orders            *Orders   = NULL;
018. C_Terminal          *Terminal = NULL;
019. C_ReplayDataBase    *DB       = NULL;
020. //+------------------------------------------------------------------+
021. int OnInit()
022. {
023.     if (_Symbol == def_SymbolReplay) DB = new C_ReplayDataBase();
024.     Orders = new C_Orders(0xC0DEDAFE78514269);
025.     
026.     return INIT_SUCCEEDED;
027. }
028. //+------------------------------------------------------------------+
029. void OnTick() {}
030. //+------------------------------------------------------------------+
031. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
032. {
033.     int handle;
034.    
035.    (*Orders).DispatchMessage(id, lparam, dparam, sparam);
                                   .
                                   .
                                   .
061. }
062. //+------------------------------------------------------------------+
063. void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
064. {
                                   .
                                   .
                                   .
092. }
093. //+------------------------------------------------------------------+
094. void OnDeinit(const int reason)
095. {
                                   .
                                   .
                                   .
                                   
111.     delete Orders;
112.     delete Terminal;
113.     delete DB;
114. }
115. //+------------------------------------------------------------------+

Fragmento del Asesor Experto

Aquí quiero que observes algunos detalles. En la línea 11 añadimos el archivo de cabecera que acabamos de ver, cuyo objetivo es permitirnos utilizar la base de datos SQLite. En la línea 19 creamos un puntero para acceder a la clase de acceso a la base de datos. En la línea 23 determinamos cuándo se utilizará la base de datos o, más concretamente, cuándo la variable de tipo puntero recibirá acceso a la clase que gestiona la base de datos. Por último, en la línea 113 liberamos el puntero de la memoria. Eso es, básicamente, lo que haremos. Sin embargo, existen algunas diferencias entre el uso del Asesor Experto cuando está conectado al servidor real de negociación —ya sea mediante una cuenta de demostración o una cuenta real— y cuando se utiliza en el sistema de repetición/simulador.

Cuando el Asesor Experto esté conectado al servidor real, todo el acceso a los datos de la posición se realizará mediante el procedimiento OnTradeTransaction. En cambio, cuando se utilice el sistema de repetición/simulador, no podremos trabajar de la misma manera.

Y aquí aparece precisamente el gran detalle que debemos analizar y resolver. Podríamos trasladar toda la carga al Asesor Experto cuando utilicemos el sistema de repetición/simulador. Pero ¿no existiría una forma algo más sencilla de gestionar esta situación? Cuando hagamos clic en uno de los botones de Chart Trade, se disparará un evento personalizado. La línea 35 del código anterior capturará ese evento y hará que la clase C_Orders ejecute la solicitud. Sin embargo, enviar la solicitud no basta para que el indicador de posición aparezca o se actualice. La aparición o actualización del indicador de posición corresponde al procedimiento OnTradeTransaction. Por este motivo, tenemos dos escenarios: uno en el que estamos conectados al servidor real y otro en el que no existe esa conexión.

Ahora bien, si limitamos el funcionamiento de la línea 35 para impedir que la clase C_Orders reciba eventos cuando utilicemos el sistema de repetición/simulador, reduciremos parte de la carga del Asesor Experto. En ese caso, podríamos simular una respuesta del servidor; es decir, simular el evento que MetaTrader 5 dispara cuando recibe información del servidor. Este evento provoca la ejecución de OnTradeTransaction, lo que permite colocar, actualizar o eliminar del gráfico el indicador de posición.

Recuerda lo siguiente: cuando utilicemos el sistema de repetición/simulador, la información que empleará el indicador de posición estará almacenada en la base de datos. De este modo, garantizamos un funcionamiento perfecto en cualquier caso. No solo simularemos el movimiento del precio, como ya hacemos, sino también los eventos que normalmente dispara MetaTrader 5.

Aunque todavía tendremos que resolver otro problema, dejaremos esa cuestión para más adelante. Lo primero será conseguir que el Asesor Experto simule de forma forzada un evento OnTradeTransaction. Sé que forzar la simulación de este evento puede parecer extremadamente complejo, pero la tarea no es tan compleja. Además, siguiendo este camino, tendremos que implementar mucho menos código y obtendremos un Asesor Experto mucho más estable y fiable, ya que podrá gestionar del mismo modo tanto los eventos procedentes del servidor real como los simulados por el sistema de repetición/simulación.


Consideraciones finales

Como acabas de leer en el último tema, estamos llegando a la recta final. El desarrollo del sistema de repetición/simulador está casi terminado. Es cierto que aún tendremos que completar algunas cosas, pero, en comparación con todo lo que ya hemos hecho, implementar lo que falta será sencillo. Sin embargo, como todo lo mostrado en este artículo debe asimilarse y comprenderse adecuadamente, quiero que tú, estimado lector y entusiasta, comiences a pensar en cómo adaptar tu Asesor Experto a lo que presentaremos en los próximos artículos. De este modo, podrás integrar correctamente tu código con lo que veremos e implementaremos.

No conozco el nivel de conocimientos o experiencia en MQL5 de cada lector. Aun así, no esperes a que se acumulen demasiadas cosas antes de empezar a plantearte cómo adaptar tu código a lo que se implementará en esta serie. Si esperas demasiado, terminarás perdiendo el hilo y, cuando intentes realizar la integración, encontrarás como mínimo grandes dificultades. Esto será aún más probable porque sé que muchos prefieren aplicar determinadas formas de programar. Sin embargo, aquí no pretendo crear una solución que sirva para todos. Mi objetivo es mostrar cómo podemos desarrollar un sistema completo de repetición/simulación cuyas aplicaciones también puedan utilizarse en una cuenta de demostración o en una cuenta real. Así que prepárate, porque los próximos artículos serán más exigentes y en esos artículos terminaremos de desarrollar el sistema de repetición/simulador.

ArchivoDescripción
Experts\Expert Advisor.mq5
Muestra la interacción entre Chart Trade y el Asesor Experto. Es necesario Mouse Study para la interacción.
Indicators\Chart Trade.mq5Crea la ventana en la que se configura la orden que se enviará. Es necesario Mouse Study para la interacción.
Indicators\Market Replay.mq5Crea los controles necesarios para interactuar con el servicio de repetición/simulador. Es necesario Mouse Study para la interacción.
Indicators\Mouse Study.mq5Permite la interacción entre los controles gráficos y el usuario. Es necesario tanto para utilizar el sistema de repetición/simulador como para operar en el mercado real.
Indicators\Order Indicator.mq5Se encarga de mostrar las órdenes de mercado y permite interactuar con ellas y controlarlas.
Indicators\Position View.mq5Se encarga de mostrar las posiciones de mercado y permite interactuar con ellas y controlarlas.
Services\Market Replay.mq5Crea y mantiene el servicio de repetición y simulación de mercado. Es el archivo principal de todo el sistema.

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

Archivos adjuntos |
Anexo.zip (779.24 KB)
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 24): Herramienta de cuantificación de la acción del precio Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 24): Herramienta de cuantificación de la acción del precio
Los patrones de velas japonesas ofrecen información valiosa sobre los posibles movimientos del mercado. Algunas velas individuales señalan la continuación de la tendencia actual, mientras que otras presagian cambios de tendencia, dependiendo de su posición dentro de la acción del precio. Este artículo presenta un Asesor Experto (EA) que identifica automáticamente cuatro formaciones clave de velas japonesas. Explore las siguientes secciones para descubrir cómo esta herramienta puede mejorar su análisis de la evolución de los precios.
Del básico al intermedio: Sobrecarga de operadores (II) Del básico al intermedio: Sobrecarga de operadores (II)
Este artículo puede parecer bastante confuso al principio por el contenido que presentaré. Sin embargo, he intentado presentar todo de la forma más sencilla y didáctica posible. Espero que comprendas lo que voy a mostrar aquí y que te resulte útil en algún momento.
Del básico al intermedio: Sobrecarga de operadores (III) Del básico al intermedio: Sobrecarga de operadores (III)
En este artículo veremos cómo implementar la sobrecarga tanto de operadores lógicos como de operadores relacionales. Hacerlo requiere cierto cuidado y una buena dosis de atención. Un simple descuido al implementar la sobrecarga de estos operadores puede hacer que todo el código termine siendo completamente inservible. Si la sobrecarga presenta algún problema, toda una base de datos creada a partir de los resultados generados por el código deberá descartarse por completo o, como mínimo, revisarse íntegramente.
Simulación de mercado: Position View (XX) Simulación de mercado: Position View (XX)
En este artículo veremos cómo modificar el código del indicador de posición para crear una especie de sombra que nos permita visualizar dónde se encuentra actualmente el precio que continúa vigente en el servidor de trading. Este mecanismo pretende facilitar la planificación de operaciones en las que desplazamos las líneas de stop loss o take profit. Añadir esta función, es decir, las sombras de precio, puede parecer extremadamente complejo. En este artículo mostraré que puedes implementarla de una forma muy sencilla y práctica.