Simulação de mercado: Position View (XV)
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 (XIV), mostrei como podemos fazer para adicionar, os detalhes necessários para que as linhas de take profit e stop loss pudessem mostrar a condição de ativação das mesmas. Ou seja, quando o preço atingisse aquele ponto. Fosse ele, de stop loss ou take profit, se teríamos lucro ou prejuízo. E quanto seria este lucro ou prejuízo. Isto em termos de pontos, não estamos ainda trabalhando em termos financeiros.
Pois bem, naquele mesmo artigo, prometi, que iria explicar os detalhes sobre como a troca de mensagens e a programação baseada em eventos. Torna possível que um código como este que estou apresentando, possa funcionar como muitos podem claramente verificar. Já que estou disponibilizando os executáveis no anexo. Isto quando os mesmos estão suficientemente estáveis. E também estou mostrando na íntegra todo o código. Isto para que outros programadores também tenham acesso, a informação e ao que está sendo implementado.
Mas apenas daquele código visto até o artigo anterior funcionar perfeitamente bem. Podemos fazer bem mais. Então para tornar a explicação um pouco mais simples. Decidi postar o mesmo código, visto anteriormente. Porém já atualizado em termos de chamadas. Ele funciona igual ao que foi visto no artigo anterior. Porém a implementação mudou ligeiramente. Não necessitando assim de ser explicado. Bastará que você entenda o que explicarei neste artigo. Que você conseguirá compreender o código. Isto claro, se você vem acompanhando esta sequência. Então vamos começar.
Uma rápida atualização
O código atualizado pode ser visto logo abaixo. Compare ele com a versão vista no artigo anterior e você notará que ele permanece igual. Se bem que pode parecer diferente à primeira vista. O código principal é visto logo abaixo.
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.127" 008. #property link "https://www.mql5.com/pt/articles/13375" 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. char digits; 026. bool bIsBuy; 027. }m_Infos; 028. //+------------------------------------------------------------------+ 029. C_ElementsTrade *Open = NULL, *Stop = NULL, *Take = NULL; 030. //+------------------------------------------------------------------+ 031. bool CheckCatch(ulong ticket) 032. { 033. ZeroMemory(m_Infos); 034. m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket); 035. if (!PositionSelectByTicket(m_Infos.ticket)) return false; 036. if (ObjectFind(0, m_Infos.szShortName) >= 0) 037. { 038. m_Infos.ticket = 0; 039. return false; 040. } 041. m_Infos.szSymbol = PositionGetString(POSITION_SYMBOL); 042. m_Infos.digits = (char)SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); 043. IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName); 044. EventChartCustom(0, evUpdate_Position, ticket, 0, ""); 045. 046. return true; 047. } 048. //+------------------------------------------------------------------+ 049. inline void ProfitNow(void) 050. { 051. double ask, bid; 052. 053. ask = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_ASK); 054. bid = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_BID); 055. if (Open != NULL) 056. (*Open).ViewValue((m_Infos.bIsBuy ? bid - m_Infos.priceOpen : m_Infos.priceOpen - ask)); 057. } 058. //+------------------------------------------------------------------+ 059. int OnInit() 060. { 061. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 062. if (!CheckCatch(user00)) 063. { 064. ChartIndicatorDelete(0, 0, def_ShortName); 065. return INIT_FAILED; 066. } 067. 068. return INIT_SUCCEEDED; 069. } 070. //+------------------------------------------------------------------+ 071. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 072. { 073. ProfitNow(); 074. 075. return rates_total; 076. } 077. //+------------------------------------------------------------------+ 078. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 079. { 080. if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam); 081. if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam); 082. if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam); 083. switch (id) 084. { 085. case CHARTEVENT_CUSTOM + evUpdate_Position: 086. if (lparam != m_Infos.ticket) break; 087. if (!PositionSelectByTicket(m_Infos.ticket)) 088. { 089. ChartIndicatorDelete(0, 0, m_Infos.szShortName); 090. return; 091. }; 092. if (Open == NULL) Open = new C_ElementsTrade(m_Infos.ticket, evMsgClosePositionEA, clrRoyalBlue, m_Infos.digits, StringFormat("%I64u : Position opening price.", m_Infos.ticket), m_Infos.bIsBuy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)); 093. if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, clrForestGreen, m_Infos.digits, StringFormat("%I64u : Take Profit price.", m_Infos.ticket), m_Infos.bIsBuy); 094. if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, clrFireBrick, m_Infos.digits, StringFormat("%I64u : Stop Loss price.", m_Infos.ticket), m_Infos.bIsBuy); 095. (*Open).UpdatePrice(0, m_Infos.priceOpen = PositionGetDouble(POSITION_PRICE_OPEN)); 096. (*Take).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_TP)); 097. (*Stop).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_SL)); 098. ProfitNow(); 099. break; 100. } 101. ChartRedraw(); 102. }; 103. //+------------------------------------------------------------------+ 104. void OnDeinit(const int reason) 105. { 106. delete Open; 107. delete Take; 108. delete Stop; 109. } 110. //+------------------------------------------------------------------+
Indicador de posição
Já o código da classe é visto a seguir.
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. //+------------------------------------------------------------------+ 010. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 011. //+------------------------------------------------------------------+ 012. #define def_PathBtns "Images\\Market Replay\\Orders\\" 013. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 014. #resource "\\" + def_Btn_Close; 015. //+------------------------------------------------------------------+ 016. #include "..\Auxiliar\C_Mouse.mqh" 017. //+------------------------------------------------------------------+ 018. #ifdef def_FontName 019. "Why are you trying to do this?" 020. #else 021. #define def_FontName "Lucida Console" 022. #define def_FontSize 10 023. #endif 024. //+------------------------------------------------------------------+ 025. class C_ElementsTrade : private C_Mouse 026. { 027. private : 028. //+------------------------------------------------------------------+ 029. struct st00 030. { 031. struct st_01 032. { 033. uchar Width, 034. Height, 035. digits; 036. }Text; 037. ulong ticket; 038. string szPrefixName, 039. szDescr; 040. EnumEvents ev; 041. double price, 042. open; 043. bool bClick, 044. bIsBuy; 045. char weight; 046. color _color; 047. }m_Info; 048. //+------------------------------------------------------------------+ 049. void UpdateViewPort(const double price) 050. { 051. int x, y; 052. 053. ChartTimePriceToXY(0, 0, 0, price, x, y); 054. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 055. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 056. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 057. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 058. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 059. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XDISTANCE, x + 10); 060. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2)); 061. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 062. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 063. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 064. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 065. } 066. //+------------------------------------------------------------------+ 067. inline void CreateLinePrice(void) 068. { 069. string szObj; 070. 071. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 072. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 073. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 074. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 075. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 076. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 077. macro_LineInFocus(false); 078. } 079. //+------------------------------------------------------------------+ 080. inline void CreateBoxInfo(const bool bMove) 081. { 082. string szObj; 083. const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0}; 084. 085. CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 086. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 087. ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c)); 088. ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? m_Info._color : (m_Info.bIsBuy ? clrForestGreen : clrFireBrick))); 089. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15)); 090. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 091. } 092. //+------------------------------------------------------------------+ 093. inline void CreateObjectInfoText(void) 094. { 095. string szObj; 096. 097. CreateObjectGraphics(szObj = def_NameObjLabel, OBJ_EDIT, clrNONE, (EnumPriority)(ePriorityDefault)); 098. ObjectSetString(0, szObj, OBJPROP_FONT, def_FontName); 099. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, def_FontSize); 100. ObjectSetInteger(0, szObj, OBJPROP_COLOR, clrBlack); 101. ObjectSetInteger(0, szObj, OBJPROP_BORDER_COLOR, m_Info._color); 102. ObjectSetInteger(0, szObj, OBJPROP_ALIGN, ALIGN_CENTER); 103. ObjectSetInteger(0, szObj, OBJPROP_READONLY, true); 104. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, m_Info.Text.Height); 105. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, m_Info.Text.Width); 106. } 107. //+------------------------------------------------------------------+ 108. inline void CreateButtonClose(void) 109. { 110. string szObj; 111. 112. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 113. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 114. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 115. } 116. //+------------------------------------------------------------------+ 117. public : 118. //+------------------------------------------------------------------+ 119. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, char digits, string szDescr = "\n", const bool IsBuy = true) 120. :C_Mouse(0, "") 121. { 122. uint w, h; 123. 124. ZeroMemory(m_Info); 125. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 126. m_Info._color = _color; 127. m_Info.szDescr = szDescr; 128. m_Info.bIsBuy = IsBuy; 129. m_Info.Text.digits = digits; 130. TextSetFont(def_FontName, def_FontSize * -10); 131. TextGetSize(StringFormat("%." + (string)digits + "f", 8888.88888), w, h); 132. m_Info.Text.Width = (uchar) w + 4; 133. m_Info.Text.Height = (uchar) h + 4; 134. } 135. //+------------------------------------------------------------------+ 136. ~C_ElementsTrade() 137. { 138. ObjectsDeleteAll(0, m_Info.szPrefixName); 139. } 140. //+------------------------------------------------------------------+ 141. inline void UpdatePrice(const double open, const double price) 142. { 143. if (price > 0) 144. { 145. CreateLinePrice(); 146. CreateButtonClose(); 147. }else 148. ObjectsDeleteAll(0, m_Info.szPrefixName); 149. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 150. if (price > 0) 151. CreateObjectInfoText(); 152. m_Info.open = open; 153. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 154. if (m_Info.ev != evMsgClosePositionEA) 155. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 156. } 157. //+------------------------------------------------------------------+ 158. void ViewValue(const double profit) 159. { 160. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, StringFormat("%." + (string)m_Info.Text.digits + "f", (profit < 0 ? -(profit) : profit))); 161. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 162. ChartRedraw(); 163. } 164. //+------------------------------------------------------------------+ 165. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 166. { 167. string sz0; 168. long _lparam = lparam; 169. double _dparam = dparam; 170. 171. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 172. switch (id) 173. { 174. case (CHARTEVENT_KEYDOWN): 175. if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break; 176. _lparam = (long) m_Info.ticket; 177. _dparam = 0; 178. EventChartCustom(0, evUpdate_Position, _lparam, 0, ""); 179. case CHARTEVENT_CUSTOM + evMsgSetFocus: 180. if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)) 181. UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price); 182. macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)); 183. EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, ""); 184. m_Info.bClick = false; 185. case CHARTEVENT_CHART_CHANGE: 186. UpdateViewPort(m_Info.price); 187. if (m_Info.ev != evMsgClosePositionEA) 188. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 189. break; 190. case CHARTEVENT_OBJECT_CLICK: 191. sz0 = GetPositionsMouse().szObjNameClick; 192. if (m_Info.bClick) 193. { 194. if (sz0 == def_NameBtnMove) 195. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, ""); 196. if (sz0 == def_NameBtnClose) 197. EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, PositionGetDouble(m_Info.ev == evMsgCloseTakeProfit ? POSITION_SL : POSITION_TP), PositionGetString(POSITION_SYMBOL)); 198. } 199. m_Info.bClick = false; 200. break; 201. case CHARTEVENT_MOUSE_MOVE: 202. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 203. if (m_Info.weight > 1) 204. { 205. UpdateViewPort(_dparam = GetPositionsMouse().Position.Price); 206. if (m_Info.ev != evMsgClosePositionEA) 207. ViewValue(m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam); 208. if (m_Info.bClick) 209. { 210. if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss)) 211. EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 212. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 213. } 214. } 215. break; 216. } 217. } 218. //+------------------------------------------------------------------+ 219. }; 220. //+------------------------------------------------------------------+ 221. #undef macro_LineInFocus 222. //+------------------------------------------------------------------+ 223. #undef def_Btn_Close 224. #undef def_PathBtns 225. #undef def_FontName 226. #undef def_FontSize 227. //+------------------------------------------------------------------+ 228. #undef def_NameObjLabel 229. #undef def_NameInfoDirect 230. #undef def_NameBtnMove 231. #undef def_NameBtnClose 232. #undef def_NameHLine 233. //+------------------------------------------------------------------+
C_ElementsTrade
Este código, uma vez compilado, funciona da mesma forma, que o código do artigo anterior. Não sendo de fato algo que mereça nenhum destaque especial. Mas vou usar ele como base no que será explicado a seguir. Isto por ele ser levemente mais simples de entender alguns dos conceitos que irei precisar utilizar. Em alguns momentos será preciso que você veja os artigos anteriores. Isto para que consiga acompanhar toda a explicação e entender como fazendo uso de programação baseada em eventos, e troca de mensagens entre aplicações. Tornou possível fazer este código, aparentemente sem sentido funcionar. Então acomode-se confortavelmente, e vamos aprender a fazer uso deste modelo de implementação. Tentarei ser o mais didático quanto for possível.
Entendendo por que o sistema funciona
Para entender todo o sistema, vamos começar pelo básico. A estrutura inicial e mais básica é vista na imagem logo abaixo:

Vamos começar a entender a troca de mensagens, analisando esta imagem. Sei que já tem um bom tempo, que estamos fazendo uso deste modelo de programação. Porém até o momento, ainda não havia parado para explicar ele em detalhes. Note que saindo do indicador Chart Trade, temos três setas. Cada uma representa uma mensagem, que será disparada quando o usuário interagir, via indicador de mouse, com o Chart Trade.
Note que apesar das mensagens serem interpretadas no código do Expert Advisor. Elas não vão diretamente para ele. Elas são de fato direcionadas para o MetaTrader 5, que irá logo depois as direcionar para o gráfico correto. Preste atenção a isto. Você pode dizer ao MetaTrader 5, que a mensagem, deve ser direcionada para o gráfico atual, ou para um outro gráfico qualquer.
Por consequência o MetaTrader 5, irá disparar um evento OnChartEvent. Porém este será do tipo customizado. Assim, o Expert Advisor conseguirá captura o requerimento feito pelo usuário ao chart Trade. Logo que este requerimento for devidamente interpretado, ele disparará um novo evento. Este irá ser direcionado ao servidor de negociação. Fazendo com que uma posição seja aberta, fechada ou tenha o volume modificado.
Muito bem, esta é a primeira parte do sistema. Uma vez que o servidor responda ao requerimento feito. Passamos a ter um fluxo mensagens e eventos, visto logo abaixo.

Este fluxo mostrado acima, acontece em diversos momentos. Quando algum requerimento é enviado ao servidor. Mas note que temos uma seta ROXA neste diagrama. Esta seta, é justamente a responsável por indicar que o Expert Advisor estará criando o indicador de posição. Está é uma parte em que outras aplicações não ficaram cientes do que está acontecendo. Mas assim que o indicador já esteja no gráfico. O Expert Advisor envia um evento customizado para o MetaTrader 5. Este será redirecionado ao gráfico, e será capturado pelo indicador de posição. Fazendo com que ele capture os dados mais atuais presentes na posição.
Note que o indicador de posição não tem consciência da presença do Expert Advisor. Assim como também o Expert Advisor, não sabe se o indicador de posição está ou não presente no gráfico. Ambas aplicações simplesmente ignoram a presença uma da outra. Tudo que elas fazem é enviar uma mensagem para o MetaTrader 5 e este direciona a mensagem para o gráfico desejado pela aplicação. A mensagem somente será interpretada somente se a aplicação, estiver esperando aquela mensagem em especial. Caso contrário ela será perdida.
Porém esta imagem acima, não nos diz o que estará ocorrendo quando o usuário interagir com o indicador de posição. Já que a mesma não conta com o indicador de mouse, sendo mostrada. Assim quando temos uma interação entre o indicador de mouse e o indicador de posição. As coisas mudam ligeiramente. Tendo uma imagem como a mostrada abaixo.

Note que agora, olhando o esquema de funcionamento, temos a impressão que a coisa, ficou muito mais complicada. Mas não é bem assim. Nesta imagem acima, temos a representação total do que acontece entre o indicador de mouse, o indicador de posição e o Expert Advisor, quando estes estão presentes no gráfico. Agora neste ponto podemos e iremos nos voltar ao código que é visto no início do artigo. Para entender cada uma destas setas mostradas acima.
Repare um detalhe curioso. Aqui temos uma seta CINZA. Está de fato, permite que o indicador de mouse e o Indicador de posição possam, de maneira virtual, trocar informações. Se bem que não é bem isto que de fato acontece. Esta seta CINZA, está na verdade representando a herança que está acontecendo no código. Note que linha 25 da classe C_ElementsTrade, que a classe está herdando de forma privativa a classe C_Mouse. Esta seta CINZA representa isto.
Mas você deve tomar o seguinte cuidado, ao analisar este esquema. Talvez você pense que não necessita do indicador de mouse no gráfico. Já que a classe C_ElementsTrade o está herdando. Mas não é isto que de fato acontece. É imprescindível que o indicador de mouse esteja de fato no gráfico. Caso contrário os eventos de mouse não serão capturados pelo indicador de posição.
Agora preste bastante atenção. Neste esquema temos saindo do MetaTrader 5 e indo para o indicador de posição uma seta. Está nos diz que eventos oriundos do MetaTrader 5, serão capturados pelo Indicador de posição. Volte ao código acima e note que isto acontece em dois locais. O primeiro é no código principal, ou seja, no procedimento OnChartEvent. E o segundo é no código da classe, ou seja, em DispatchMessage. Agora repare que separei o evento Update, dos demais eventos. Isto no diagrama mostrado. Por que fiz isto? O motivo é que você meu caro leitor, precisa compreender que existem dois tipos de eventos aqui. O primeiro tem origem no Expert Advisor. Este que gera os eventos de Update. Já o segundo pode ter origem no indicador de posição ou está vindo do MetaTrader 5. De qualquer forma este segundo tipo será visto tanto pelo indicador de mouse, quando pelo Expert Advisor. Porém eles não saberão o que fazer com este evento, ou mensagem vinda do Indicador de posição. Ignorando completamente tal mensagem.
Porém esta mesma mensagem é importante para o Indicador de posição. Assim quando um requerimento é feito, vindo do indicador de posição. O MetaTrader 5 irá disparar uma chamada OnChartEvent. E esta mensagem postada pelo MetaTrader 5, cuja origem é o Indicador de posição será vista por todas as aplicações. No entanto, apenas no código DispatchMessage, presente no indicador de posição é que este evento, ou mensagem será devidamente tratada.
Um dos momentos em que isto acontece, é justamente quando a linha 178 é executada. Note que ali estamos enviando um evento evUpdate_Position. Este evento será de fato capturado, pelo código principal do indicador de posição. Isto na linha 85. Note que originalmente, o indicador de posição pensaria que tal evento foi gerado pelo Expert Advisor. Mas não, ele foi gerado pelo Indicador de posição, lançado para o MetaTrader 5. Para logo em seguida o MetaTrader 5, devolver este mesmo evento ao indicador de posição. Isto obriga o indicador de posição, a atualizar as informações da posição. Mas como este evento foi gerado, depois que a tecla ESC foi pressionada. Isto nos permitirá tratar tal condição. Não nos forçando a manter qualquer tipo de informação em memória.
Mas esta não é a questão mais interessante. Lembra-se do artigo anterior? Onde expliquei como modificar o código, a ponto de que, ao tentarmos, via interação do indicador de mouse e indicador de posição. Criar a linha de stop loss ou take profit. Caso elas não existissem? Pois bem. Note que esta tarefa está sendo representada na imagem acima. Apesar de não ser tão evidente assim. Mas acompanhem o raciocínio. O indicador de posição, recebe os dados do mouse, primeiramente via herança. Mas principalmente via eventos oriundos do MetaTrader 5.
No primeiro momento, a variável m_Info.bClick estará com um valor falso. Então quando o evento CHARTEVENT_OBJECT_CLICK, chegar vindo do MetaTrader 5. A linha 192 impedirá que o código prossiga. Isto por conta do fato de estarmos checando a variável m_Info.bClick. Porém, se o indicador de mouse, informar que o clique é válido. A linha 202 irá tornar a variável m_Info.bClick como verdadeira. Como o evento CHARTEVENT_OBJECT_CLICK é disparado depois do evento CHARTEVENT_MOUSE_MOVE, como ficou mostrado nos artigos anteriores. Ao checar a variável na linha 192, o teste irá passar, e teremos no caso de clicar no objeto de movimentação. A execução da linha 195. Esta irá direcionar uma mensagem para o MetaTrader 5.
Agora é que a coisa fica interessante. Apesar de parecer um pouco confusa. Mas olhe sempre o diagrama. Ao executar esta linha 195, seria como se fizéssemos uma chamada a linha 179. Porém, apesar da chamada ser originalmente dentro do mesmo código. Para a aplicação é como se ela estivesse surgida de um outro local, fora do código do indicador. O que acontece ali, é que o código irá atualizar alguns valores. E logo depois irá retornar. Mas paralelo a isto, teremos a execução da linha 199. Finalizando assim tanto o evento da linha 179, quanto da linha 190. Porém, se existir mais de um indicador de posição no gráfico. Todos receberão a mesma mensagem vindas do MetaTrader 5. Não importando a origem da mesma. Todos responderão de maneira bastante parecida. Assim, a linha que precisa receber o foco, terá o foco do mouse. Todas as demais, perderão o foco.
No próximo momento em que o indicador de posição, receber um evento CHARTEVENT_MOUSE_MOVE, iremos verificar na linha 203, se temos o foco. Caso isto seja afirmativo, nas linhas 205 e 207, termos a visualização do movimento da linha, seja de take profit, seja do stop loss. Nenhuma outra linha será de fato afetada nesta movimentação. E a movimentação não é de fato real. Ou seja, ela não estará acontecendo conforme você move o mouse. Ela é virtual. E estará aguardando um novo evento para se confirmar, ou para ser descartada. Caso ela seja descartada, e isto ocorre ao pressionarmos a tecla ESC teremos um evento originado no MetaTrader 5. Este será capturado pela linha 174. O que fará que todos os dados sejam revistos. Já que teremos o disparo na linha 178 de um evento de update. O que fará com que os antigos dados, ou dados presentes no servidor de negociação, seja novamente mostrados, pelo indicador de posição.
Agora, caso os dados de movimentação do mouse, sejam aceitos. Isto por conta, de um clique do operador na posição desejada para o preço. Teremos na linha 211 o disparo de um evento. Este visa passar uma mensagem ao Expert Advisor. Informando que o Expert Advisor deverá fazer um requerimento ao servidor a fim de atualizar a nova posição, seja do take profit, seja do stop loss. Depois disto na linha 212 dizemos a todos os indicadores de posição, inclusive este que disparou o evento. De que as linhas de take profit e stop loss, deverão todas perder o foco. Enquanto isto o Expert Advisor aguarda uma reposta do servidor de negociação. Quando esta resposta chegar, o Expert Advisor disparará um evento. Este pedirá para o indicador de posição, cujo bilhete da posição corresponda ao valor informado na mensagem, de que ele deverá ser atualizado. Assim voltamos novamente na linha 85 do código principal do indicador de posição.
Esta é a maravilha da troca de mensagens e programação baseada em eventos. Nós de fato, não precisamos nos preocupar com a ordem em que as coisas acontecem. Desde que elas aconteçam. Porém note que o indicador de mouse também recebe um requerimento. Neste momento tal requerimento, tem como origem o indicador de posição. E ele é disparado na linha 183. Isto nos permite ocultar a linha de preço de indicador de mouse. Mas também poderíamos mudar completamente este comportamento. Bastando é claro, dizer ao indicador de mouse qual o comportamento que desejamos que ele cumpra. E ele obedecerá, podendo inclusive acionar algum tipo de estudo, ou coisa do tipo enquanto movemos o ponteiro do mouse, a fim de posicionar a linha de stop loss ou take profit.
Como eu venho dizendo a um bom tempo. Não existem limites para o que se pode fazer. Por isto, estou dedicando este artigo para explicar a você, meu caro e estimado leitor. Como efetuar a programação baseada em eventos e troca de mensagens. Pois se você vier a desejar manipular, ou analisar qualquer coisa. Poderá fazer isto de forma supersimples, fácil e rápida, diretamente no gráfico. Não precisa sair procurando métodos ou recursos mirabolantes para tal coisa.
Por exemplo: Diversas pessoas, tem me feito um pedido, cujo objetivo, me parece bastante adequado. Apesar de eu, não ter dito como de fato fazer isto, ou não ter dado incentivo para que pensassem assim. É algo que podemos facilmente fazer se fizermos uso de troca de mensagens entre as aplicações.
O tal pedido é o seguinte: Eles gostariam de fazer com que o Expert Advisor, que estão criando, diga ao serviço de replay/simulador para interromper o lançamento de novos ticks, ou cotações. À primeira vista, isto parece ser algo extremamente complicado e difícil de ser implementado. Já que não temos como enviar um pedido, mensagem ou evento diretamente ao serviço. Lembre-se de que eventos customizados, assim como mensagens usando este mecanismo que estou mostrando. Estarão sempre ligados ao gráfico. Ou seja, não temos uma forma de transferir mensagens para um serviço de maneira direta. Isto por conta que ele não está de fato ligado a nenhum gráfico específico. Porém existe sim uma forma de transferir, ou repassar mensagens ao um serviço. E para separar as coisas e mostrar como você deverá proceder, vamos a um novo tópico.
Dando pause no serviço de replay/simulador diretamente pelo Expert Advisor
Para cumprir este objetivo, será necessária uma pequena ajuda. Ou melhor, precisamos que algo esteja acontecendo. Para que um serviço consiga obter, ou receber informações do tipo eventos, ou mensagens. Precisamos que o serviço esteja olhando algo que esteja no gráfico. Novamente, as mensagens e eventos são direcionadas ao gráfico. NUNCA SE ESQUEÇA DISTO quando estiver programando em MQL5. Felizmente o serviço de replay/simulador, conta com um indicador presente no gráfico. Este é o indicador de controle. Ao interagirmos com este indicador. Isto usando o indicador de mouse. Podemos dizer ao serviço para interromper ou iniciar a simulação ou replay.
Porém como originalmente não era a minha intenção que o sistema fosse utilizado, para modelagem ou testes de estratégias automatizadas. Entenda isto como usar um Expert Advisor para lhe ajudar em operações de mercado. O sistema não conta até o presente momento com evento, ou mensagem que permita o serviço ser interrompido ou iniciado a pedido do Expert Advisor. Ou por qualquer outro indicador ou coisa do gênero. Como por exemplo um script em MQL5.
Porém para que tal coisa seja feita, é necessário efetuar algumas mudanças no código. Mas não se preocupe, são mudanças muito simples, que você poderá facilmente colocar em seu código. Tudo que você precisará fazer, será adicionar uma única linha ao seu Expert Advisor, indicador ou script que esteja sendo construído. Isto permitirá que o serviço de replay/simulador interrompa ou inicie a execução.
Muito bem, vamos ver no código original, o sistema de replay/simulador, o que precisará ser modificado. Então ao abrir a classe C_Controls, devemos nos dirigir ao procedimento DispatchMessage. Aqui precisamos adicionar o tratamento de eventos ou mensagens para que o serviço interrompa ou inicie seu trabalho. Bem, originalmente o código a ser modificado pode ser visto no fragmento logo abaixo:
179. //+------------------------------------------------------------------+ 180. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 181. { 182. short x, y; 183. static ushort iPinPosX = 0; 184. static short six = -1, sps; 185. uCast_Double info; 186. 187. switch (id) 188. { 189. case (CHARTEVENT_CUSTOM + evCtrlReplayInit): 190. info.dValue = dparam; 191. if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break; 192. iPinPosX = m_Slider.Minimal = (info._16b[eCtrlPosition] > def_MaxPosSlider ? def_MaxPosSlider : (info._16b[eCtrlPosition] < iPinPosX ? iPinPosX : info._16b[eCtrlPosition])); 193. SetPlay((eObjectControl)(info._16b[eCtrlStatus]) == ePlay); 194. break; 195. case CHARTEVENT_OBJECT_DELETE: 196. if (StringSubstr(sparam, 0, StringLen(def_ObjectCtrlName(""))) == def_ObjectCtrlName("")) 197. { 198. if (sparam == def_ObjectCtrlName(ePlay)) 199. { 200. delete m_Section[ePlay].Btn; 201. m_Section[ePlay].Btn = NULL; 202. SetPlay(m_Section[ePlay].state); 203. }else 204. { 205. RemoveCtrlSlider(); 206. CreateCtrlSlider(); 207. } 208. } 209. break; 210. case CHARTEVENT_MOUSE_MOVE: 211. if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft)) switch (CheckPositionMouseClick(x, y)) 212. { 213. case ePlay: 214. SetPlay(!m_Section[ePlay].state); 215. if (m_Section[ePlay].state) 216. { 217. RemoveCtrlSlider(); 218. m_Slider.Minimal = iPinPosX; 219. }else CreateCtrlSlider(); 220. break; 221. case eLeft: 222. PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal)); 223. break; 224. case eRight: 225. PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider)); 226. break; 227. case ePin: 228. if (six == -1) 229. { 230. six = x; 231. sps = (short)iPinPosX; 232. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 233. } 234. iPinPosX = sps + x - six; 235. PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX))); 236. break; 237. }else if (six > 0) 238. { 239. six = -1; 240. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 241. } 242. break; 243. } 244. ChartRedraw(GetInfoTerminal().ID); 245. } 246. //+------------------------------------------------------------------+
Fragmento original de C_Controls
Note que temos na linha 213, um filtro, a fim de saber onde está sendo dado o clique. Se estivermos clicando no botão de play, teremos a mudança de pause para play do serviço. Ou vice-versa, caso estivermos em modo play. Isto é conseguido ao executar a linha 214. Ok, então tudo que precisamos fazer é criar algum tipo de evento, neste procedimento acima. Isto de maneira que ao receber o evento, podemos dizer ao serviço de replay/simulador. Se desejamos dar play ou pausar o a execução do serviço. Simples assim.
Muito bem, para fazer isto, vamos ao arquivo Defines.mqh e incluiremos dois novos eventos. Conforme mostrado 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. }; 56. //+------------------------------------------------------------------+ 57. enum EnumPriority { //Priority list on objects 58. ePriorityNull = -1, 59. ePriorityDefault = 0 60. }; 61. //+------------------------------------------------------------------+
Defines.mqh
Note que nas linhas 54, está o novo evento a ser implementado. Quando você for fazer uso do mesmo, em seus códigos. Lembre-se de usar este arquivo Defines.mqh. Isto para que o compilador, saiba qual o valor correto a ser usado. Então em alguma parte do seu código, você deverá adicionar a seguinte linha:
EventChartCustom(0, evMsgServiceSwapStatus, 0, 0, "");
Isto quando desejar trocar o status do serviço de replay/simulador. Ou seja, caso o serviço esteja em execução ela irá ser interrompida, aguardando que receba permissão de prosseguir. Caso ele esteja pausado, irá receber permissão de prosseguir a execução. Note que é algo muito simples. Mas como o local exato e o motivo que será o gatilho, que de fato irá disparar tal linha. Depende de diversos fatores, que estarão ligados ao que você de fato, esteja implementando. Não tem como mostrar um exemplo prático aqui. Mas acredito, que aqueles que gostariam de poder efetuar tal tarefa, com toda a certeza. Saberão como e onde, de fato fazer uso desta linha mostrada acima. Lembrando que você não necessariamente precisa informar uma pause e um play. Muito provavelmente você fará uso mais massivo da mensagem de pause. Já que dar play no sistema, pode ser feito diretamente via interação do indicador de mouse, com o indicador de controle.
Agora vamos ver o que deverá ser adicionado, ou modificado, no fragmento mostrado acima. Isto para que de fato o indicador de controle consiga dizer ao serviço de replay/simulador o que deverá ser feito. Assim o novo fragmento, do procedimento DispatchMessage, da classe C_Controls, pode ser visto logo abaixo:
179. //+------------------------------------------------------------------+ 180. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 181. { 182. short x, y; 183. static ushort iPinPosX = 0; 184. static short six = -1, sps; 185. uCast_Double info; 186. 187. switch (id) 188. { 189. case (CHARTEVENT_CUSTOM + evMsgServiceSwapStatus): 190. SetPlay(!m_Section[ePlay].state); 191. if (m_Section[ePlay].state) 192. { 193. RemoveCtrlSlider(); 194. m_Slider.Minimal = iPinPosX; 195. }else CreateCtrlSlider(); 196. break; 197. case (CHARTEVENT_CUSTOM + evCtrlReplayInit): 198. info.dValue = dparam; 199. if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break; 200. iPinPosX = m_Slider.Minimal = (info._16b[eCtrlPosition] > def_MaxPosSlider ? def_MaxPosSlider : (info._16b[eCtrlPosition] < iPinPosX ? iPinPosX : info._16b[eCtrlPosition])); 201. SetPlay((eObjectControl)(info._16b[eCtrlStatus]) == ePlay); 202. break; 203. case CHARTEVENT_OBJECT_DELETE: 204. if (StringSubstr(sparam, 0, StringLen(def_ObjectCtrlName(""))) == def_ObjectCtrlName("")) 205. { 206. if (sparam == def_ObjectCtrlName(ePlay)) 207. { 208. delete m_Section[ePlay].Btn; 209. m_Section[ePlay].Btn = NULL; 210. SetPlay(m_Section[ePlay].state); 211. }else 212. { 213. RemoveCtrlSlider(); 214. CreateCtrlSlider(); 215. } 216. } 217. break; 218. case CHARTEVENT_MOUSE_MOVE: 219. if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft)) switch (CheckPositionMouseClick(x, y)) 220. { 221. case ePlay: 222. EventChartCustom(0, evMsgServiceSwapStatus, 0, 0, ""); 223. break; 224. case eLeft: 225. PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal)); 226. break; 227. case eRight: 228. PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider)); 229. break; 230. case ePin: 231. if (six == -1) 232. { 233. six = x; 234. sps = (short)iPinPosX; 235. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 236. } 237. iPinPosX = sps + x - six; 238. PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX))); 239. break; 240. }else if (six > 0) 241. { 242. six = -1; 243. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 244. } 245. break; 246. } 247. ChartRedraw(GetInfoTerminal().ID); 248. } 249. //+------------------------------------------------------------------+
Novo fragmento de C_Controls
Observe atentamente e você notará que estou fazendo a mesma coisa que foi feita durante todo este artigo. Ou seja, estou forçando o indicador de controle a receber uma mensagem criada por ele mesmo. Tal mensagem está sendo criada na linha 222. E será interceptada na linha 189. Isto garante que no momento em que seu Expert Advisor, disparar a mesma mensagem. O indicador de controle irá conseguir entender e responder de maneira adequada. Visto que o mecanismo é o mesmo. Porém, vale ressaltar alguns detalhes aqui. Como por exemplo: Se o seu Expert Advisor, disparar a mensagem no exato momento que você clicar no botão de pause do indicador de controle. Teremos uma dupla mensagem na fila, e isto irá fazer com que o serviço de uma pausa, para logo depois dar play. Ou vice versa. Você pode mudar este comportamento se desejar.
Mas como eu não estou mostrando como desenvolver este sistema de forma a ter um substituto ao testador de estratégia do MetaTrader 5. Não vejo muitos problemas nesta questão. É tudo uma questão de ter calma e saber que você poderá melhorar, ou modificar o sistema da maneira que mais lhe vier a parecer ser interessante.
Considerações Finais
Neste artigo, tentei explicar da forma o mais simples possível como você pode fazer uso de troca de mensagens entre aplicações. Isto para que consiga de fato, desenvolver algo realmente funcional e de maneira o mais simples e eficaz quando for possível ser feito. Não sei se de fato consegui passar a ideia por detrás do conceito. Já que ele não é tão simples de ser entendido e compreendido por parte de quem o está vendo pela primeira vez. Mostrei como você pode fazer para conseguir modificar o sistema de replay/simulador, a fim de poder depurar um Expert Advisor ou um outro código qualquer que você esteja criando. Isto de maneira igualmente simples e direta. Já que apliquei a mesma técnica descrita neste artigo. Isto para tornar a coisa ainda mais interessante.
Porém, como o objetivo desta série de artigos sobre como desenvolver um replay/simulador, visa única e exclusivamente fins didáticos. Não poderia deixar passar esta oportunidade de explicar um pouco, sobre o mecanismo que o código vem fazendo uso a um bom tempo. Isto para motivar aqueles que estão chegando agora, a lerem os artigos anteriores. E quem sabe, entre estes entusiastas e aspirantes a programadores, venhamos a ter alguém que realmente se destaque. Visto que o que estou explicando aqui, não é algo que alguém vai de fato vir a dominar da noite para o dia. Será preciso um longo período de estudos e trabalhos fazendo uso de tal mecanismo. Mas no final lhe garanto que valerá muito a pena estudar e se dedicar a entender tal conceito. E nos vemos no próximo artigo. Onde continuaremos a desenvolver mais um pouco o nosso sistema de replay/simulador. Desejo a todos bons estudos e até mais.
| 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.
MQL5 Trading Toolkit (Parte 4): Desenvolvendo uma Biblioteca EX5 de Gerenciamento de Histórico
Como publicar código no CodeBase: Guia prático
Criando um Painel de Administração de Trading em MQL5 (Parte VIII): Painel de Análises
Negociando com o Calendário Econômico do MQL5 (Parte 5): Aprimorando o Painel com Controles Responsivos e Botões de Filtro
- 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