Simulação de mercado: Position View (XVII)
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 (XVI), fizemos com que o indicador de posição, nos mostrasse o resultado financeiro de uma posição aberta. Porém, nem todos operadores de fato gostam de fazer uso de tal modo de visualização. O motivo pode variar de operador para operador. Mas em alguns casos o motivo de fato me parece bastante plausível e justificável. Evitar ficar com um nível elevado de ganância ou medo, durante uma operação. Isto por que ao observar valores financeiros, alguns operadores ficam com medo de perder ainda mais, ou mesmo perder o dinheiro que já conseguiram ganhar do mercado. E este tipo de coisa realmente atrapalha bastante em alguns momentos. Tanto que alguns operadores simplesmente, se retiram e evitam olhar uma operação que esteja em aberto. Justamente para evitar a tentação de encerrar antecipadamente ou prematuramente alguma operação.
Apesar desta atitude ser justificável. Ela de fato não é adequada, isto por conta que alguns ativos podem vir a ter momentos de grande volatilidade. E estes momentos fazem com que as linhas de limite, que são mais conhecidas como Stop Loss e Take Profit. Não sejam de fato respeitadas. Fazendo com que uma operação venha a apresentar resultados não esperados. No caso de um Take maior do que o esperado, não vejo ninguém reclamando em mídias sociais ou outras plataformas de comunicação. Porém, quando a perda é maior do que a planejada. Todos começam a xingar e falar mal de tudo que existe, ou possa vir a existir. Porém a falha nestes casos é apenas e somente do operador. Já que este devia estar atento pelo fato de estar com uma posição aberta em um momento de grande volatilidade.
Bem, mas não estou aqui para discutir tais méritos. Apenas queria apresentar os motivos pelos quais, devemos promover, diferentes métodos de visualizar dados de uma posição. Isto por que, pode acontecer do operador não se sentir confortável em usar um ou outro método.
Fazer as atualizações no código para promover isto. Não é nem de longe uma das tarefas mais complicadas. Na verdade é algo bastante simples e singelo. Porém, ao mesmo tempo, será algo interessante de explicar. Podendo ser de grande valia, para aqueles entusiastas, que estão fazendo uso destes artigos. Isto usando eles como material de apoio, para poder estudar e aprender um pouco mais sobre MQL5. Então vamos começar, indo direto ao ponto.
Criando um sistema de trocas
Muito bem, antes de começarmos vamos ver o código da classe C_ElementsTrade. Isto para que você possa se localizar no atual momento do código. Este 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. //+------------------------------------------------------------------+ 011. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 012. //+------------------------------------------------------------------+ 013. #define def_PathBtns "Images\\Market Replay\\Orders\\" 014. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 015. #resource "\\" + def_Btn_Close; 016. //+------------------------------------------------------------------+ 017. #include "..\Auxiliar\C_Mouse.mqh" 018. //+------------------------------------------------------------------+ 019. #ifdef def_FontName 020. "Why are you trying to do this?" 021. #else 022. #define def_FontName "Lucida Console" 023. #define def_FontSize 10 024. #endif 025. //+------------------------------------------------------------------+ 026. class C_ElementsTrade : private C_Mouse 027. { 028. private : 029. //+------------------------------------------------------------------+ 030. struct st00 031. { 032. struct st_01 033. { 034. short Width, 035. Height, 036. digits; 037. }Text; 038. ulong ticket; 039. string szPrefixName, 040. szDescr; 041. EnumEvents ev; 042. double price, 043. open, 044. volume, 045. var; 046. bool bClick, 047. bIsBuy; 048. char weight; 049. color _color; 050. int sizeText; 051. }m_Info; 052. //+------------------------------------------------------------------+ 053. short UpdateViewPort(const double price, short size = 0, uint ui = 0) 054. { 055. static short _SizeControls; 056. static short _Width; 057. uint x, y; 058. 059. if (size > 0) 060. { 061. size += (short)(ui + 8); 062. _SizeControls = (_SizeControls > size ? _SizeControls : size); 063. size = (short)(_SizeControls - ui - 12); 064. _Width = (_Width > size ? _Width : size); 065. }else 066. { 067. ChartTimePriceToXY(0, 0, 0, price, x, y); 068. x = 125 + (m_Info.ev == evMsgClosePositionEA ? 0 : (m_Info.ev == evMsgCloseTakeProfit ? _SizeControls : (_SizeControls * 2))); 069. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 070. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 071. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 072. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 073. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XDISTANCE, x + 10); 074. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2)); 075. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + _Width + 20); 076. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 077. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + _Width + 20); 078. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 079. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XDISTANCE, x - 10); 080. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); 081. } 082. return _Width; 083. } 084. //+------------------------------------------------------------------+ 085. inline void CreateLinePrice(void) 086. { 087. string szObj; 088. 089. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 090. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 091. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 092. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 093. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 094. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 095. macro_LineInFocus(false); 096. CreateObjectGraphics(szObj = def_NameBackGround, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 097. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 098. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 099. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 100. } 101. //+------------------------------------------------------------------+ 102. inline void CreateBoxInfo(const bool bMove) 103. { 104. string szObj; 105. const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0}; 106. 107. CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 108. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 109. ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c)); 110. ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? (m_Info.ev == evMsgCloseTakeProfit ? clrDarkGreen : clrMaroon) : (m_Info.bIsBuy ? clrDarkGreen : clrMaroon))); 111. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15)); 112. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 113. } 114. //+------------------------------------------------------------------+ 115. inline void CreateObjectInfoText(void) 116. { 117. string szObj; 118. 119. CreateObjectGraphics(szObj = def_NameObjLabel, OBJ_EDIT, clrNONE, (EnumPriority)(ePriorityDefault)); 120. ObjectSetString(0, szObj, OBJPROP_FONT, def_FontName); 121. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, def_FontSize); 122. ObjectSetInteger(0, szObj, OBJPROP_COLOR, clrBlack); 123. ObjectSetInteger(0, szObj, OBJPROP_BORDER_COLOR, m_Info._color); 124. ObjectSetInteger(0, szObj, OBJPROP_ALIGN, ALIGN_CENTER); 125. ObjectSetInteger(0, szObj, OBJPROP_READONLY, true); 126. } 127. //+------------------------------------------------------------------+ 128. inline void CreateButtonClose(void) 129. { 130. string szObj; 131. 132. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 133. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 134. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 135. } 136. //+------------------------------------------------------------------+ 137. inline void AdjustDinamic(const string szTxt) 138. { 139. uint w, h; 140. 141. TextSetFont(def_FontName, def_FontSize * -10); 142. TextGetSize(szTxt, w, h); 143. m_Info.Text.Height = (uchar) h + 4; 144. m_Info.Text.Width = (uchar) w + 4; 145. m_Info.Text.Width = UpdateViewPort(0, m_Info.Text.Width, h = 32); 146. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XSIZE, m_Info.Text.Width); 147. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YSIZE, m_Info.Text.Height); 148. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XSIZE, m_Info.Text.Width + h + (m_Info.ev == evMsgClosePositionEA ? 8 : 0)); 149. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YSIZE, m_Info.Text.Height + 5); 150. } 151. //+------------------------------------------------------------------+ 152. public : 153. //+------------------------------------------------------------------+ 154. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, char digits, string szDescr = "\n", const bool IsBuy = true) 155. :C_Mouse(0, "") 156. { 157. ZeroMemory(m_Info); 158. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 159. m_Info._color = _color; 160. m_Info.szDescr = szDescr; 161. m_Info.bIsBuy = IsBuy; 162. m_Info.Text.digits = digits; 163. } 164. //+------------------------------------------------------------------+ 165. ~C_ElementsTrade() 166. { 167. ObjectsDeleteAll(0, m_Info.szPrefixName); 168. } 169. //+------------------------------------------------------------------+ 170. inline void UpdatePrice(const double open, const double price, const double vol = 0, const double var = 0) 171. { 172. m_Info.sizeText = 0; 173. ObjectsDeleteAll(0, m_Info.szPrefixName); 174. m_Info.volume = (vol > 0 ? vol : m_Info.volume); 175. m_Info.var = (var > 0 ? var : m_Info.var); 176. if (price > 0) 177. { 178. CreateLinePrice(); 179. CreateButtonClose(); 180. CreateObjectInfoText(); 181. } 182. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 183. m_Info.open = open; 184. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 185. if (m_Info.ev != evMsgClosePositionEA) 186. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 187. } 188. //+------------------------------------------------------------------+ 189. void ViewValue(const double profit) 190. { 191. string szTxt; 192. 193. szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f", ((profit < 0 ? -(profit) : profit) / m_Info.var) * m_Info.volume); 194. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt); 195. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 196. if (StringLen(szTxt) != m_Info.sizeText) 197. { 198. AdjustDinamic(szTxt); 199. m_Info.sizeText = StringLen(szTxt); 200. } 201. } 202. //+------------------------------------------------------------------+ 203. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 204. { 205. string sz0; 206. long _lparam = lparam; 207. double _dparam = dparam; 208. 209. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 210. switch (id) 211. { 212. case (CHARTEVENT_KEYDOWN): 213. if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break; 214. _lparam = (long) m_Info.ticket; 215. _dparam = 0; 216. EventChartCustom(0, evUpdate_Position, _lparam, 0, ""); 217. case CHARTEVENT_CUSTOM + evMsgSetFocus: 218. if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)) 219. UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price); 220. macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)); 221. EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, ""); 222. m_Info.bClick = false; 223. case CHARTEVENT_CHART_CHANGE: 224. UpdateViewPort(m_Info.price); 225. if (m_Info.ev != evMsgClosePositionEA) 226. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 227. break; 228. case CHARTEVENT_OBJECT_CLICK: 229. sz0 = GetPositionsMouse().szObjNameClick; 230. if (m_Info.bClick) 231. { 232. if (sz0 == def_NameBtnMove) 233. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, ""); 234. if (sz0 == def_NameBtnClose) 235. EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, PositionGetDouble(m_Info.ev == evMsgCloseTakeProfit ? POSITION_SL : POSITION_TP), PositionGetString(POSITION_SYMBOL)); 236. } 237. m_Info.bClick = false; 238. break; 239. case CHARTEVENT_MOUSE_MOVE: 240. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 241. if (m_Info.weight > 1) 242. { 243. UpdateViewPort(_dparam = GetPositionsMouse().Position.Price); 244. if (m_Info.ev != evMsgClosePositionEA) 245. ViewValue(m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam); 246. if (m_Info.bClick) 247. { 248. if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss)) 249. EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 250. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 251. } 252. } 253. break; 254. } 255. } 256. //+------------------------------------------------------------------+ 257. }; 258. //+------------------------------------------------------------------+ 259. #undef macro_LineInFocus 260. //+------------------------------------------------------------------+ 261. #undef def_Btn_Close 262. #undef def_PathBtns 263. #undef def_FontName 264. #undef def_FontSize 265. //+------------------------------------------------------------------+ 266. #undef def_NameBackGround 267. #undef def_NameObjLabel 268. #undef def_NameInfoDirect 269. #undef def_NameBtnMove 270. #undef def_NameBtnClose 271. #undef def_NameHLine 272. //+------------------------------------------------------------------+
C_ElementsTrade
Mas por que estou mostrando o código na íntegra? Bem o motivo é que devido ao nível de complexidade, para compilar todo o sistema. Mas estou mostrando todas as modificações que o código vem sofrendo. Isto para que aqueles que desejam estudar o projeto, ou mesmo modificar o mesmo. Consigam ter acesso ao código. Como se trata de um projeto grande, com diversos arquivos e aplicações separadas que precisam trabalhar em conjunto. Muitos poderiam não conseguir de fato entender tal coisa. Assim estou disponibilizando, de forma anexada ao artigo, apenas os executáveis. Mas quem esteja acompanhando os artigos, e deseja de fato o código fonte. Terá ele na íntegra, apenas seguindo o que venho mostrando. Artigo por artigo.
Bem, dada as explicações. Vamos começar, fazendo uma pequena mudança. Ou melhor, adicionando algo a este código. Tão modificação pode ser vista no fragmento abaixo.
25. //+------------------------------------------------------------------+ 26. class C_ElementsTrade : private C_Mouse 27. { 28. private : 29. //+------------------------------------------------------------------+ 30. struct stInfos 31. { 32. struct st_01 33. { 34. short Width, 35. Height, 36. digits; 37. }Text; 38. ulong ticket; 39. string szPrefixName, 40. szDescr; 41. EnumEvents ev; 42. double price, 43. open, 44. volume, 45. var, 46. tickSize; 47. bool bClick, 48. bIsBuy; 49. char weight; 50. color _color; 51. int sizeText; 52. enum {eValue, eFinance, eTicks, ePercentage} ViewMode; 53. }m_Info; 54. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Observe que na linha 30, o nome da estrutura foi modificado. E o mais curioso é a linha 51. Este tipo de coisa, que pode ser vista na linha 51, pode parecer extravagante, e até mesmo uma alucinação. Já que não é algo que você, meu caro leitor, provavelmente já deve ter visto anteriormente. Neste momento, vamos entender um pouco mais, sobre esta linha 52. No decorrer do artigo, este código ficará mais claro. E você entenderá o motivo de ele, ter sido criado assim. Primeiramente temos uma enumeração. Mas uma enumeração sem nome. Isto pode parecer estranho. Mas já que no final estamos informando uma variável que receberá a enumeração. Não teremos problemas em usar os valores dentro da enumeração. No entanto, talvez o que mais possa de fato gerar dúvidas é: Por que esta enumeração está dentro da estrutura? E que tipo de implicação isto trará ao código?
Bem, o fato de que esta enumeração esteja dentro da estrutura, a tornará exclusiva da estrutura. Este tipo de coisa, nos permite criar enumerações que possam ter valores parecidos em termos de nomes. A maior dificuldade de fato em se programar algo. Não é a programação em si. Mas sim dar nome aos bois. Ou seja, você pode muito bem acabar tendo mais problemas em se lembrar, o que uma variável com um nome do tipo: value_001 significa, do que perceber o que uma variável com o nome: Price_01 significa. E observe que o nome da variável na linha 52, parece ser algo simples de lembrar o significado. Mas comece a programar e implementar as coisas e você logo perceberá que este nome pode não ser tão significativo. Assim como os valores dentro da enumeração. Mas pelo fato, de que a enumeração está dentro da estrutura. Posso dar algum tipo de significado a ela. Assim a variável na linha 52, passa a ter algum tipo de significado especial.
Uma vez feito isto, precisamos modificar um detalhe no código principal. Então para mostrar o que será modificado, vamos ver o fragmento abaixo.
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 (ObjectFind(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. //+------------------------------------------------------------------+ . . . 084. //+------------------------------------------------------------------+ 085. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 086. { 087. double volume; 088. 089. if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam); 090. if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam); 091. if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam); 092. switch (id) 093. { 094. case CHARTEVENT_CUSTOM + evUpdate_Position: 095. if (lparam != m_Infos.ticket) break; 096. if (!PositionSelectByTicket(m_Infos.ticket)) 097. { 098. ChartIndicatorDelete(0, 0, m_Infos.szShortName); 099. return; 100. }; 101. if (Open == NULL) Open = new C_ElementsTrade(m_Infos.ticket, evMsgClosePositionEA, clrRoyalBlue, m_Infos.digits, m_Infos.tickSize, StringFormat("%I64u : Position opening price.", m_Infos.ticket), m_Infos.bIsBuy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)); 102. if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, clrForestGreen, m_Infos.digits, m_Infos.tickSize, StringFormat("%I64u : Take Profit price.", m_Infos.ticket), m_Infos.bIsBuy); 103. if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, clrFireBrick, m_Infos.digits, m_Infos.tickSize, StringFormat("%I64u : Stop Loss price.", m_Infos.ticket), m_Infos.bIsBuy); 104. volume = PositionGetDouble(POSITION_VOLUME); 105. (*Open).UpdatePrice(0, m_Infos.priceOpen = PositionGetDouble(POSITION_PRICE_OPEN), volume, m_Infos.var); 106. (*Take).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_TP), volume, m_Infos.var); 107. (*Stop).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_SL), volume, m_Infos.var); 108. ProfitNow(); 109. break; 110. } 111. ChartRedraw(); 112. }; 113. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Observe que na linha 26, adicionamos uma nova variável. Esta recebe um valor na linha 47. Estamos fazendo isto aqui, para evitar fazer isto de maneira desnecessária na classe C_ElementsTrade. Bem, este valor será repassado para a classe. Isto é feito nas linhas 101 a 103. Este valor servirá para nos dar suporte ao formato, onde apresentaremos a quantidade de ticks no momento. Não confunda isto com valores em termos de pontos. Pois é diferente.
Bem, como você já deve imaginar, precisamos modificar o constructor da classe. Esta modificação é vista no fragmento abaixo.
155. //+------------------------------------------------------------------+ 156. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, char digits, double ticksize, string szDescr = "\n", const bool IsBuy = true) 157. :C_Mouse(0, "") 158. { 159. ZeroMemory(m_Info); 160. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 161. m_Info._color = _color; 162. m_Info.szDescr = szDescr; 163. m_Info.bIsBuy = IsBuy; 164. m_Info.Text.digits = digits; 165. m_Info.tickSize = ticksize; 166. } 167. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Observe que as mudanças são bem simples. Então não vou dar nenhum destaque aqui. Assim podemos partir direto para o procedimento, que cria os valores a serem mostrados. E o modificar, para que a formatação dos valores aconteça. Ou seja, vamos para o procedimento ViewValue, e ver seu novo código. Este pode ser visto no fragmento abaixo.
191. //+------------------------------------------------------------------+ 192. void ViewValue(const double profit) 193. { 194. string szTxt; 195. 196. switch (m_Info.ViewMode) 197. { 198. case stInfos::eValue: 199. szTxt = StringFormat("%." + (string)m_Info.Text.digits + "f ", MathAbs(profit)); 200. break; 201. case stInfos::eFinance: 202. szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f ", (MathAbs(profit) / m_Info.var) * m_Info.volume); 203. break; 204. case stInfos::eTicks: 205. szTxt = StringFormat(" %d ", (uint)MathRound(MathAbs(profit) / m_Info.tickSize)); 206. break; 207. case stInfos::ePercentage: 208. szTxt = StringFormat(" %.2f%% ", NormalizeDouble((MathAbs(profit) / (m_Info.open ? m_Info.open : m_Info.price)) * 100, 2)); 209. break; 210. } 211. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt); 212. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 213. if (StringLen(szTxt) != m_Info.sizeText) 214. { 215. AdjustDinamic(szTxt); 216. m_Info.sizeText = StringLen(szTxt); 217. } 218. } 219. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Aqui temos algo realmente, um código muito bonito. Isto dado o seu nível de simplicidade, frente ao que conseguimos fazer. Observe que na linha 194, iniciamos um chaveamento entre os modos declarados na linha 52. Por padrão, assim que o constructor executar, teremos o modo zero entrando em ação. Se você deseja usar o financeiro como modo padrão, coloque ele como sendo o primeiro valor na enumeração da linha 52.
Muito bem, agora note que nas linhas 198, 201, 204 e 207, temos as checagens a fim de encontrar a chave correta para formatar o valor a ser apresentado. Veja, que não é suficiente apenas dizer o valor da enumeração. Precisamos também informar o nome da estrutura, seguido de um conjunto duplo de dois pontos ( :: ). Este é um símbolo para o solucionador de escopo. Assim o compilador conseguirá entender, de onde vem o valor da enumeração. Se você tivesse diversas enumerações com nomes parecidos. As coisas ficariam muito confusas. Mas como estamos usando uma modelagem um pouco melhor. Fica mais simples encontrar a enumeração correta. Por isto que mudei o nome da estrutura.
Muito bem, este código visto acima, resolvemos totalmente o nosso problema, em apresentar os valores em formatos diferentes. Mas ainda temos um pequeno problema. Como permitir ao operador, ou usuário possa trocar as informações que serão impressas? Isto de maneira o mais simples possível. Já que não queremos complicar a nossa vida em termos de programação. E muito menos queremos que o usuário fique confuso tentando ler as informações apresentadas.
Bem, existem diversas formas de implementar isto. Algumas mais simples, outras um pouco mais complicadas. Porém, você deve sempre se lembrar do seguinte: Para o usuário o mais simples é clicar em algo. Mas para nós que estamos programando, o mais simples seria, que o usuário trocasse algum valor, que indicaríamos como parâmetro de entrada do indicador.
Estes tais parâmetros que estou dizendo, podem ser visto na imagem abaixo.

Fazer uso do que está sendo mostrado na imagem acima. É de longe, a maneira mais fácil de programar as coisas. Porém para o operador, ou usuário. Isto não é muito intuitivo. Ou pior, não é tão adequado. Já que ele precisaria abrir o indicador e trocar alguma informação. Para que as coisas viessem a acontecer. Porém para o usuário, clicar em algum ponto do indicador, ou objeto no gráfico é perfeitamente intuitivo. Então vamos fazer assim. Mesmo que isto torne o nosso trabalho, como programador um pouco maior.
Bem, pensando desta forma, agora teremos alguns novos problemas e decisões a serem tomadas. Uma destas decisões, tem reflexo quando o indicador de posição for usado em contas HEDGING. Já que neste caso poderemos ter mais de um indicador de posição no gráfico. Outra decisão, reflete como as coisas deverão correr. Para que você entenda melhor esta segunda questão. Já que a primeira será resolvida durante a implementação. Suponhamos que o indicador, esteja no modo valor.
E o usuário queira visualizar o take profit como financeiro. Ok, ao clicar no take profit, apenas ele irá mudar para o financeiro, ou os demais valores, que neste caso são o preço de abertura e o stop loss, também mudarão? E se o usuário desejar ver o valor financeiro no take profit. O valor percentual no preço de abertura e o stop loss como diferença entre os preços. Isto será feito, ou não permitiremos que isto aconteça? Note que tudo isto deve ser planejado. Antes mesmo de começarmos a de fato implementar a solução. Outra questão. Onde o usuário, ou operador deverá clicar? Isto para que a formatação do valor mude. Veja que apesar de ser algo simples em termos de codificação para ser feito. Pensar em todos estes pontos, faz parte da implementação. Pois se você decidir mudar as coisas depois, pode ter mais ou menos trabalho para conseguir isto.
Muito bem, então vamos ver qual é a minha proposta. Você pode discordar dela. É um direito seu. Mas de qualquer forma, a intenção aqui, é mostrar como se chega em um resultado a ser implementado. A minha proposta é: O usuário deverá clicar no objeto OBJ_EDIT. Isto para que a formatação seja modificada. A formatação irá mudar em um ciclo. Ou seja, cada vez que o usuário clicar no objeto OBJ_EDIT, mudaremos a formatação para o próximo modo presente na enumeração.
Muito bem, acabei de dar a dica, do próximo procedimento a ser modificado. E é isto mesmo que você deve ter imaginado meu caro leitor. Vamos modificar o procedimento DispatchMessage da classe C_ElementsTrade.
Mas antes de fazer isto, vamos fazer uma outra coisa. Esta pode ser vista logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_VERSION_DEBUG 05. //+------------------------------------------------------------------+ 06. #ifdef def_VERSION_DEBUG 07. #define macro_DEBUG_MODE(A) \ 08. Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A)); 09. #else 10. #define macro_DEBUG_MODE(A) 11. #endif 12. //+------------------------------------------------------------------+ 13. #define def_SymbolReplay "RePlay" 14. #define def_MaxPosSlider 400 15. #define def_MaskTimeService 0xFED00000 16. #define def_IndicatorTimeFrame (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96)))) 17. #define def_IndexTimeFrame 4 18. //+------------------------------------------------------------------+ 19. union uCast_Double 20. { 21. double dValue; 22. long _long; // 1 Information 23. datetime _datetime; // 1 Information 24. uint _32b[sizeof(double) / sizeof(uint)]; // 2 Informations 25. ushort _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations 26. uchar _8b [sizeof(double) / sizeof(uchar)]; // 8 Informations 27. }; 28. //+------------------------------------------------------------------+ 29. enum EnumEvents { 30. evTicTac, //Event of tic-tac 31. evHideMouse, //Hide mouse price line 32. evShowMouse, //Show mouse price line 33. evHideBarTime, //Hide bar time 34. evShowBarTime, //Show bar time 35. evHideDailyVar, //Hide daily variation 36. evShowDailyVar, //Show daily variation 37. evHidePriceVar, //Hide instantaneous variation 38. evShowPriceVar, //Show instantaneous variation 39. evCtrlReplayInit, //Initialize replay control 40. evChartTradeBuy, //Market buy event 41. evChartTradeSell, //Market sales event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event to communication 44. evEA_At_ChartTrade, //Event to communication 45. evChatWriteSocket, //Event to Mini Chat 46. evChatReadSocket, //Event To Mini Chat 47. evUpdate_Position, //Event to communication 48. evMsgClosePositionEA, //Event to communication 49. evMsgCloseTakeProfit, //Event to communication 50. evMsgCloseStopLoss, //Event to communication 51. evMsgNewTakeProfit, //Event to communication 52. evMsgNewStopLoss, //Event to communication 53. evMsgSetFocus, //Event to communication 54. evMsgServiceSwapStatus, //Event to stop the replay/simulator service 55. evMsgSwapViewModePosition //Event to Swap Mode 56. }; 57. //+------------------------------------------------------------------+ 58. enum EnumPriority { //Priority list on objects 59. ePriorityNull = -1, 60. ePriorityDefault = 0 61. }; 62. //+------------------------------------------------------------------+
Defines.mqh
Observe que na linha 55 temos a definição de um novo evento. Agora sim podemos ir para o procedimento DispatchMessage, na classe C_ElementsTrade. E para que as coisas fiquem completamente entendidas. Vamos ver este procedimento em um novo tópico. Assim será mais fácil separar as coisas, e permitir que você, meu caro leitor entenda o que está acontecendo.
Procedimento DispatchMessage. Que confusão
Apesar do título deste tópico, denotar que o que faremos é confuso. Na verdade, se você entendeu os artigos anteriores. E vem de fato, estudando o material, e se dedicando em aprender o que cada artigo tem apresentado. O que será visto aqui, lhe parecerá bastante simples. Longe de ser algo confuso, ou complicado. Mas antes de continuarmos, quero mostrar uma coisa a você, meu caro leitor. Veja a imagem abaixo.

Observe que estou destacando uma linha de retorno do compilador. Mas o que esta linha tem algo especial, a ponto, de que eu venha, a dar algum destaque a ela? Bem, esta linha surgiu, por conta de um alerta do compilador. Mas para entender, vamos ver o fragmento que a disparou.
219. //+------------------------------------------------------------------+ 220. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 221. { 222. string sz0; 223. long _lparam = lparam; 224. double _dparam = dparam; 225. 226. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 227. switch (id) 228. { 229. case (CHARTEVENT_KEYDOWN): 230. if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break; 231. _lparam = (long) m_Info.ticket; 232. _dparam = 0; 233. EventChartCustom(0, evUpdate_Position, _lparam, 0, ""); 234. case CHARTEVENT_CUSTOM + evMsgSetFocus: 235. if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)) 236. UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price); 237. macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)); 238. EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, ""); 239. m_Info.bClick = false; 240. case CHARTEVENT_CHART_CHANGE: 241. UpdateViewPort(m_Info.price); 242. if (m_Info.ev != evMsgClosePositionEA) 243. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 244. break; 245. case CHARTEVENT_CUSTOM + evMsgSwapViewModePosition: 246. if (m_Info.ticket == (ulong)(_lparam)) 247. { 248. m_Info.ViewMode = (stInfos::e1)((m_Info.ViewMode + 1) & 0x03); 249. EventChartCustom(0, evUpdate_Position, _lparam, 0, NULL); 250. } 251. break; 252. case CHARTEVENT_OBJECT_CLICK: 253. sz0 = GetPositionsMouse().szObjNameClick; 254. if (m_Info.bClick) 255. { 256. if (sz0 == def_NameBtnMove) 257. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, ""); 258. if (sz0 == def_NameBtnClose) 259. EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, PositionGetDouble(m_Info.ev == evMsgCloseTakeProfit ? POSITION_SL : POSITION_TP), PositionGetString(POSITION_SYMBOL)); 260. if (sz0 == def_NameObjLabel) 261. EventChartCustom(0, evMsgSwapViewModePosition, m_Info.ticket, 0, NULL); 262. } 263. m_Info.bClick = false; 264. break; 265. case CHARTEVENT_MOUSE_MOVE: 266. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 267. if (m_Info.weight > 1) 268. { 269. UpdateViewPort(_dparam = GetPositionsMouse().Position.Price); 270. if (m_Info.ev != evMsgClosePositionEA) 271. ViewValue(m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam); 272. if (m_Info.bClick) 273. { 274. if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss)) 275. EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 276. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 277. } 278. } 279. break; 280. } 281. } 282. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Olhe exatamente a linha informada na imagem acima. Veja que nela temos um código, onde fazemos uma conversão de tipo. O código do fragmento acima, já está atualizado. A ponto de não gerar o tal alerta. Porém, quero que você, meu caro e estimado leitor, observe com calma esta linha 248. Você verá que estou usando algo, que se você estiver prestando atenção, notará que não será encontrado no código. Assim, é preciso corrigir um detalhe, que foi dito no tópico anterior.
E o detalhe é o seguinte: Apesar das enumerações, de fato poderem ser anônimas, da forma como foi feito na linha 52 da classe C_ElementsTrade. Elas não serão corretamente convertidas, sem que especifiquemos um nome a enumeração. O motivo disto não é por conta de qualquer problema no código ou no compilador. E sim o fato de que no MQL5 existe uma função especial. Esta função especial é a EnumToString, que tem como objetivo, traduzir um valor, que obviamente é numérico, em uma string. Mas não qualquer string. Mas sim uma string especial, que representa exatamente o nome que aquele valor numérico tem dentro da enumeração.
Mas espere um pouco. Aqui não estamos em nenhum momento fazendo a tradução, ou conversão do valor em uma string. Sendo que esta é a palavra que se encontra naquela posição numérica dentro da enumeração. Então por que devemos nos preocupar com o alerta do compilador? Não podemos simplesmente o ignorar? Bem, este alerta, serve mais como um aviso. De que provavelmente poderemos ter algum tipo de falha na tradução caso ela venha a acontecer. Não é bom, você ignorar todos os alertas. Existem alguns que podemos ignorar. E existem outros que devemos corrigir. Cada caso é um caso. Não existe uma regra geral para este tipo de situação.
No caso em questão, a fatoração será feita da maneira correta. Mas como não gosto de ficar vendo alertas sendo disparados. Prefiro corrigir a causa que o está fazendo ser disparado. Assim sendo, tudo que será preciso fazer, é modificar a linha 52, vista no tópico anterior, para a linha que é vista logo abaixo.
052. enum e1 {eValue, eFinance, eTicks, ePercentage} ViewMode;
Uma vez feito isto, e mantendo o código do procedimento DispatchMessage, como é visto no fragmento acima. O alerta não mais será disparado pelo compilador. Então podemos voltar a nossa atenção ao procedimento DispatchMessage. E entender como ele funciona. Ele em si não é complicado, apesar de parecer confuso, se você não está de fato conseguindo acompanhar esta sequência de artigos. Mas se você está conseguindo entender as explicações contidas nos artigos desta série sobre o replay/simulador. Este código, é na verdade, muito simples. Então vamos dar uma passada pelo que é novidade dentro dele.
Aqui foi adicionada a linha 260, que é onde checamos se o clique foi dado no objeto OBJ_EDIT. Que é o objeto que contém o valor a ser mostrado ao operador ou usuário da plataforma. Bem, caso tudo esteja ok, e a checagem passe. Disparamos um evento na linha 261. Já explique como este processo é interpretado. Então na linha 245, capturamos este evento customizado.
Agora na linha 246, verificamos se o bilhete é o mesmo usado pelo indicador de posição. Caso positivo, iremos primeiro na linha 248, girar entre os quatro tipos de visualização. Uma após a outra. Depois de termos trocado o tipo de visualização, na linha 249, disparamos um novo evento. Este tem como objetivo, atualizar os demais segmentos do indicador de posição. Ou seja, mesmo que você clique no take profit, tanto o stop loss, quanto o resultado da operação, serão também atualizados. Isto evita que tenhamos um segmento mostrando porcentagem, enquanto os demais podem estar mostrando outra coisa. E o resultado pode ser visto na animação abaixo.

Notem que mesmo adicionado praticamente quase nada de código, ao procedimento DispatchMessage. Ainda assim viemos a conseguir exatamente o que foi planejado para ser feito. Ou seja, um belo resultado no final das contas. Porém, ainda quero explicar uma coisa. Que pode vir a ser o seu caso em particular, meu caro leitor. Suponhamos que você, venha a desejar usar este indicador de posição, em uma conta do tipo HEDGING. Este tipo de conta, para quem não sabe, é aquele que você pode tanto manter uma posição de compra e uma de venda. No mesmo ativo ao mesmo tempo. E mesmo já tendo uma posição comprada, você consegue abrir uma nova posição. Isto sem que uma afete diretamente a outra. Este afetar é relativo. Já que financeiramente ambas estarão de alguma forma correlacionadas.
O que acontece, se você tiver dois indicadores de posição no gráfico? Basicamente, eles serão independentes um do outro. Ou seja, ao efetuar a troca na forma de visualizar os dados de uma posição. Apenas os segmentos daquela posição, ou seja, o preço de abertura, o take profit e o stop loss. Serão de fato afetados. Os segmentos das demais posições, não sofreram nenhum tipo de modificação, ou troca na forma de demonstrar o resultado da posição. Isto por conta do teste que está sendo feito na linha 246.
Mas caso você, meu caro leitor e entusiasta, venha a desejar que a mudança na forma de visualizar os dados da posição, também mude dos de outras posições. Bastará remover o teste mencionado na linha 246. Porém, e é aqui que você deve pensar antes de fazer tal remoção. Existe uma falha, ou melhor dizendo, uma falta de comunicação entre os indicadores de posição. E esta falta de comunicação, irá fazer com que as coisas fiquem bastante confusas, ou no mínimo, não venham a ser como você esperava.
Para entender isto, primeiro você deve entender que o indicador sempre começa no modo zero da enumeração declarada na linha 52. Então quando o indicador é lançado no gráfico ele começará no modo ZERO. Neste momento você usa o que é visto na animação acima, e troca o modo para, por exemplo o modo UM. Lembrando que temos QUATRO MODOS. Depois de isto feito, você abre uma nova posição. Isto em uma conta HEDGING. Este momento teremos um indicador no modo UM e um outro no modo ZERO. Agora pense: Se você removeu o teste na linha 246, ao trocar o modo desta nova posição para o modo UM. Você também irá modificar o modo de visualização da primeira posição, de UM para TRÊS. E agora temos a confusão sendo feita. Isto por que, se você tentar voltar o modo de visualização da primeira posição para o modo UM. Irá por consequência mudar também o da nova posição, desta vez para o modo TRÊS.
Resumindo, não é adequado apenas mudar as coisas removendo este ou aquele ponto. Você meu caro leitor, deve antes de mais nada parar e pensar antes de fazer as coisas. Não que eu esteja lhe desmotivando a mudar, ou adaptar o código as suas necessidades particulares. Não é isto. Apenas quero que você saiba o que está fazendo, antes mesmo de tentar fazer.
Mas não quero lhe deixar na mão, meu caro entusiasta. Vamos pensar um pouco, para encontrar uma solução para este problema. Quando um indicador, ou Expert Advisor é lançado no gráfico. Primeiro ele executa o código presente na função OnInit. Mas o MetaTrader 5, dispara um evento que será capturado pela função OnChartEvent. Este evento é o CHARTEVENT_CHART_CHANGE. E esta informação é importante. Pois saber disto, nos permite sincronizar os indicadores de posição, assim que ele é lançado no gráfico.
Bem, não vou complicar as coisas neste momento. Vou apenas lhe mostrar, parte da solução neste momento, meu caro leitor. No futuro, irei mostrar como criar uma solução ainda melhor, pois ela de fato se fará necessária. Então vamos ver como você pode sincronizar as coisas neste momento. Fazendo o mínimo de esforço para tal tarefa.
Muito bem, no fragmento visto abaixo, temos a primeira parte da solução.
219. //+------------------------------------------------------------------+ 220. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 221. { 222. string sz0; 223. long _lparam = lparam; 224. double _dparam = dparam; 225. 226. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 227. switch (id) 228. { 229. case (CHARTEVENT_KEYDOWN): 230. if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break; 231. _lparam = (long) m_Info.ticket; 232. _dparam = 0; 233. EventChartCustom(0, evUpdate_Position, _lparam, 0, ""); 234. case CHARTEVENT_CUSTOM + evMsgSetFocus: 235. if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)) 236. UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price); 237. macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)); 238. EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, ""); 239. m_Info.bClick = false; 240. case CHARTEVENT_CHART_CHANGE: 241. UpdateViewPort(m_Info.price); 242. if (m_Info.ev != evMsgClosePositionEA) 243. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 244. break; 245. case CHARTEVENT_CUSTOM + evMsgSwapViewModePosition: 246. m_Info.ViewMode = (stInfos::e1)_dparam; 247. m_Info.ViewMode = (stInfos::e1)((m_Info.ViewMode + 1) & 0x03); 248. EventChartCustom(0, evUpdate_Position, _lparam, 0, NULL); 249. break; 250. case CHARTEVENT_OBJECT_CLICK: 251. sz0 = GetPositionsMouse().szObjNameClick; 252. if (m_Info.bClick) 253. { 254. if (sz0 == def_NameBtnMove) 255. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, ""); 256. if (sz0 == def_NameBtnClose) 257. EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, PositionGetDouble(m_Info.ev == evMsgCloseTakeProfit ? POSITION_SL : POSITION_TP), PositionGetString(POSITION_SYMBOL)); 258. if (sz0 == def_NameObjLabel) 259. EventChartCustom(0, evMsgSwapViewModePosition, m_Info.ticket, (double)m_Info.ViewMode, NULL); 260. } 261. m_Info.bClick = false; 262. break; 263. case CHARTEVENT_MOUSE_MOVE: 264. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 265. if (m_Info.weight > 1) 266. { 267. UpdateViewPort(_dparam = GetPositionsMouse().Position.Price); 268. if (m_Info.ev != evMsgClosePositionEA) 269. ViewValue(m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam); 270. if (m_Info.bClick) 271. { 272. if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss)) 273. EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 274. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 275. } 276. } 277. break; 278. } 279. } 280. //+------------------------------------------------------------------+
Fragmento de C_ElementTrade
Observe que agora, na linha 259, estamos informando qual é o atual valor da visualização. Isto usando o dparam. Então quando na linha 246, o código for executado. Iremos igualar o valor de todos os indicadores de posição com aquele vindo no parâmetro dparam. É aqui onde a sincronização acontece. Sei que poderia ter feito a linha 246 e 247, serem uma única linha. Mas queria mostrar para você, meu caro leitor, como de fato a coisa acontece. Pois bem, se você executar isto, e fizer a mudança na visualização. Irá obter o que é visto na animação abaixo.

Pergunta: Por que os segmentos não estão sincronizados? Resposta: Eles estão sincronizados. Mas é preciso que façamos a atualização do gráfico, para que eles fiquem visivelmente sincronizados. E por que disto? O motivo é a linha 248. Ela apenas irá atualizar o indicador que recebeu o evento de clique, por parte do usuário. Mas então como podemos resolver este inconveniente? Simples. Basta atualizar todas as posições. Para isto é só modificar o código para o que é visto logo abaixo.
245. case CHARTEVENT_CUSTOM + evMsgSwapViewModePosition: 246. m_Info.ViewMode = (stInfos::e1)((((stInfos::e1)_dparam) + 1) & 0x03); 247. for (int c0 = PositionsTotal() - 1; c0 >= 0; c0--) 248. EventChartCustom(0, evUpdate_Position, PositionGetTicket(c0), 0, NULL); 249. break; 250. case CHARTEVENT_OBJECT_CLICK:
Fragmento de C_ElementsTrade
Observe que agora, estou focando apenas e somente, na parte que realmente precisou ser modificada. Aquelas duas linhas que existiam no código anterior, agora já são apenas uma, ou seja, as linhas 246 e 247 do fragmento anterior, agora estão contidas na linha 246. Já que acredito que você tenha compreendido como elas funcionam. Já a correção é a colocação da linha 247 no código. Note que diferente do código anterior, agora, o evento de evUpdate_Position, será disparado para todas posições em aberto. Com isto temos a nossa solução e o resultado é visto na animação abaixo.

Virão como é simples. Porém não está perfeito. Depois mostrarei um código ainda melhor. Onde as coisas serão bem mais interessantes. Mas por hora está bom assim.
Considerações finais
Neste artigo, mostrei como você, entusiasta e sedento por conhecimento. Pode conseguir fazer com que, o indicador de posição ficasse ainda mais interessante de ser utilizado. Se bem, que ainda ele não está totalmente concluído. Precisando ainda sofrer algumas poucas modificações, antes de que possamos de fato o usar. Isto considerando que a ideia, é ter um indicador que sirva tanto para o replay/simulador. Quanto também para ser usado no mercado, seja em conta demo, seja em conta real. Se bem, que a proposta aqui, é sempre apresentar a coisa toda, da maneira que seja o mais didático quanto for possível fazer. Acredito que você, meu caro e estimado leitor, esteja de fato conseguindo acompanhar o desenvolvimento. Mesmo que ele pareça lento e sem muita perspectiva de realmente funcionar no replay/simulador.
Mas estamos chegando lá. Um passo de cada vez. Mas para aqueles que, apenas desejam experimentar as aplicações já desenvolvidas até aqui. No anexo, vou deixar as aplicações já compiladas. Bastara apenas que você extraia os arquivos e os utilize para ver como as coisas estão neste ponto do desenvolvimento. Lembrando que eles têm propósitos didáticos. Podendo conter algumas falhas. Então os use por sua conta e risco.
| Arquivo | Descriçã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.mq5 | Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação) |
| Indicators\Market Replay.mq5 | Cria os controles para interação com o serviço de replay/simulador (É necessário o Mouse Study para interação) |
| Indicators\Mouse Study.mq5 | Permite 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.mq5 | Responsável pela indicação de ordens de mercado, permitindo interação e controle das mesmas |
| Indicators\Position View.mq5 | Responsável pela indicação de posições de mercado, permitindo interação e controle das mesmas |
| Services\Market Replay.mq5 | Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema) |
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Expert Advisor de scalping Ilan 3.0 AI com aprendizado de máquina
Do básico ao intermediário: Filas, Listas e Árvores (VI)
Redes neurais em trading: Hierarquia de habilidades para comportamento adaptativo de agentes (HiSSD)
Métodos de conjunto para aprimorar previsões numéricas em MQL5
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso