preview
Simulação de mercado: A união faz a força (I)

Simulação de mercado: A união faz a força (I)

MetaTrader 5Testador |
98 0
Daniel Jose
Daniel Jose

Introdução

Olá pessoal, e sejam bem-vindos a mais um artigo da série sobre como construir um sistema de replay/simulação.

No artigo anterior Simulação de mercado: Position View (XX), como você pode modificar o indicador de posição a fim de conseguir criar uma sombra no ponto onde o preço se encontrava. Bem, apesar de tudo, existe uma pequena falha naquela implementação. A falha acontece quando durante a movimentação, seja do take profit, seja do stop loss. Onde você em algum momento venha a decidir mudar a forma de visualizar o resultado da operação. Trocando de financeiro, para por exemplo, porcentagem. Apesar de alguns operadores desejarem fazer isto, durante a movimentação. Isto não pode ser de fato conseguido. Sendo necessário primeiro finalizar a movimentação para somente depois modificar a forma de visualizar os dados. Para assim voltar a pedir uma nova movimentação, tendo como base a forma de apresentação dos resultados como esperado.

Apesar da resolução de tal questão ser bastante simples. Pessoalmente acho que não se faz necessário, corrigir ou mesmo mostrar como fazer isto. Já que acredito que aqueles que estão acompanhando esta série de fato, já devam estar ansiosos para ver este sistema finalmente concluído. Por conta deste motivo. Aqui neste artigo, foi decidido, partir para a parte onde já vamos jogar as quatro aplicações para dentro do replay/simulador. Pois elas já se encontram suficientemente desenvolvidas para poderem ser utilizadas nele.

Obviamente ainda falta desenvolver o sistema de ordens pendentes. Porém como o sistema de ordens pendentes, faz um grande uso do indicador de posição. Bastando efetuar algumas poucas mudanças nele. Iremos criar o sistema de ordens pendentes, já fazendo isto dentro do replay/simulador.

Então, já que esta decisão foi tomada. Neste artigo, vamos fazer as devidas mudanças no indicador de posição a fim de começar a usar ele no replay/simulador. Pelo menos na sua forma mais básica. Então mãos à obra, e vamos a implementação das modificações.


Compreendendo o que temos até o momento

Para começar quero que você meu caro leitor. Veja como o código da classe C_ElementsTrade se encontra. Isto depois de ter sido feita a remoção de partes duplicadas que apareceram no artigo anterior. Este código pode ser visto na íntegra logo abaixo.

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

Acredito que você, não terá dificuldades em entender este código. Já que ele é exatamente o que foi visto até este momento. Porém, aqui ele se encontra sem todas aquelas duplicatas. Sendo que as partes duplicadas foram substituídas por macros. Assim, mesmo que para o compilador o código permaneça duplicado. Para nós que precisamos modificar, ou ler o código. O mesmo não estará de fato duplicado. Já que ao mudarmos o código na macro. Precisaremos fazer isto somente uma única vez.

Para que você entenda isto. Veja a linha 66, onde temos uma macro sendo definida. Está macro é de uso local. Sendo assim, na linha 101 a removemos. Porém quero chamar a sua atenção ao fato de estarmos usando a macro em dois pontos. Um na linha 90 e ou outro na linha 98. Então nestes pontos, o compilador simplesmente pegará o código da macro, e o irá replicar ali. Então para o compilador o código será duplicado. Mas para nós não. Já que se você desejar modificar a posição inicial na coordenada X, bastará mudar isto uma única vez. Ou seja, na linha 68. E isto irá se refletir tanto para a sombra, quanto para o segmento que esteja sendo mostrado. Simples assim.

Agora veja, que pelo motivo de termos feito a remoção de qualquer dependência, ao ativo, ou informação sobre algo do ativo. Não precisamos no preocupar com esta classe C_ElementsTrade. Isto para portar o indicador para dentro do sistema de replay/simulador. Sendo assim, agora nosso foco, será integralmente voltado para o código principal do indicador. Este pode ser visto na íntegra logo abaixo. Um detalhe, este código, é da versão anterior, vista até no artigo passado.

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 posição

Como você pode ver, aqui está todo o conjunto, onde as dependências existem. Este código foi explicado em detalhes nos artigos anteriores. Até o seu ponto atual. Pois bem, deste momento em diante. Tudo será feito aqui. Neste código mostrado acima. Isto para que possamos isolar, ou melhor dizendo, separar o replay/simulador do mercado físico. Mas mesmo que esta separação venha a ocorrer. Ainda assim iremos manter o indicador de posição capaz de funcionar em ambas situações. Sem que ele, indicador de posição, consiga diferenciar, se está no replay/simulador, ou em contato com o servidor real de negociação. Seja em uma conta demo ou conta real.

Então a primeira coisa a ser feita, é trazer o nome do ativo que será usado no replay/simulador. Isto para que comecemos a separar as coisas. Este nome está declarado no arquivo de cabeçalho Defines.mqh. Veja os artigos anteriores para mais detalhes. Então tudo que precisamos é usar a definição que existe no tal arquivo de cabeçalho. Este já se encontra incluído, por conta da linha 15 que pode ser vista no código principal.

Fazer isto à primeira vista, parece uma tarefa relativamente simples, não é mesmo? Porém ela é bem mais trabalhosa do que possa parecer. Isto por que todas as referências a PositionGetDouble, PositionGetInteger e PositionSelectByTicket. São pontos de bloqueio no uso do replay/simulador. E é justamente estes pontos que precisamos conseguir contornar. Isto para que o indicador de posição de fato possa ser usado no replay/simulador.

Assim, precisamos pensar em uma solução, para conseguir efetivamente começar a trabalhar. Neste ponto, muitos iriam simplesmente empacar, não conseguindo progredir ao próximo passo da implementação. Visto que parece ser algo impossível de ser feito. Mas será mesmo? Bem, vamos recordar uma coisa da qual eu expliquei a um tempo nesta mesma série de artigos. O uso do SQL ou mesmo a comunicação entre o MetaTrader 5 e o Excel. No momento em que expliquei sobre tais assuntos, talvez você, meu caro e estimado leitor. Pode ter imaginado que aquilo era completamente desnecessário. E por que motivo eu estaria explicando aquelas coisas em meio a uma série voltada a construir um replay/simulador. Pois bem, se você não estudou o assunto. Sugiro que procure dentro desta série de artigos, estudar os mesmos. Pois agora chegou a hora de usar aquele conhecimento.


Entendendo a escolha da solução

Por motivos práticos, e não necessariamente por dificuldades na programação. Iremos usar, aqui no replay/simulador a solução do SQL. Isto por conta que o MQL5, já conta com meios para trabalhar com o SQL. Isto sem precisar recorrer a programação externa. Lembre-se: O meu objetivo, é mostrar que podemos criar um replay/simulador, totalmente dentro do MQL5. Assim, se você precisar, ou desejar, pode adaptar as coisas de modo a usar o Excel junto ao MetaTrader 5. E assim ter as métricas de suas operações e estudos no replay/simulador. Mas aqui não irei mostrar como fazer isto. Iremos direto usar o SQL.

Mas neste ponto, pode surgir uma questão: Por que não usar um arquivo especial, seja de texto ou binário, para trabalhar na simulação do servidor. Não seria mais simples do que usar o SQL para isto? Bem, esta é de fato uma questão interessante. E sim, seria mais simples a princípio. Porém, pense no seguinte: Se as operações efetuadas no replay/simulador, estiverem em um arquivo especialmente feito apenas para isto. Teríamos um sobre trabalho, para caso fosse desejado, analisar posteriormente as operações. Porém, colocando tudo dentro do SQL. Este sobre trabalho simplesmente deixa de existir. Já que é muito mais fácil ler o conteúdo de um banco de dados SQL. Do que ter que converter o conteúdo de um arquivo, especialmente criado apenas para efetuar a mesma tarefa de armazenar as operações efetuadas no replay/simulador.

Então a questão aqui, não é na utilização no replay/simulador. E sim na simplicidade em ler o que foi feito dentro do replay/simulador. Este é o motivo de usarmos o SQL.

Pois bem, então a coisa começa a tomar um outro rumo. Já que o indicador de posição, não deve criar o arquivo SQL. Ele apenas deverá ler este arquivo. A tarefa de criar este arquivo, será delegada ao Expert Advisor. Este é que irá de fato, efetuar a simulação do servidor de negociação. Ao indicador de posição, restará apenas mostrar o que se encontra no banco de dados, que será criado pelo Expert Advisor.

Ok. Esta é a base inicial. Porém, existem algumas coisas a serem revistas, antes de começarmos. Isto por que, desde a última vez que o serviço de replay/simulador passou por alguma atualização. As classes usadas para dar suporte a ele, sofreram grandes mudanças. Este é o tipo problema, que torna a confecção de projetos maiores um pouco mais complicada. Muitos programadores iniciantes, costumam simplesmente desistir. Ou quando não desistem de um projeto, acabam entrando em uma ciranda em que não conseguem de fato retomar o projeto inicial.

Se você tentar fazer uso do serviço de replay/simulador, agora, depois de tanto tempo. Notará que o executável não estará funcionando como esperado. E se tentar compilar novamente o serviço, com base no antigo código. Perceberá que não será possível dar play no sistema. Ou se forçar um play, não conseguirá interagir com o indicador de controle. Isto por que existem uma série de coisas que estão desatualizadas no código do replay/simulador. Então antes de prosseguirmos precisamos atualizar o antigo código, presente no replay/simulador. Porém não existe motivo para pânico. A atualização será feita de uma maneira bastante simples e fácil de entender.


Atualizando o replay/simulador

A primeira das atualizações, deverá ser feita na classe C_DrawImage. Isto é visto no fragmento logo abaixo.

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

Esta atualização foi feita na linha 141, visando justamente definir o valor da propriedade OBJPROP_ZORDER. Já que sem fazer isto, os objetos criados por esta classe, terá o mesmo ZORDER da classe C_Mouse. E como esta classe é usada no indicador de controle. Precisamos elevar o valor da propriedade OBJPROP_ZORDER para que os cliques nos objetos, sejam de fato capturados na classe C_Controls.

A próxima atualização é um pouco mais trabalhosa de ser feita. Isto por conta de que ela será feita na classe C_Controls. Apesar de muitos de vocês, imaginarem que não precisamos efetuar tais atualizações. A verdade é que, o próprio MetaTrader 5, passou por diversas atualizações e melhorias. E estas trouxeram uma melhor estabilidade para o programa. Além de melhorar diversas outras coisas no decorrer de todo este tempo. Então de fato, precisamos atualizar o sistema de replay/simulador. Mas acredito que esta será a última vez em que precisaremos fazer isto. O novo código da classe C_Controls pode ser visto logo abaixo. Já atualizado.

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

Não vou entrar em muitos detalhes sobre as mudanças. Isto fica como tema de casa. Para que você estude e tente perceber o que de fato mudou no código. Porém, vou dar uma dica. Olhe o constructor, que se encontra presente na linha 138. Assim como também a declaração da classe que está presente na linha 27. Elas mudaram. Assim como alguns poucos pontos dentro do código. Mas tudo está ligado ao motivo da herança ter sido modificada. Já que o constructor mudou, com toda a certeza também mudou o código principal. Este é visto logo abaixo. Apenas o fragmento que de fato precisou mudar.

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 do Indicador de controle

Note que apenas a linha 23 precisou de fato mudar. Clara que também mudei a versão, que é informada na linha seis. Assim como o link para este artigo que é informado na linha sete. Com isto já temos novamente o serviço de replay/simulador atualizado e pronto para a próxima etapa. Onde iremos adicionar o indicador de posição ao sistema de replay/simulador.

Mas como esta atualização, acabou sendo bem proveitosa. Foi notado que também poderia ser feita uma outra atualização. Está agora no Indicador de mouse. Porém esta atualização é opcional. Ou seja, você caso não deseje efetuar a mesma. Não é forçado a fazer isto. Mas o código já compilado que estará nos anexos, deste e dos próximos artigos, já irá conter a atualização vista logo abaixo. Note que são três arquivos. E todos eles estão na íntegra, já que não entrarei em detalhes de onde foi feita a atualização.

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

Com isto finalizamos completamente a atualização do sistema. E finalmente podemos começar a trabalhar no sistema de replay / simulador, de forma a usar o indicador de posição dentro do ativo customizado. Este ativo customizado, do qual estou me referindo, é justamente o usado pelo replay / simulador.


Atualizando o código do Indicador de posição

Muito bem, agora que já atualizamos todo os sistemas, podemos começar a tornar o código do indicador de posição adequado para ser usado no replay/simulador. Para fazer isto iremos inicialmente assumir que não temos nenhuma noção de como o banco de dados em SQL será criado. Então, inicialmente o novo código é visto logo abaixo, na íntegra.

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 Posição

Eu sei que parece loucura da minha parte, colocar todo o código novamente na íntegra. Mas quero que você, meu caro e estimado leitor, perceba o que estou fazendo. Pois é algo de extrema importância e que pode vir a lhe ajudar em algum momento no futuro. Preste atenção a este código acima. Note que adicionei três novas funções a ele. Estas funções, tem como objetivo, permitir que criemos tudo que for preciso ser feito no Expert Advisor. Isto para que o Expert Advisor, monte um banco de dados com as posições que irão surgir. Isto conforme o replay/simulador for sendo executado e você usando o indicador Chart Trade e o Indicador de mouse, peça para o Expert Advisor, comprar ou vender. A interação restante será conseguida, quando o Expert Advisor emitir eventos customizados, cujo objetivo será a atualização, colocação ou remoção do indicador de posição do gráfico. Isto dentro do ativo de replay/simulador.

Ok. Então a ideia básica destas três funções, que podem ser vistas nas linhas 33, 41 e 49 é justamente esta. Fazer com que o indicador de posição funcione da mesma maneira em qualquer tipo de cenário. Assim, se estivermos em contato com o servidor real de negociação. Seja em conta demo ou conta real. Os testes nas linhas 35, 43 e 51, permitirão que as funções originais, presentes no MQL5, capture e atualize os valores presentes na posição. A fim de que os dados sejam corretamente apresentados no gráfico. E não pensem que me esqueci da PositionGetString, pois não me esqueci dela. Olhe na linha 69 e você perceberá como resolvi o problema. Como é algo muito simples não precisou de um procedimento extra.

Esta é a primeira parte do que precisamos. Agora observe que o nome das funções das linhas mencionadas, são justamente muito parecidos com os nomes usados na biblioteca do MQL5. Isto é proposital. Já que ao ler o código, você notará que aparentemente não existe nenhuma diferença nele. Mas se prestar atenção, verá que os nomes das funções, cujo objetivo e capturar os dados da posição. É ligeiramente diferente, devido ao primeiro carácter no nome da função. Fazendo desta forma, conseguimos limitar todo o trabalho a ser feito, a apenas codificar o que se encontra dentro das funções modificadas.

Naturalmente, esta forma de codificar, seria o que muitos entenderiam como sobrecarga. Porém aqui não estamos de fato sobrecarregando as funções. Já que o nome é ligeiramente diferente.

Então no que precisávamos fazer no indicador de posição. Já foi feito. Agora devemos passar ao Expert Advisor. Pois é lá que a coisa realmente será feita de uma maneira um tanto quanto mais profunda.


A união faz a força

Agora vamos começar a focar por um por algum período no Expert Advisor. Como sei que muitos de vocês, devido aos comentários, estão acompanhando esta série de artigos, desejando usar o seu próprio Expert Advisor. Vou mostrar a você, meu estimado leitor, como integrar o seu código, ao que iremos de fato desenvolver aqui. Isto a fim de que o replay / simulador, consiga lhe ser de alguma utilidade prática. Já que não faz sentido você ficar preso apenas as aplicações que estou explicando. Assim sendo, não iremos colocar o código diretamente no Expert Advisor. Iremos de fato criar um arquivo de cabeçalho para isto. Se bem que talvez venha a ser necessário mais de um.

No entanto, neste primeiro momento, vamos criar um que irá nos fornecer o suporte inicial para trabalhar com o SQLite. Já expliquei como fazer parte das coisas em outros artigos. Mas aqui faremos algo bem mais específico. O código inicial já foi explicado em outro artigo, onde falei sobre SQL, dentro desta mesma sequência. Então não irei explicar novamente ele aqui. Apenas vou mostrar como ele se encontra inicialmente. Isto pode ser visto na íntegra logo abaixo.

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

Apesar deste código, ser levemente diferente do original. Ele trabalha da mesma maneira. Porém ele é um pouco mais simples, devido ao fato de que aqui estamos criando algo específico. Diferente do que foi feito no passado. Mas tudo que foi explicado e dito a respeito deste código, no artigo onde ele aparece, continua valendo. A única real diferença que existe aqui, é a linha quatro, onde definimos o nome do arquivo de banco de dados.

Muito bem, já podemos então começar a usar um banco de dados SQLite. Agora precisamos preparar o Expert Advisor para que ele possa criar os dados que serão usados pelo indicador de posição. Mas antes de realmente começarmos a fazer as coisas vamos entender alguns detalhes aqui. Para isto veja o esqueleto, em forma de fragmento, do Expert Advisor que vamos usar. O código pode ser visto abaixo.

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 do Expert Advisor

Aqui meu caro leitor, quero que você observe alguns detalhes. Note que na linha 11 estamos adicionando o arquivo de cabeçalho, visto a pouco. Este tem como objetivo permitir que usemos o banco de dados SQLite. Na linha 19, criamos um ponteiro para poder usar a classe de acesso ao banco de dados. Na linha 23, dizemos quando o banco de dados será utilizado. Ou no caso, quando a variável ponteiro, irá receber permissão de acesso a classe do banco de dados. Já na linha 113 removemos o ponteiro da memória. Basicamente será isto. Mas aqui temos algumas coisas, que serão diferentes em relação a forma de usar o Expert Advisor. Isto quando ele estiver em contato com o servidor de negociação real, seja em conta demo ou conta real. E quando ele estiver sendo usado no replay/simulador.

Todo o processo de acessar os dados da posição serão feitos no procedimento OnTradeTransaction. Isto quando o Expert Advisor estiver em contato com o servidor real. Já quando estiver usando o replay/simulação. Não teremos esta mesma forma de trabalhar.

E é justamente aqui que temos o tal grande detalhe a ser resolvido e pensado. Até dá para colocar todo o peso sobre as costas do Expert Advisor. Isto quando estivermos usando o replay/simulador. Mas será que não haveria uma forma um tanto quanto mais simples de fazer as coisas? Isto por que, quando clicarmos em um dos botões do Chart Trade, um evento customizado será disparado. Este evento, será justamente capturado pela linha 35, do código acima. Fazendo com que a classe C_Orders, de fato execute o pedido. Porém apenas enviar o pedido não fará com que o indicador de posição venha a de fato surgir ou ser atualizado. Quem efetua esta tarefa é justamente o procedimento OnTradeTransaction. E por este motivo, temos dois tipos de cenário. Um onde estamos em contato com o servidor real e outro onde não temos contato.

Porém, se limitarmos o funcionamento da linha 35, de forma que o Expert Advisor, não permita que a classe C_Orders receba eventos. Isto quando estivermos usando o replay / simulador. Tiramos parte do peso das costas do Expert Advisor. Isto por que, poderíamos simplesmente simular uma resposta do servidor. Como se de fato, simularíamos o evento que o MetaTrader 5 dispara quando recebe alguma informação do servidor. Este evento faz com que OnTradeTransaction venha a ser executada. Fazendo assim com que o indicador de posição possa ser colocado, atualizado, ou removido do gráfico.

Lembre-se do seguinte fato. Quando estivermos usando o replay/simulador. As informações que serão de fato usadas pelo indicador de posição, estarão no banco de dados. Assim garantimos um perfeito funcionamento em qualquer caso. Simulando não apenas a movimentação de preço, como já estamos fazendo. Mas também iremos simular o disparo de eventos que seria feito pelo MetaTrader 5.

Se bem que ainda teremos um outro problema para ser resolvido ainda. Mas isto ficar para um outro momento. A primeira coisa a ser feita será conseguir forçar o Expert Advisor a fazer a simulação de um evento OnTradeTransaction. Sei que isto parece ser algo extremamente complexo de ser feito. Mas você verá que não é bem assim. E que seguindo este caminho precisaremos implementar muito menos código. E teremos um Expert Advisor bem mais estável e confiável. Já que ele tanto conseguirá lidar da mesma forma com eventos vindos do servidor real. Como também do sistema de replay/simulação.


Considerações finais

Como você acabou de ler neste último tópico. A coisa está chegando aos finalmente. Estamos quase com o desenvolvimento do replay/simulador concluído. É bem verdade que ainda precisaremos fazer algumas poucas coisas. Mas frente a tudo que realmente já foi feito. Implementar o que falta será moleza. Mas como tudo que foi mostrado neste artigo, precisa ser adequadamente digerido e compreendido. Quero que você, meu caro leitor e entusiasta. Comece a pensar em formas de adaptar o seu Expert Advisor ao que será apresentado nos próximos artigos. Isto para que você, possa fazer com que o seu código, se integre perfeitamente ao que será visto e implementado.

Não sei de fato qual é o nível de conhecimento, ou habilidade de cada um em MQL5. Mas não espere que as coisas venham a se acumular em demasia. Antes de realmente começar a pensar em formas de adaptar seu código ao que será implementado nesta série de artigos. Pois se você ficar esperando muito, acabará perdendo o fio da meada e quando for tentar fazer a integração, terá no mínimo uma grande dificuldade. Ainda mais pelo fato, de que sei, que muitos gostam de usar determinadas formas de programar as coisas. Mas aqui não estou disposto a tentar criar uma solução que sirva a todos. Quero sim, mostrar como podemos criar todo um sistema de replay/simulação. Onde as aplicações criadas para tal propósito, venham a também poderem ser usadas em uma conta demo ou conta real. Então se prepare. Pois vem chumbo grosso nos próximos artigos, onde finalizaremos o desenvolvimento do replay/simulador.

ArquivoDescrição
Experts\Expert Advisor.mq5
Demonstra a interação entre o Chart Trade e o Expert Advisor (É necessário o Mouse Study para interação)
Indicators\Chart Trade.mq5Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação)
Indicators\Market Replay.mq5Cria os controles para interação com o serviço de replay/simulador (É necessário o Mouse Study para interação)
Indicators\Mouse Study.mq5Permite interação entre os controles gráficos e o usuário (Necessário tanto para operar o replay simulador, quanto no mercado real)
Indicators\Order Indicator.mq5Responsável pela indicação de ordens de mercado, permitindo interação e controle das mesmas
Indicators\Position View.mq5Responsável pela indicação de posições de mercado, permitindo interação e controle das mesmas
Services\Market Replay.mq5Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema)
Arquivos anexados |
Anexo.zip (779.24 KB)
Gerenciamento de riscos (Parte 3): Criação da classe principal de gerenciamento de riscos Gerenciamento de riscos (Parte 3): Criação da classe principal de gerenciamento de riscos
Neste artigo começaremos a criação da classe principal de gerenciamento de riscos, que será o elemento chave para o controle de riscos no sistema. Vamos nos concentrar na construção das bases, na definição das principais estruturas, variáveis e funções. Além disso, implementaremos os métodos necessários para atribuir valores de lucro máximo e prejuízo máximo, estabelecendo assim o alicerce do gerenciamento de riscos.
Do básico ao intermediário: Sobrecarga de operadores (II) Do básico ao intermediário: Sobrecarga de operadores (II)
Este será um artigo que a principio irá parecer bem confuso devido ao que será mostrado nele. Porém tentei deixar as coisas o mais simples e didáticas quanto foi possível ser feito. Espero que você consiga compreender o que estará sendo demonstrando neste artigo. E que isto venha a lhe ser útil em algum momento.
Automatizando Estratégias de Negociação em MQL5 (Parte 3): O Sistema Zone Recovery RSI para Gestão Dinâmica de Operações Automatizando Estratégias de Negociação em MQL5 (Parte 3): O Sistema Zone Recovery RSI para Gestão Dinâmica de Operações
Neste artigo, criamos um Sistema EA Zone Recovery RSI em MQL5, utilizando sinais de RSI para acionar operações e uma estratégia de recuperação para gerenciar perdas. Implementamos uma classe "ZoneRecovery" para automatizar as entradas de operações, a lógica de recuperação e o gerenciamento de posições. O artigo conclui com insights de backtesting para otimizar a performance e aprimorar a eficácia do EA.
Gerenciamento de riscos (Parte 2): Implementação do cálculo de lotes na interface gráfica Gerenciamento de riscos (Parte 2): Implementação do cálculo de lotes na interface gráfica
Neste artigo, analisaremos como aprimorar e aplicar de forma mais eficiente os conceitos apresentados no artigo anterior, utilizando as poderosas bibliotecas de elementos gráficos de controle do MQL5. Conduzirei você passo a passo pelo processo de criação de uma interface gráfica totalmente funcional, explicando o plano de projeto subjacente, bem como o propósito e o princípio de funcionamento de cada método empregado. Além disso, ao final do artigo testaremos o painel criado, a fim de confirmar seu correto funcionamento e sua aderência aos objetivos estabelecidos.