
Simulação de mercado: Position View (XII)
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 (XI), expliquei como você pode conseguir movimentar as linhas de stop loss e take profit, de uma maneira supersimples e prática. Tendo refletido no gráfico exatamente o que está presente no servidor de negociação real. Porém como você claramente deve ter notado, existe uma dificuldade visível, que acomete o indicador de posição. Isto quando removemos tanto a linha de stop loss quanto a linha de take profit. Tal problema se deve ao fato de que não é possível verificar, e isto no gráfico, se estamos vendidos ou comprados. Sendo necessário abrir o terminal a fim de verificar tal fato. No entanto, podemos resolver isto de uma forma bastante simples e prática. E é isto que veremos inicialmente neste artigo. Então vamos ao primeiro tópico deste artigo.
Criando uma indicação da direção esperada
Talvez uma das coisas mais interessantes no que estou explicando como fazer. Não seja de fato os indicadores e o próprio sistema de replay/simulação. Mas sim, como modificar o próprio código a fim de obter novas funcionalidades, ou mesmo adaptar o que está sendo construído e implementado para que se possa obter algo particular e pessoal.
No artigo anterior, mostrei como você poderia com um mínimo de esforço, conseguir movimentar as linhas de stop loss e take profit. Muitos consideram tal coisa uma tarefa extremamente complexa e de difícil execução. Dada a quantidade de coisas a serem feitas. Se você não tiver os devidos cuidados, pode até mesmo ter informações sendo contraditórias. Isto por que você pode estar movendo a linha para um ponto. No entanto, isto não esteja sendo refletido no servidor. O que irá lhe fazer amargar muitas derrotas e pouco lucro em negociações reais. Devemos sempre pensar em mudar o mínimo possível o código. E quando fizer qualquer modificação nos certificar que elas de fato estejam de acordo com o esperado e o que realmente esteja acontecendo.
Seguindo esta mesma filosofia, de mudar o mínimo possível e tentar obter o máximo possível. Vamos ver como incluir no indicador de posição, alguma informação que nos permita saber se estamos em uma posição vendida ou comprada. E isto apenas olhando o gráfico.
Muito bem, a primeira coisa a ser feita é modificar a classe C_ElementsTrade. Isto para que possamos incluir algum objeto gráfico para nos informar tal coisa. Fazer isto é uma tarefa bastante simples e direta. No código abaixo, você pode ver o que a nova classe C_ElementsTrade, que permite justamente fazer tal coisa. Adicionar a indicação se estamos em uma posição comprada ou vendida.
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. //+------------------------------------------------------------------+ 009. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 010. //+------------------------------------------------------------------+ 011. #define def_PathBtns "Images\\Market Replay\\Orders\\" 012. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 013. #resource "\\" + def_Btn_Close; 014. //+------------------------------------------------------------------+ 015. #include "..\Auxiliar\C_Mouse.mqh" 016. //+------------------------------------------------------------------+ 017. class C_ElementsTrade : private C_Mouse 018. { 019. private : 020. //+------------------------------------------------------------------+ 021. struct st00 022. { 023. ulong ticket; 024. string szPrefixName, 025. szDescr; 026. EnumEvents ev; 027. double price; 028. bool bClick, 029. bIsBuy; 030. char weight; 031. color _color; 032. }m_Info; 033. //+------------------------------------------------------------------+ 034. void UpdateViewPort(void) 035. { 036. int x, y; 037. 038. ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y); 039. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 040. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 041. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 042. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 043. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 044. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + 18); 045. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 046. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30); 047. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 048. } 049. //+------------------------------------------------------------------+ 050. inline void CreateLinePrice(void) 051. { 052. string szObj; 053. 054. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 055. macro_LineInFocus(false); 056. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 057. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 058. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 059. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 060. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 061. } 062. //+------------------------------------------------------------------+ 063. inline void CreateBoxMove(void) 064. { 065. string szObj; 066. 067. CreateObjectGraphics(szObj = def_NameBtnMove, OBJ_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 068. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 069. ObjectSetString(0, szObj, OBJPROP_TEXT, "u"); 070. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, 17); 071. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 072. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, 21); 073. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, 23); 074. } 075. //+------------------------------------------------------------------+ 076. inline void CreateButtonClose(void) 077. { 078. string szObj; 079. 080. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 081. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 082. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 083. } 084. //+------------------------------------------------------------------+ 085. inline void CreateInfoDirect(void) 086. { 087. string szObj; 088. const char c[] = {(char)(m_Info.bIsBuy ? 236 : 238), 0}; 089. 090. CreateObjectGraphics(szObj = def_NameInfoDirect, OBJ_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 091. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 092. ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c)); 093. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, 15); 094. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 095. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, 21); 096. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, 23); 097. ObjectSetInteger(0, szObj, OBJPROP_COLOR, (m_Info.bIsBuy ? clrForestGreen : clrFireBrick)); 098. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 099. } 100. //+------------------------------------------------------------------+ 101. public : 102. //+------------------------------------------------------------------+ 103. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, string szDescr = "\n", const bool IsBuy = true) 104. :C_Mouse(0, "") 105. { 106. ZeroMemory(m_Info); 107. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 108. m_Info._color = _color; 109. m_Info.szDescr = szDescr; 110. m_Info.bIsBuy = IsBuy; 111. } 112. //+------------------------------------------------------------------+ 113. ~C_ElementsTrade() 114. { 115. ObjectsDeleteAll(0, m_Info.szPrefixName); 116. } 117. //+------------------------------------------------------------------+ 118. inline void UpdatePrice(const double open, const double price) 119. { 120. m_Info.price = (price > 0 ? price : open); 121. if (price > 0) 122. { 123. CreateLinePrice(); 124. CreateButtonClose(); 125. }else 126. ObjectsDeleteAll(0, m_Info.szPrefixName); 127. if (m_Info.ev != evMsgClosePositionEA) CreateBoxMove(); 128. else CreateInfoDirect(); 129. UpdateViewPort(); 130. } 131. //+------------------------------------------------------------------+ 132. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 133. { 134. string sz0; 135. 136. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 137. switch (id) 138. { 139. case CHARTEVENT_CUSTOM + evMsgSetFocus: 140. macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev)); 141. m_Info.bClick = false; 142. case CHARTEVENT_CHART_CHANGE: 143. UpdateViewPort(); 144. break; 145. case CHARTEVENT_OBJECT_CLICK: 146. sz0 = GetPositionsMouse().szObjNameClick; 147. if (m_Info.bClick) switch (m_Info.ev) 148. { 149. case evMsgClosePositionEA: 150. if (sz0 == def_NameBtnClose) 151. EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, ""); 152. break; 153. case evMsgCloseTakeProfit: 154. if (sz0 == def_NameBtnClose) 155. EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL)); 156. else if (sz0 == def_NameBtnMove) 157. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, ""); 158. break; 159. case evMsgCloseStopLoss: 160. if (sz0 == def_NameBtnClose) 161. EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL)); 162. else if (sz0 == def_NameBtnMove) 163. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, ""); 164. break; 165. } 166. m_Info.bClick = false; 167. break; 168. case CHARTEVENT_MOUSE_MOVE: 169. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 170. if (m_Info.bClick && (m_Info.weight > 1)) 171. { 172. switch (m_Info.ev) 173. { 174. case evMsgCloseTakeProfit: 175. EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 176. break; 177. case evMsgCloseStopLoss: 178. EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 179. break; 180. } 181. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 182. } 183. break; 184. } 185. } 186. //+------------------------------------------------------------------+ 187. }; 188. //+------------------------------------------------------------------+ 189. #undef macro_LineInFocus 190. //+------------------------------------------------------------------+ 191. #undef def_Btn_Close 192. #undef def_PathBtns 193. //+------------------------------------------------------------------+ 194. #undef def_NameInfoDirect 195. #undef def_NameBtnMove 196. #undef def_NameBtnClose 197. #undef def_NameHLine 198. //+------------------------------------------------------------------+
C_ElementsTrade.mqh
Para quem não está conseguindo visualizar as mudanças. Vou dar algum destaque em pontos bem específicos. Isto para deixar as coisas mais evidentes. No entanto, o ideal é que você, meu caro leitor, tente entender o código sem precisar ler as explicações que estarei dando. Isto para que você consiga se acostumar a também entender e capturar partes que possam vir a lhe interessar. Muito bem, na linha sete, temos a definição do nome que o objeto a ser criado irá ter. Na linha 29, definimos uma nova variável. Esta será subutilizada neste momento. Aparentando se de pouca necessidade ter sido declarada. Mas depois você entenderá melhor o motivo de ela ter sido declarada. Agora indo para o procedimento da linha 34, temos a inclusão de duas novas linhas. Que são as linhas 44 e 45. Esta tem como objetivo posicionar o objeto no gráfico.
Agora iremos dar um salto até a linha 85. Esta linha é que criará o objeto. Aqui neste procedimento CreateInfoDirect, temos um detalhe a ser compreendido. Caso você, meu caro leitor, decida usar uma imagem para representar uma posição de compra ou venda. Este procedimento visto a partir da linha 85, deverá sofrer algumas modificações. Nada muito complicado. Você pode fazer uso do código CreateButtonClose, como base, a fim de carregar a imagem a ser usada. Mas aqui vamos usar algo similar ao procedimento CreateBoxMove. Ou seja, iremos colocar um carácter para representar se estamos comprados ou vendido. Um detalhe: Neste momento, muito do código presente em CreateBoxMove, também irá aparecer neste CreateInfoDirect. Isto para que você, meu caro leitor e entusiasta consiga entender como criar as coisas. Em breve esta duplicata de código desaparecerá.
Bem, mas vamos entender o que está acontecendo aqui. Já que alguns pontos parecem bastante complicados e de difícil compreensão. Estilo o que está acontecendo na linha 88. Agora, responda sinceramente. Olhando esta linha 88, você consegue entender o que estou fazendo? Se sim, ótimo. Isto significa que você já sabe o que irei explicar. Se não, preste atenção. Na linha 92, que é onde iremos informar o texto para o objeto. Precisaremos passar um valor que seja uma string. Para fazer isto, usamos a chamada CharArrayToString, que faz parte do MQL5. Note o parâmetro que estamos passando na chamada. Ele é exatamente o que está sendo definido na linha 88.
Mas por que estou definindo o valor como sendo uma constante? O motivo é que quero garantir que não irei modificar acidentalmente o valor deste array. Quero garantir que ele será o mesmo durante toda a rotina. Mesmo que esta rotina seja simples. Pode acontecer no futuro de ela se tornar um pouco mais complicada. E não quero o correr risco de mudar acidentalmente o valor do array c. Agora vem a mágica. Uma string em MQL5, segue o mesmo princípio de strings em C/C++, ou seja, elas terminam em um carácter NULO, ou \0. Este é o valor zero que você vê nesta linha 88. Por conta que teremos dois e somente dois carácteres dentro da string. O array c, tem uma dimensão de dois, mas o estamos deixando com uma dimensão dinâmica. Mas isto somente é possível, por conta que estou definindo logo de cara, o conteúdo do array. Caso isto não fosse feito desta maneira o código iria falhar durante a compilação. Então é preciso usar justamente a sintaxe vista na linha.
Ou seja, depois de definido o nome e o fato de ser um array. Temos o sinal de igualdade, seguido imediatamente por um abre chaves. Logo depois, usamos uma conversão explicita de tipos. Isto para evitar que o compilador gere avisos durante a compilação. A seguir temos um operador ternário sendo executado. Aqui é onde usamos o valor daquela variável definida na linha 29. Se ela for verdadeira, o primeiro carácter da string será o símbolo cujo valor é 236, se for falso, o valor do símbolo será 238. Preste atenção a isto. Não estou dizendo que teremos o valor 236 ou 238 sendo parte da string. Não é isto. O que de fato estou fazendo é dizendo ao compilador para usar o símbolo 236 ou 238 de uma tabela de símbolos. Normalmente usamos a tabela ASCII, mas aqui iremos usar uma tabela diferente. Para saber que símbolo é este que estou pedindo ao compilador para usar, basta olhar na linha 91, onde dizemos qual será a fonte usada para imprimir a string na tela. Note que a fonte é Wingdings. Então bastará você pegar a tabela de símbolos Wingdings e procurar os símbolos cujos valores são 236 e 238. E você terá ideia do que será impresso na tela. Simples assim. Já todo o restante do procedimento é algo notório e constantemente visto quando programados em MQL5. Isto para conseguir colocar objetos no gráfico via programação MQL5. Não merecendo assim nenhum destaque extra.
Bem, você deve estar imaginando: Mas como iremos informar a classe se é para desenhar um símbolo de compra ou venda? A resposta é simples. Vamos fazer isto no momento que o constructor vier a ser chamado. Por isto observe na linha 103, o código do constructor. Note que ele passou por uma leve mudança. Mas nada assim tão grandioso que mereça uma explicação. Porém, pelo fato de que este contructor foi modificado, precisamos também modificar o código que faz uso dele. Mas antes de vermos isto, vamos dar uma última olhada no que mudou nesta classe. Estou me referindo ao código presente na linha 118. Aqui fizemos uma pequena mudança. Esta é justamente a linha 128. Apesar de não ser algo assim tão extraordinário, aqui temos uma diferenciação entre as linhas do preço de abertura, das demais linhas. Que são as de take profit e stop loss.
Observe que na linha 127 temos um teste, a fim de identificar se estamos lidando, dentro da classe, com a linha de preço ou uma das outras linhas. Caso, seja a linha de preço, não podermos, ou melhor, não deveremos criar o objeto de movimentação. Neste caso, o que será executado, é justamente a criação do objeto que indica se estamos comprados ou vendidos. E com isto podemos encerrar o código da classe C_ElementsTrade. Mas como eu havia falado a pouco, houve uma mudança no código de chamada do constructor. Assim precisamos modificar o código da classe C_IndicatorPosition. Mas como grande parte do código se manteve intacto. Vamos focar apenas no que mudou. Assim sendo, a parte que sofreu mudança, pode ser vista no fragmento logo abaixo.
45. //+------------------------------------------------------------------+ 46. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 47. { 48. double value; 49. 50. if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam); 51. if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam); 52. if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam); 53. switch (id) 54. { 55. case CHARTEVENT_CUSTOM + evUpdate_Position: 56. if (lparam != m_Infos.ticket) return; 57. if (!PositionSelectByTicket(m_Infos.ticket)) 58. { 59. ChartIndicatorDelete(0, 0, m_Infos.szShortName); 60. return; 61. }; 62. if (Open == NULL) Open = new C_ElementsTrade(m_Infos.ticket, evMsgClosePositionEA, clrRoyalBlue, StringFormat("%I64u : Position opening price.", m_Infos.ticket), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY); 63. if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, clrForestGreen, StringFormat("%I64u : Take Profit price.", m_Infos.ticket)); 64. if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, clrFireBrick, StringFormat("%I64u : Stop Loss price.", m_Infos.ticket)); 65. (*Open).UpdatePrice(0, value = PositionGetDouble(POSITION_PRICE_OPEN)); 66. (*Take).UpdatePrice(value, PositionGetDouble(POSITION_TP)); 67. (*Stop).UpdatePrice(value, PositionGetDouble(POSITION_SL)); 68. break; 69. } 70. ChartRedraw(); 71. } 72. //+------------------------------------------------------------------+
Fragmento de C_IndicatorPosition.mqh
Note como a mudança é extremamente sutil. E ela acontece justamente nas linhas 62 a 64. Somente isto foi modificado frente ao que existia antes. E quando você compilar o indicador de posição. E ele for jogado no gráfico, você terá como resultado algo parecido com o visto na imagem abaixo.
Nesta imagem, você pode ver onde está a diferença, que é justamente a região destacada. Agora temos a indicação clara e nítida se estamos comprados ou vendido. Legal não é mesmo? Foi algo bastante simples de ser feito. Mas antes de partimos para a próxima coisa, vamos ver como o código final da classe C_ElementsTrade ficou depois de removermos as partes duplicadas. Isto é visto 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. //+------------------------------------------------------------------+ 009. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 010. //+------------------------------------------------------------------+ 011. #define def_PathBtns "Images\\Market Replay\\Orders\\" 012. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 013. #resource "\\" + def_Btn_Close; 014. //+------------------------------------------------------------------+ 015. #include "..\Auxiliar\C_Mouse.mqh" 016. //+------------------------------------------------------------------+ 017. class C_ElementsTrade : private C_Mouse 018. { 019. private : 020. //+------------------------------------------------------------------+ 021. struct st00 022. { 023. ulong ticket; 024. string szPrefixName, 025. szDescr; 026. EnumEvents ev; 027. double price; 028. bool bClick, 029. bIsBuy; 030. char weight; 031. color _color; 032. }m_Info; 033. //+------------------------------------------------------------------+ 034. void UpdateViewPort(void) 035. { 036. int x, y; 037. 038. ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y); 039. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 040. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 041. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 042. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 043. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 044. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + 18); 045. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 046. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30); 047. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 048. } 049. //+------------------------------------------------------------------+ 050. inline void CreateLinePrice(void) 051. { 052. string szObj; 053. 054. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 055. macro_LineInFocus(false); 056. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 057. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 058. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 059. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 060. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 061. } 062. //+------------------------------------------------------------------+ 063. inline void CreateBoxInfo(const bool bMove) 064. { 065. string szObj; 066. const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0}; 067. 068. CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 069. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 070. ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c)); 071. ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? m_Info._color : (m_Info.bIsBuy ? clrForestGreen : clrFireBrick))); 072. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15)); 073. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 074. } 075. //+------------------------------------------------------------------+ 076. inline void CreateButtonClose(void) 077. { 078. string szObj; 079. 080. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 081. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 082. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 083. } 084. //+------------------------------------------------------------------+ 085. public : 086. //+------------------------------------------------------------------+ 087. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, string szDescr = "\n", const bool IsBuy = true) 088. :C_Mouse(0, "") 089. { 090. ZeroMemory(m_Info); 091. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 092. m_Info._color = _color; 093. m_Info.szDescr = szDescr; 094. m_Info.bIsBuy = IsBuy; 095. } 096. //+------------------------------------------------------------------+ 097. ~C_ElementsTrade() 098. { 099. ObjectsDeleteAll(0, m_Info.szPrefixName); 100. } 101. //+------------------------------------------------------------------+ 102. inline void UpdatePrice(const double open, const double price) 103. { 104. m_Info.price = (price > 0 ? price : open); 105. if (price > 0) 106. { 107. CreateLinePrice(); 108. CreateButtonClose(); 109. }else 110. ObjectsDeleteAll(0, m_Info.szPrefixName); 111. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 112. UpdateViewPort(); 113. } 114. //+------------------------------------------------------------------+ 115. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 116. { 117. string sz0; 118. 119. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 120. switch (id) 121. { 122. case CHARTEVENT_CUSTOM + evMsgSetFocus: 123. macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev)); 124. m_Info.bClick = false; 125. case CHARTEVENT_CHART_CHANGE: 126. UpdateViewPort(); 127. break; 128. case CHARTEVENT_OBJECT_CLICK: 129. sz0 = GetPositionsMouse().szObjNameClick; 130. if (m_Info.bClick) switch (m_Info.ev) 131. { 132. case evMsgClosePositionEA: 133. if (sz0 == def_NameBtnClose) 134. EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, ""); 135. break; 136. case evMsgCloseTakeProfit: 137. if (sz0 == def_NameBtnClose) 138. EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL)); 139. else if (sz0 == def_NameBtnMove) 140. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, ""); 141. break; 142. case evMsgCloseStopLoss: 143. if (sz0 == def_NameBtnClose) 144. EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL)); 145. else if (sz0 == def_NameBtnMove) 146. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, ""); 147. break; 148. } 149. m_Info.bClick = false; 150. break; 151. case CHARTEVENT_MOUSE_MOVE: 152. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 153. if (m_Info.bClick && (m_Info.weight > 1)) 154. { 155. switch (m_Info.ev) 156. { 157. case evMsgCloseTakeProfit: 158. EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 159. break; 160. case evMsgCloseStopLoss: 161. EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 162. break; 163. } 164. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 165. } 166. break; 167. } 168. } 169. //+------------------------------------------------------------------+ 170. }; 171. //+------------------------------------------------------------------+ 172. #undef macro_LineInFocus 173. //+------------------------------------------------------------------+ 174. #undef def_Btn_Close 175. #undef def_PathBtns 176. //+------------------------------------------------------------------+ 177. #undef def_NameInfoDirect 178. #undef def_NameBtnMove 179. #undef def_NameBtnClose 180. #undef def_NameHLine 181. //+------------------------------------------------------------------+
C_ElementsTrade.mqh
E como uma forma de lhe incentivar a estudar, não explicarei o que foi modificado e por que foi modificado. Quero que você estude e procure entender o que eu fiz neste código acima a fim de remover as partes duplicadas. Fica então como um dever de casa, entender por que estas mudanças não afetaram o que é visto no gráfico.
Mas antes de terminar este artigo. Vamos promover mais uma atualização ao sistema. Isto para que as coisas se tornem ainda mais interessantes. Bem a tal atualização visa nos permitir saber se estaremos ajustando o take profit ou stop loss. Mas vamos ver isto em um novo tópico.
O que estou movendo: O Take Profit ou Stop Loss?
Não é nada incomum você vir a ficar na dúvida se está de fato movendo o take profit ou stop loss. Isto depois de clicar no objeto que permite mover tais linhas. Na verdade a dúvida pode até mesmo surgir, depois de clicar no gráfico. Já que não existe até o presente momento nenhuma forma de sabermos se estamos movendo uma das linhas de preço. Ou se simplesmente estamos movendo o mouse pelo gráfico. E se estivermos movendo uma linha de preço e nos esquecermos de clicar novamente, poderemos ter uma surpresa desagradável ao clicar no gráfico em uma dada posição. Então para resolver este problema, vamos melhorar inda mais as coisas. Se bem, que neste primeiro momento a melhoria será bem simples. Visando apenas nos alertar que estamos movendo uma das linhas de preço. Seja ela o take profit, seja ela o stop loss.
Muito bem, você meu caro leitor, neste momento pode estar pensando o seguinte: Agora sim temos um problema que vai dar bastante trabalho ser devidamente resolvido. Mas não é assim de fato tão trabalhoso. Isto se você estiver acompanhando esta série de artigos sobre o replay/simulador vai entender de forma bem rápida o que será feito. Mas antes de fazer isto. É preciso que você entenda como as coisas estão acontecendo aqui.
Vou dar uma resumida básica, mas aconselho que você, caso não tenha lido os artigos anteriores, faça isto para entender as coisas por completo. Caso contrário não conseguirá entender absolutamente nada do que farei daqui a pouco.
Todas as interações que estão ocorrendo, estão sendo feitas e corretamente aceitas, justamente pelo fato de termos no gráfico, diversas coisas que trabalham em conjunto. Ou seja, temos o indicador de mouse, o Expert Advisor, o indicador de posição. Além é claro o indicador Chart Trade. Porém este último não fará parte do que faremos daqui a pouco. Ok, quando usamos o indicador de mouse e clicamos, seja no objeto de finalizar a linha, seja a linha de preço, seja a posição. Temos as ações sendo executadas pelo Expert Advisor, porém refletidas no gráfico pelo indicador de posição. Agora quando clicamos no objeto para mover as linhas de take profit ou stop loss. Fazemos isto movendo o indicador de mouse e no final devemos dar um novo clique a fim de que o Expert Advisor, venha a ter consciência de que é preciso mudar a posição, seja do take profit, seja do stop loss.
Basicamente o que fazemos é dizer via indicador de mouse, que o preço se moveu. Mas o Expert Advisor receberá a informação do indicador de posição. Porém até que o requerimento de mudanças do preço de fato seja executado pelo servidor de negociação. A linha de preço não irá se mover. Quando o servidor reportar ao Expert Advisor que um novo preço foi informado a linha, seja ela de take profit, seja de stop loss. O Expert Advisor informará ao indicador de posição que ele deverá ser atualizado. Sendo que neste momento é que realmente a linha irá se mover no gráfico. Perfeito. Então tudo que temos, é uma interação entre as aplicações que estão sendo executadas no gráfico. E dependendo da forma como está interação acontece, teremos uma determinada resposta sendo apresentada pelo MetaTrader 5, ao usuário.
Então a primeira coisa que precisamos fazer é dizer a estas mesmas aplicações, de que algo precisa ser modificado. Mas devemos fazer isto, sem mexer no que já esteja funcionando. Para simplificar e permitir que você, meu caro leitor compreenda o que estará acontecendo de fato. Vamos fazer as coisas com bastante calma. Primeiro, vamos fazer o seguinte: Quando o usuário clicar em um dos objetos que permitem mover a linha de preço, iremos ocultar, a linha de preço do mouse. Assim que o usuário indicar onde será a nova posição, seja do take profit ou stop loss. Iremos mostrar de volta a linha de preço do mouse. Simples assim.
Mas se você não estiver acompanhando esta série de artigos. Pode até ficar tentado a mexer nos objetos presentes no gráfico. E isto apesar de não está errado, será considerado um erro. Devido ao fato de que não devemos manipular tais objetos. E sim informar as aplicações para que elas mudem de comportamento. Este é o caminho correto a ser seguido. Assim sendo, para conseguir tal coisa que acabei de explicar. Será preciso mudar um pouquinho o código presente na classe C_ElementsTrade. A mudança a ser feita é tão, mas tão sutil. Que se você não prestar atenção não conseguirá notar ela. Mas ela pode ser vista no fragmento logo abaixo.
114. //+------------------------------------------------------------------+ 115. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 116. { 117. string sz0; 118. 119. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 120. switch (id) 121. { 122. case CHARTEVENT_CUSTOM + evMsgSetFocus: 123. macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev)); 124. EventChartCustom(0, (ushort)(dparam ? evHideMouse : evShowMouse), 0, 0, ""); 125. m_Info.bClick = false; 126. case CHARTEVENT_CHART_CHANGE: 127. UpdateViewPort(); 128. break; 129. case CHARTEVENT_OBJECT_CLICK: 130. sz0 = GetPositionsMouse().szObjNameClick; 131. if (m_Info.bClick) switch (m_Info.ev) 132. {
Fragmento de C_ElementsTrade
Neste fragmento acima, temos exatamente a mudança a ser feita. E é isto mesmo, estamos fazendo a mudança no procedimento DispatchMessage. Dificilmente você, se não prestar bastante atenção notará onde está a modificação. Ela está justamente na linha 124. Esta linha diz ao indicador de mouse, se é para ele apresentar ou ocultar a linha de preço. Simples assim. Mas note o fato de termos uma declaração ushort nesta chamada. Esta declaração efetua explicitamente a conversão de tipos. Normalmente as enumerações são do tipo int. Porém precisamos converter o valor para o tipo ushort, caso contrário o compilador irá nos alerta de uma possível perda de dados. Precisamos dizer ao compilador que estamos cientes disto. E fazemos isto, convertendo o tipo que normalmente é int para ushort.
Isto conclui a primeira parte do que desejamos fazer. Ou seja, agora quando estivermos em modo de movimentar a linha de preço. A linha horizontal do indicador de mouse irá ser ocultada. Caso contrário ela será apresentada no gráfico. Ponto número um concluído. Agora vamos ao ponto número dois. Este é feito da seguinte maneira. Iremos substituir a linha de preço do mouse, por uma outra coisa. No caso, uma falsa linha de preço, que representará a linha que estaremos movendo. Para fazer isto, precisamos fazer um pouco mais de mudanças no código. Mas ainda assim, permaneceremos na classe C_ElementsTrade.
Muito bem, vamos pensar um pouco. Como queremos representar esta linha falsa? Na minha opinião, a melhor maneira é usando as cores originais das linhas. Assim teremos a nítida sensação de que estamos movendo a linha correta. Mas tem um outro detalhe. Mover apenas a linha ao meu ver parece algo pouco complicado. Algo um tanto sem graça de ser feito. Que tal complicar um pouco, movendo também os demais controles, presentes na linha. Como o botão de fechar e o objeto de movimento? Hum, agora me parece um pouco mais desafiador. Mas será mesmo? Bem, vamos ver. Se a ideia é mover todos os objetos junto com a linha de preço, bastará que informemos onde é o novo preço. Hum, da forma como o código se encontra isto não será possível de ser feito. Porém e se mudarmos o procedimento UpdateViewPort de forma que ele receba o preço? Assim o novo código pode ser visto no fragmento logo abaixo.
33. //+------------------------------------------------------------------+ 34. void UpdateViewPort(const double price) 35. { 36. int x, y; 37. 38. ChartTimePriceToXY(0, 0, 0, price, x, y); 39. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 40. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 41. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 42. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 43. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 44. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + 18); 45. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 46. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30); 47. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 48. } 49. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Ok. Com isto precisamos atualizar o procedimento que atualiza o preço. Este é visto no fragmento logo abaixo.
101. //+------------------------------------------------------------------+ 102. inline void UpdatePrice(const double open, const double price) 103. { 104. if (price > 0) 105. { 106. CreateLinePrice(); 107. CreateButtonClose(); 108. }else 109. ObjectsDeleteAll(0, m_Info.szPrefixName); 110. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 111. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 112. } 113. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Bem, mas e daí. Fazer isto não resolve de fato a questão. Não conseguimos mover o preço como esperado. Mas se modificarmos agora o procedimento DispatchMessage conforme é visto abaixo. A coisa muda de figura.
113. //+------------------------------------------------------------------+ 114. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 115. { 116. string sz0; 117. 118. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 119. switch (id) 120. { 121. case CHARTEVENT_CUSTOM + evMsgSetFocus: 122. macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev)); 123. EventChartCustom(0, (ushort)(dparam ? evHideMouse : evShowMouse), 0, 0, ""); 124. m_Info.bClick = false; 125. case CHARTEVENT_CHART_CHANGE: 126. UpdateViewPort(m_Info.price); 127. break; 128. case CHARTEVENT_OBJECT_CLICK: 129. sz0 = GetPositionsMouse().szObjNameClick; 130. if (m_Info.bClick) switch (m_Info.ev) 131. { 132. case evMsgClosePositionEA: 133. if (sz0 == def_NameBtnClose) 134. EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, ""); 135. break; 136. case evMsgCloseTakeProfit: 137. if (sz0 == def_NameBtnClose) 138. EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL)); 139. else if (sz0 == def_NameBtnMove) 140. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, ""); 141. break; 142. case evMsgCloseStopLoss: 143. if (sz0 == def_NameBtnClose) 144. EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL)); 145. else if (sz0 == def_NameBtnMove) 146. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, ""); 147. break; 148. } 149. m_Info.bClick = false; 150. break; 151. case CHARTEVENT_MOUSE_MOVE: 152. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 153. if (m_Info.weight > 1) 154. { 155. UpdateViewPort(GetPositionsMouse().Position.Price); 156. if (m_Info.bClick) 157. { 158. switch (m_Info.ev) 159. { 160. case evMsgCloseTakeProfit: 161. EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 162. break; 163. case evMsgCloseStopLoss: 164. EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 165. break; 166. } 167. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 168. } 169. } 170. break; 171. } 172. } 173. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Agora preste atenção ao seguinte fato. Quando uma linha estiver selecionada, a variável weight terá um valor maior que um. Então o teste na linha 153 irá ter sucesso, permitindo assim que o bloco de código seja executado. Dentro deste bloco, temos a linha 155, que faz justamente a chamada ao procedimento UpdateViewPort. Informando a este o novo possível ponto onde a linha de preço deverá estar. Isto é feito em tempo real. Mas a atualização final, somente se dará no momento em que o clique for dado. E assim que o servidor retornar dizendo qual é o preço correto, tudo continuará sendo feito como era antes. Ou seja, mudamos o código, mas seu funcionamento permaneceu praticamente inalterado pelo ponto de visto de conformidade.
Virão que não foi algo de fato nada complicado de ser feito. Tudo que foi preciso de fato ser feito, foi ajustar algumas poucas coisas. E o código começou a fazer o que era esperado, ou desejada. Nada desafiador. Para dizer a verdade foi até um pouco monótono. Mas agora podemos de fato complicar a coisa de verdade. Ou será que não? Bem, vamos pensar no seguinte fato. E se durante a movimentação da linha de preço. O operador resolva executar um estudo. Bem isto deveria invalidar a movimentação que estaria sendo feita. Forçando os objetos a retornarem ao seu ponto de origem. Muitos pode pensar que isto é muito complicado de ser feito. Já que os valores mudaram durante os primeiros movimentos executados. Mas vamos ver como solucionar este problema.
Muito bem, primeiro, quando começamos a mover uma das linhas de preço. Seja o take profit ou Stop loss. Ficamos proibidos de iniciar um estudo. Mas caso operador deseje fazer um estudo, usando uma modificação feita no indicador de mouse. Para saber como fazer isto, veja os artigos passados. Ele precisará recorrer ao cancelamento da movimentação. Fazer isto envolve pensar em como você deseja cancelar a movimentação. Conversando com algumas pessoas, eles sugeriram fazer uso de algo bastante prático e seguro. Pressionar a tecla ESC. Pois pode acontecer de você acidentalmente pressionar o botão do mouse ou outra coisa qualquer. Mas a tecla ESC é algo que você não pressionará sem notar. Então vamos modificar novamente o procedimento DispatchMessage para permitir isto. Assim a movimentação será cancelada e você poderá fazer outra coisa que desejar.
113. //+------------------------------------------------------------------+ 114. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 115. { 116. string sz0; 117. long _lparam = lparam; 118. double _dparam = dparam; 119. 120. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 121. switch (id) 122. { 123. case (CHARTEVENT_KEYDOWN): 124. if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break; 125. _lparam = (long) m_Info.ticket; 126. _dparam = 0; 127. case CHARTEVENT_CUSTOM + evMsgSetFocus: 128. macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)); 129. EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, ""); 130. m_Info.bClick = false; 131. case CHARTEVENT_CHART_CHANGE: 132. UpdateViewPort(m_Info.price); 133. break; 134. case CHARTEVENT_OBJECT_CLICK: 135. sz0 = GetPositionsMouse().szObjNameClick; 136. if (m_Info.bClick) switch (m_Info.ev)
Fragmento de C_ElementsTrade
No fragmento acima está a solução. Não coloquei todo o código do procedimento DispatchMessage, pois é algo totalmente desnecessário ao meu ver. Já que a solução pode ser facilmente vista dentro deste fragmento. Note que foi necessário adicionarmos duas novas variáveis locais. Estas evitam que venhamos a modificar acidentalmente e de forma bem perigosa, os valores informados pelo MetaTrader 5. Estas variáveis estão nas linhas 117 e 118. Mas agora vem uma pergunta, que é bastante válida. Por que você não coloca os argumentos da chamada, isto na linha 114 sem serem constantes. Assim não necessitaria de usar tais variáveis? De fato, isto seria uma solução em diversos casos. Mas por experiência, sei que não é uma boa ideia fazer isto.
O motivo é que muitas das vezes você fica tentado em mudar o valor. E em algum momento no futuro, se depara com o fato do código não está respondendo de maneira adequada aos dados informados pela plataforma MetaTrader 5. Seja pelo sistema operacional. Pois dentro do sistema operacional Windows usamos esta mesma estrutura para passar informações entre aplicações, ou processos diferentes. É mais comum ver isto quando se programa DLLs. Por isto não aconselho você a fazer tal mudança. Prefira sempre modificar a coisa localmente como estou mostrando. Isto irá lhe poupar muitas dores de cabeça e tempo perdido tentando entender por que as coisas não estão funcionando de maneira adequada.
Pois bem, uma vez feito isto, você pode notar que na linha 123, estamos capturando agora eventos de teclado. Existem diversas formas de tratar tais eventos e os filtrar. Mas aqui no MetaTrader 5, a forma mais simples e prática de tratar teclas especiais. É justamente usando o que é visto na linha 124. Atenção ao seguinte fato: Quando a tecla ESC não estiver pressionada teremos o enceramento do tratamento de evento do teclado. Ou seja, caso você selecione algo a ser movido, e pressione por exemplo a tecla F2. O código de tratamento finalizará nesta linha 124. Isto por conta do comando break. Porém, no momento em que o MetaTrader 5, nos informar que de fato a tecla ESC foi pressionada de forma válida. Isto para a nossa aplicação, teremos a execução das linhas 125 até a linha 133. Note que um evento de teclado, que foi informado pelo MetaTrader 5, onde a tecla ESC foi pressionada. Irá invadir de forma intencional, dois outros tratadores de eventos. Isto evita que precisemos duplicar código de forma desnecessária.
Considerações finais
Bem, por hora acredito que já temos bastante coisa sendo feita. A ponto de você, meu caro e estimado leitor, tem muito para aprender e testar. Mas como alguns de vocês, talvez não saibam como criar estes códigos usados. No anexo deixo, os arquivos já compilados. Isto para que você, que ainda está iniciando, possa ter, pelo menos uma visão, do que esperar ao colocar tudo que foi explicado até aqui para funcionar.
Lembrando que tais coisas mostradas aqui, tem como objetivo serem o mais didáticas quanto possível. Devendo se tomar o máximo cuidado, ao se tentar usar as aplicações do anexo, em uma conta real. Minha sugestão é: Use em contas demo. Aprenda como o sistema funciona e modifique o mesmo conforme as suas necessidades particulares.
E nos vemos no próximo artigo, onde continuaremos dando mais um passo em direção a um sistema mais completo.
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.





- 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