
Simulação de mercado: Position View (XI)
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 (X), expliquei como poderíamos conseguir selecionar os objetos que estamos criando. Isto tendo auxílio do MetaTrader 5. Já que a principal e maior preocupação até o momento, era poder encontrar uma maneira satisfatória de conseguir selecionar os objetos no gráfico. Sem o auxílio do MetaTrader 5, isto usando a propriedade ZOrder. Tal tarefa seria bastante complicada e de difícil solução. Mas como ficou provado durante a explicação do artigo anterior. Obtivemos uma maneira bastante prática e eficaz de usar o MetaTrader 5 a nosso favor.
Porém, da mesma forma que conseguimos obter aquele resultado. Também acredito ter ficado claro, que temos um outro problema em mãos. E este é justamente selecionar as linhas de preço. Mesmo quando elas estão relativamente mais espessas, ainda assim não é uma tarefa das mais simples. Isto quando precisamos fazer tal tarefa de forma bastante rápida e com as linhas bem próximas umas das outras.
E poder fazer a correta seleção da linha desejada, e isto de forma o mais rápido possível. Além de ser algo extremamente necessário, é algo que precisamos pensar com carinho. Isto por que não só iremos de fato selecionar a linha. Mas também queremos promover a criação da linha de preço, seja ele de take profit ou stop loss, diretamente no gráfico. Isto via uma interação que deverá ser a mais simples quanto for possível ser implementada. Isto não pelo ponto de vista do código, mas sim pelo ponto de vista do operador.
No passado distante, mostrei aqui em artigos um modelo que ao meu ver é bastante adequado ao que precisamos. Porém, aquele modelo, estava completamente ligado ao Expert Advisor. Sendo algo bastante complicado remover das entranhas do Expert Advisor. Mas como ele é bastante interessante para o que precisamos. Irei mostrar aqui como implementar o mesmo. No entanto, agora faremos isto em um indicador. Então vamos começar.
Criando um botão para mover os preços
A ideia naquele modelo bastante antigo. Era o uso de um botão próprio para permitir que pudéssemos selecionar a linha desejada. Isto além de nos permitir selecionar a linha, nos permitia também criar a linha, caso ela não existisse no gráfico. Além de outros pequenos afazeres que também desenvolveremos neste artigo. Assim a primeira coisa a ser feita, é vista no código 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. //+------------------------------------------------------------------+ 008. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 009. //+------------------------------------------------------------------+ 010. #define def_PathBtns "Images\\Market Replay\\Orders\\" 011. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 012. #resource "\\" + def_Btn_Close; 013. //+------------------------------------------------------------------+ 014. #include "..\Auxiliar\C_Mouse.mqh" 015. //+------------------------------------------------------------------+ 016. class C_ElementsTrade : private C_Mouse 017. { 018. private : 019. //+------------------------------------------------------------------+ 020. struct st00 021. { 022. ulong ticket; 023. string szPrefixName; 024. EnumEvents ev; 025. double price; 026. bool bClick; 027. char weight; 028. }m_Info; 029. //+------------------------------------------------------------------+ 030. void UpdateViewPort(void) 031. { 032. int x, y; 033. 034. ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y); 035. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 036. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 037. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 038. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 039. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 040. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30); 041. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 042. } 043. //+------------------------------------------------------------------+ 044. inline void CreateLinePrice(const color _color, const string szDescr) 045. { 046. string szObj; 047. 048. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, _color, (EnumPriority)(ePriorityDefault)); 049. macro_LineInFocus(false); 050. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, _color); 051. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 052. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 053. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 054. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, szDescr); 055. } 056. //+------------------------------------------------------------------+ 057. inline void CreateBoxMove(const color _color) 058. { 059. string szObj; 060. 061. CreateObjectGraphics(szObj = def_NameBtnMove, OBJ_LABEL, _color, (EnumPriority)(ePriorityDefault)); 062. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 063. ObjectSetString(0, szObj, OBJPROP_TEXT, "u"); 064. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, 17); 065. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 066. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, 21); 067. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, 23); 068. } 069. //+------------------------------------------------------------------+ 070. inline void CreateButtonClose(void) 071. { 072. string szObj; 073. 074. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 075. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 076. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 077. } 078. //+------------------------------------------------------------------+ 079. public : 080. //+------------------------------------------------------------------+ 081. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, EnumPriority ePrio, string szDescr = "\n") 082. :C_Mouse(0, "") 083. { 084. ZeroMemory(m_Info); 085. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 086. CreateLinePrice(_color, szDescr); 087. if (ev != evMsgClosePositionEA) CreateBoxMove(_color); 088. CreateButtonClose(); 089. } 090. //+------------------------------------------------------------------+ 091. ~C_ElementsTrade() 092. { 093. ObjectsDeleteAll(0, m_Info.szPrefixName); 094. } 095. //+------------------------------------------------------------------+ 096. inline void UpdatePrice(const double price) 097. { 098. m_Info.price = price; 099. UpdateViewPort(); 100. } 101. //+------------------------------------------------------------------+ 102. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 103. { 104. string sz0; 105. 106. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 107. switch (id) 108. { 109. case CHARTEVENT_CUSTOM + evMsgSetFocus: 110. macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev)); 111. m_Info.bClick = false; 112. case CHARTEVENT_CHART_CHANGE: 113. UpdateViewPort(); 114. break; 115. case CHARTEVENT_OBJECT_CLICK: 116. sz0 = GetPositionsMouse().szObjNameClick; 117. if (m_Info.bClick) switch (m_Info.ev) 118. { 119. case evMsgClosePositionEA: 120. if (sz0 == def_NameBtnClose) 121. EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, ""); 122. break; 123. case evMsgCloseTakeProfit: 124. if (sz0 == def_NameBtnClose) 125. EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL)); 126. else if (sz0 == def_NameBtnMove) 127. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, ""); 128. break; 129. case evMsgCloseStopLoss: 130. if (sz0 == def_NameBtnClose) 131. EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL)); 132. else if (sz0 == def_NameBtnMove) 133. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, ""); 134. break; 135. } 136. m_Info.bClick = false; 137. break; 138. case CHARTEVENT_MOUSE_MOVE: 139. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 140. if (m_Info.bClick && (m_Info.weight > 1)) EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 141. break; 142. } 143. } 144. //+------------------------------------------------------------------+ 145. }; 146. //+------------------------------------------------------------------+ 147. #undef macro_LineInFocus 148. //+------------------------------------------------------------------+ 149. #undef def_Btn_Close 150. #undef def_PathBtns 151. //+------------------------------------------------------------------+ 152. #undef def_NameBtnMove 153. #undef def_NameBtnClose 154. #undef def_NameHLine 155. //+------------------------------------------------------------------+
C_ElementsTrade.mqh
Aqui começamos a fazer as coisas ficarem ainda mais agradáveis. Note que na linha seis, criamos uma nova definição. Esta irá nomear o objeto que representará o nosso ícone de movimentação. Quando ele não estiver presente, não poderemos movimentar a linha. Quando ele existir a linha poderá ser movimentada. Porém neste momento não vamos nos preocupar com a questão da movimentação. Primeiro vamos ver como trabalhar a seleção.
Observe que o código sofreu muitas poucas mudanças. Isto para que você, meu caro leitor, consiga de fato entender o passo a passo. E assim caso deseje, possa facilmente modificar o que estou mostrando.
Na linha 40, indicamos a localização do nosso novo objeto no eixo X. Repare que estou dando um leve espaço entre ele e o botão de fechar. Agora preste atenção. Na linha 35, modifiquei o deslocamento dos objetos. Isto para que eles não venham a se sobrepor de maneira alguma. Com isto, temos um pequeno espaço entre eles de forma a não termos problemas quando um indicador estiver no gráfico.
Para facilitar ainda mais modificar as coisas no futuro. Removi de dentro do constructor os procedimentos que criam os objetos. Desta maneira surgiu os procedimentos CreateLinePrice e CreateButtonClose. O conteúdo presente nestes procedimentos, antes estavam no constructor. Assim não irei dar maiores explicações sobre os mesmos. Porém, o procedimento CreateBoxMove é novo aqui. Merecendo alguma explicação básica. Neste procedimento, apenas estamos criando um objeto do tipo OBJ_LABEL, e colocando um texto nele. Na verdade um carácter. Este será justamente o que irá nos mostrar que podemos ou não selecionar e por consequência mover a linha de preço. Como o código presente aqui é muito simples, não acredito ser necessário explicar o mesmo detalhadamente.
Agora observe no constructor o seguinte fato. Para ser mais exato, olhe a linha 87. Nela estamos checando se o controle criado é uma linha de fechamento de posição. Caso não seja, indica que podemos criar o objeto de movimentação da linha de preço. Muito bem, agora podemos seguir para o procedimento DispatchMessage. Neste tivemos algumas mudanças um pouco maiores. Porém tais mudanças visa, justamente evitar que precisemos duplicar código. Já que algumas coisas podem ser tratadas em cascata.
Você pode notar isto, olhando o evento da linha 109. Veja que neste evento, iremos na linha 111, atribuir o valor falso para o clique de mouse. Mas este evento da linha 109, não irá terminar como muitos poderiam esperar. Ele de fato irá penetrar ou invadir o próximo evento, que é o CHARTEVENT_CHART_CHANGE, presente na linha 112. Mas por que fazer ou permitir isto? O motivo é simples. Quando a espessura da linha mudar, precisamos repintar ela no gráfico. Sem esta invasão do evento customizado no evento de repintura. Iriamos precisar adicionar uma chamada somente para isto no evento customizado. Algo que podemos desconsiderar já que estamos simplesmente colocando as coisas em cascata.
Agora note que não estamos no evento CHARTEVENT_OBJECT_CLICK testando se o clique foi dado na linha de preço. Agora estamos verificando se ele foi dado no objeto de movimentação. Então não perca o seu tempo tentando selecionar a linha de preço. Clique direto no pequeno diamante que aparece na linha do preço. E ela será selecionada junto.
Uma outra novidade, aqui é justamente no evento CHARTEVENT_MOUSE_MOVE. Note que agora, na linha 140, temos um novo teste sendo feito. Este tem como finalidade verificar se existe alguma outra linha selecionada no gráfico. Caso isto seja verdadeiro, um evento para remover a seleção será disparado. Este tipo de implementação é algo bastante curioso. Visto que se ocorrer qualquer linha estiver selecionada e você clicar em qualquer parte do gráfico. A linha selecionada será desmarcada. Isto sem que precisemos nos preocupar, sobre quem de fato esteja selecionada. Todas as linhas serão desmarcadas.
Muito bem, mas apesar do que acabei de mostrar funcionar para selecionar, ou marcar, uma linha de preço. Isto não serve para nada mais. Não podendo de fato fazer algo realmente útil. Além de marcar uma linha de preço. Mas agora vamos pensar um pouco. Lhe garanto que será algo bastante interessante de ser feito.
Olhando este código acima, e lendo a explicação de como ele funciona. Você, meu caro leitor, conseguiria imaginar uma maneira simples, rápida e direta de mover estas linhas de preço? Não importa se o movimento será ou não aceito pelo servidor de negociação. Apenas quero que você me diga, se é ou não possível mover as linhas com o que acabamos de implementar. Lembrando que precisamos adicionar código ao sistema para de fato isto ser possível. Mas a pergunta se refere a questão de fazer isto, adicionando a menor quantidade possível de código. Não quero precisar digitar muito código aqui. Então é ou não possível fazer isto?
Se você respondeu que NÃO, pode ser que você não esteja de fato conseguindo visualizar como as coisas estão ocorrendo. Pois sem pensar com calma você de fato irá imaginar que não é possível fazer isto. Mas podemos fazer tal coisa. Desde é claro adicionemos um pouco mais de código ao nosso sistema geral. Quero que você se lembre, que nos artigos anteriores, já adicionamos duas novas mensagens ao sistema. Entretanto, tais mensagens não foram implementadas devido ao fato de que não era prudente fazer isto naquele momento. Porém agora temos uma forma muito simples e bastante rápida de selecionar as linhas de preço. Então podemos fazer uso aquele sistema de mensagens. Implementando finalmente o mesmo. Mas antes de fazermos isto, quero explicar o que será feito. Para que você entenda ou procure imaginar, antes de ver a solução. Como faremos para implementar a mesma.
Se você trocar o arquivo de cabeçalho C_ElementsTrade.mqh, pelo código visto no começo deste tópico. Notará que quando você selecionar uma linha de preço, ao clicar em algum ponto ela será desmarcada. A ideia então é justamente fazer isto. Quando pedimos para o indicador de posição, que ele desmarque uma linha de preço, ele irá deverá enviar uma mensagem para o Expert Advisor. Informando que aquela linha que estava marcada, deverá ser deslocada para uma nova posição. Esta nova posição será justamente onde ocorrer o clique do mouse. Captaram agora a ideia.
Porém ela não está completamente perfeita. Mas não vamos nos preocupar com isto neste momento. Apenas vamos fazer o que acabei de dizer. Quando uma linha de preço estiver selecionada. Ao clicar em um outro ponto. Esta linha deverá ser deslocada para este novo ponto. Isto pode ser conseguido, se você modificar código do procedimento DispatchMessage para o que é visto logo abaixo.
138. case CHARTEVENT_MOUSE_MOVE: 139. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 140. if (m_Info.bClick && (m_Info.weight > 1)) 141. { 142. switch (m_Info.ev) 143. { 144. case evMsgCloseTakeProfit: 145. EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 146. break; 147. case evMsgCloseStopLoss: 148. EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 149. break; 150. } 151. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 152. } 153. break;
Fragmento de C_ElementsTrade.mqh
Observe o que aconteceu aqui. Quando você adicionar este fragmento ao código da classe C_ElementsTrade. Você será capaz de deslocar as linhas de take profit e stop loss e isto usando o indicador de mouse. Porém, perceba o seguinte fato. Não estamos movendo as linhas ou objetos grudados nela. Estamos fazendo exatamente o que havia sido planejado. Ou seja, marque uma linha, usando para isto o objeto de movimentação. Leve o indicador de mouse até uma nova posição e clique novamente. Quando isto ocorrer, o teste da linha 140 deverá ter sucesso em algum ponto.
Quando isto acontecer, teremos um chaveamento na linha 142, a fim de disparar o evento mais adequado. No caso poderá ser o evento na linha 145, caso a linha de take profit estivesse selecionada. Mas também poderá ser o evento da linha 148, caso a linha de stop loss estivesse selecionada. Note que a mensagem a ser repassada ao MetaTrader 5, é praticamente igual. Porém ela difere justamente no segundo parâmetro da mesma.
Esta mensagem será repassada pelo MetaTrader 5, ao gráfico onde o indicador esteja presente. E será capturada pelo Expert Advisor. Dentro do código da classe C_Orders, presente no Expert Advisor, esta mensagem será interpretada como um pedido de mudança da linha de preço. Seja ela a de stop loss ou take profit. Assim que o servidor retornar com o resultado do nosso pedido. O Expert Advisor irá disparar novamente uma mensagem, que será capturada pelo indicador de posição. Forçando o mesmo a apresentar o novo ponto onde a linha de preço se encontra. Desta maneira, sempre que olharmos para o gráfico, saberemos exatamente onde estará as linhas no servidor. Já que estamos sempre refletindo exatamente o que se encontra no servidor. E é assim que podemos mover facilmente as coisas precisando de o mínimo possível de código.
Este sistema que acabamos de implementar, funciona tão maravilhosamente bem. Que você pode até mesmo usar o trailing stop, presente no MetaTrader 5, para que a linha de stop loss se mova. Que este indicador de posição, conseguirá acompanhar e será fiel ao que estará presente no servidor de negociação.
Só que apesar deste sistema funcionar maravilhosamente bem, ainda não resolvemos um outro problema. Este se trata do seguinte fato: Como podemos criar as linhas de take profit ou stop loss, quando elas foram removidas do servidor. E por consequência do gráfico? Bem, isto parece ser algo extremamente mais complicado de ser feito. Mas acompanhe a explicação e você verá que isto é tal simples, quando o que acabamos de fazer. Mas para separar as coisas de maneira que você não as confunda, vamos para um próximo tópico.
Sem linhas de stop loss e take profit. Como as colocar?
Agora temos um problema um pouco diferente do visto no tópico anterior. Este problema é o seguinte: Suponhamos que você remova os valores de stop loss e take profit do servidor. Mas depois de um tempo, você decida que os quer recolocar novamente na posição. Bem, existem diversas maneiras diferentes de se fazer isto. Porém, aqui quero mostrar como você pode modificar o indicador de posição. Isto a fim de que ele nos permita recolocar o stop loss ou take profit diretamente via gráfico.
Para fazer isto, precisaremos modificar um pouco mais o indicador. Isto por que, quando removemos o stop loss ou take profit da posição. O indicador remove também os elementos, ou objetos referentes a linha de stop loss ou take profit. E é justamente este comportamento que teremos de mudar. Isto para que possamos usar o mesmo mecanismo que foi visto no tópico anterior. Ou seja, clique em um dos pontos, seja ele stop loss ou take profit. Leve o indicador de mouse até o ponto desejado e clique novamente. Mudando assim o valor limite para encerramento da posição. A ideia básica é esta.
Mas como poderemos fazer isto facilmente? Novamente você, meu caro leitor, talvez esteja pensando em alguma solução bem maluca. Não sei se ela seria mais simples ou mais complicada do que, a solução que será apresentada. Mas da mesma forma que foi feito no tópico anterior. A ideia aqui é modificar o mínimo possível o código.
Muito bem, então vamos parar novamente e pensar um pouco. Qual seria a solução mais simples para no nosso problema? Bem, considerando que queremos usar o mesmo modelo visto no tópico anterior. A solução mais simples seria ter de alguma forma, o objeto de movimento da linha de preço, presente no gráfico de alguma forma. Se você, meu caro leitor, pensou isto. Pensou a mesma coisa que eu pensei. Mas onde podemos colocar o objeto, quando não existe a linha de preço. Seja ela a de take profit ou stop loss? Bem, neste caso, eu penso que a melhor solução seria colocar o objeto de movimento, justamente na linha onde a posição foi aberta. Não sei se você, meu caro leitor, pensa da mesma maneira. Mas você é livre para pensar como desejar. Desde é claro, consiga entender o que esteja fazendo.
Então vamos fazer desta maneira. Quando a linha, seja de stop loss ou take profit, for removida da posição. O indicador de movimento, será colocado junto a linha do preço de abertura. Mas agora temos um problema. Quando esta mesma linha, seja de stop loss ou take profit é removida. O indicador de posição, também remove os objetos da linha. E não queremos complicar ainda mais, e de forma inútil o nosso código. Assim vamos ver onde o indicador remove tais objetos. Isto pode ser visto 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, ePriorityNull, StringFormat("%I64u : Position opening price.", m_Infos.ticket)); 63. if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, clrForestGreen, ePriorityDefault, StringFormat("%I64u : Take Profit price.", m_Infos.ticket)); 64. if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, clrFireBrick, ePriorityDefault, StringFormat("%I64u : Stop Loss price.", m_Infos.ticket)); 65. (*Open).UpdatePrice(PositionGetDouble(POSITION_PRICE_OPEN)); 66. if ((value = PositionGetDouble(POSITION_TP)) > 0) (*Take).UpdatePrice(value); else 67. { 68. delete Take; 69. Take = NULL; 70. } 71. if ((value = PositionGetDouble(POSITION_SL)) > 0) (*Stop).UpdatePrice(value); else 72. { 73. delete Stop; 74. Stop = NULL; 75. } 76. break; 77. } 78. ChartRedraw(); 79. } 80. //+------------------------------------------------------------------+
Fragmento de C_IndicatorPosition.mqh
Este fragmento logo acima faz parte da classe C_IndicatorPosition. E ele é o código original presente naquela classe. Ok, agora note os seguintes pontos. Na linha 66 e 71 temos um teste sendo feito. Uma para o take profit e outro para o stop loss. Agora preste atenção, para entender o que precisaremos fazer. Estes testes que estão sendo feitos. Diz ao indicador de posição, que deveremos remover os ponteiros para a classe C_ElementsTrade. Isto quando o valor presente na posição for igual ou menor que zero. Acredito que você, meu caro leitor e entusiasta consiga entender isto claramente.
Porém, aqui mora um ponto onde precisaremos mexer no código. Pense um pouco. Se em vez de remover o ponteiro para a classe C_ElementsTrade. Repassarmos o valor da linha take profit e stop loss para a classe C_ElementsTrade. Poderemos lá na classe C_ElementsTrade criar algum tipo de mecanismo que nos permita fazer o que desejamos. Então este mesmo fragmento mostrado acima, deverá ser modificado a fim de que possamos fazer isto. Tal modificação a ser feita é vista 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, ePriorityNull, StringFormat("%I64u : Position opening price.", m_Infos.ticket)); 63. if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, clrForestGreen, ePriorityDefault, StringFormat("%I64u : Take Profit price.", m_Infos.ticket)); 64. if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, clrFireBrick, ePriorityDefault, 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
Observe que o código não sofreu praticamente nenhuma grande mudança. Porém o que fizemos foi simplesmente remover aqueles testes que estavam sendo feitos, e agora independentemente do valor presente no stop loss ou take profit. Ele será repassado para a classe C_ElementsTrade. Isto para que possamos lidar de maneira adequado com os objetos presentes na linha de preço.
Perfeito, agora vamos ver como está o código lá na classe C_ElementsTrade. O código original é visto no fragmento logo abaixo.
095. //+------------------------------------------------------------------+ 096. inline void UpdatePrice(const double price) 097. { 098. m_Info.price = price; 099. UpdateViewPort(); 100. } 101. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade.mqh
Pois bem, agora vamos focar neste fragmento mostrado acima. Pois é aqui que a mágica acontecerá. Porém note que originalmente estamos esperando receber apenas um único parâmetro. No entanto, na modificação feita na classe C_IndicatorPosition, você nota, que estamos repassando dois parâmetros para este procedimento mostrado acima. Esta então é a primeira mudança que será feita. Mas vamos acumular um pouco mais de coisas antes de ver como o código deverá ficar. Bem, o primeiro argumento recebido, será onde se encontra a linha de preço de abertura da posição. Já o segundo é o preço da linha a ser criada.
Agora pense um pouco. Tudo que precisamos fazer é remover os objetos desnecessários, que neste momento são o objeto que representa a linha de preço e o botão de fechar. E manter o botão de movimentação. Porém este objeto que representa a movimentação será realocado para uma nova posição. Que é justamente o preço de abertura da posição. Assim, a maneira mais simples de fazer isto é modificar o fragmento acima de forma que ele ficaria como mostrado logo abaixo.
095. //+------------------------------------------------------------------+ 096. inline void UpdatePrice(const double open, const double price) 097. { 098. m_Info.price = (price > 0 ? price : open); 099. if (price == 0) 100. { 101. ObjectDelete(0, def_NameBtnClose); 102. ObjectDelete(0, def_NameHLine); 103. } 104. UpdateViewPort(); 105. } 106. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade.mqh
De fato este fragmento permitiria que o nosso indicador de posição viesse a funcionar como seria esperado. Porém existe um pequeno inconveniente aqui. Observe que quando o operador remove a linha, seja de stop loss ou take profit da posição aberta. O indicador de posição irá de fato, remover a linha horizontal e o botão de fechar. Isto por conta das linhas 101 e 102 vistas no fragmento acima. Porém, quando o operador pedir para repor a linha de stop loss ou take profit novamente na posição aberta. Apenas o botão de movimentação será devidamente movido. Porém, os demais objetos, foram removidos da lista de objetos. E não irão de maneira alguma aparecer no gráfico. O resultado pode ser visto na imagem logo abaixo:
Isto não é de fato um grande problema. É apenas um inconveniente, que precisamos corrigir. Mas corrigir isto é tão simples quanto o que foi feito até agora. Porém precisaremos fazer uma mudança um pouco maior no código da classe. Desta maneira o código final, já corrigido pode ser visto logo abaixo na íntegra.
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. //+------------------------------------------------------------------+ 008. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 009. //+------------------------------------------------------------------+ 010. #define def_PathBtns "Images\\Market Replay\\Orders\\" 011. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 012. #resource "\\" + def_Btn_Close; 013. //+------------------------------------------------------------------+ 014. #include "..\Auxiliar\C_Mouse.mqh" 015. //+------------------------------------------------------------------+ 016. class C_ElementsTrade : private C_Mouse 017. { 018. private : 019. //+------------------------------------------------------------------+ 020. struct st00 021. { 022. ulong ticket; 023. string szPrefixName, 024. szDescr; 025. EnumEvents ev; 026. double price; 027. bool bClick; 028. char weight; 029. color _color; 030. }m_Info; 031. //+------------------------------------------------------------------+ 032. void UpdateViewPort(void) 033. { 034. int x, y; 035. 036. ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y); 037. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 038. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 039. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 040. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 041. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 042. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30); 043. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 044. } 045. //+------------------------------------------------------------------+ 046. inline void CreateLinePrice(void) 047. { 048. string szObj; 049. 050. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 051. macro_LineInFocus(false); 052. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 053. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 054. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 055. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 056. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 057. } 058. //+------------------------------------------------------------------+ 059. inline void CreateBoxMove(void) 060. { 061. string szObj; 062. 063. CreateObjectGraphics(szObj = def_NameBtnMove, OBJ_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 064. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 065. ObjectSetString(0, szObj, OBJPROP_TEXT, "u"); 066. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, 17); 067. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 068. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, 21); 069. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, 23); 070. } 071. //+------------------------------------------------------------------+ 072. inline void CreateButtonClose(void) 073. { 074. string szObj; 075. 076. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 077. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 078. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 079. } 080. //+------------------------------------------------------------------+ 081. public : 082. //+------------------------------------------------------------------+ 083. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, EnumPriority ePrio, string szDescr = "\n") 084. :C_Mouse(0, "") 085. { 086. ZeroMemory(m_Info); 087. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 088. m_Info._color = _color; 089. m_Info.szDescr = szDescr; 090. } 091. //+------------------------------------------------------------------+ 092. ~C_ElementsTrade() 093. { 094. ObjectsDeleteAll(0, m_Info.szPrefixName); 095. } 096. //+------------------------------------------------------------------+ 097. inline void UpdatePrice(const double open, const double price) 098. { 099. m_Info.price = (price > 0 ? price : open); 100. if (price > 0) 101. { 102. CreateLinePrice(); 103. CreateButtonClose(); 104. }else 105. ObjectsDeleteAll(0, m_Info.szPrefixName); 106. if (m_Info.ev != evMsgClosePositionEA) CreateBoxMove(); 107. UpdateViewPort(); 108. } 109. //+------------------------------------------------------------------+ 110. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 111. { 112. string sz0; 113. 114. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 115. switch (id) 116. { 117. case CHARTEVENT_CUSTOM + evMsgSetFocus: 118. macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev)); 119. m_Info.bClick = false; 120. case CHARTEVENT_CHART_CHANGE: 121. UpdateViewPort(); 122. break; 123. case CHARTEVENT_OBJECT_CLICK: 124. sz0 = GetPositionsMouse().szObjNameClick; 125. if (m_Info.bClick) switch (m_Info.ev) 126. { 127. case evMsgClosePositionEA: 128. if (sz0 == def_NameBtnClose) 129. EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, ""); 130. break; 131. case evMsgCloseTakeProfit: 132. if (sz0 == def_NameBtnClose) 133. EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL)); 134. else if (sz0 == def_NameBtnMove) 135. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, ""); 136. break; 137. case evMsgCloseStopLoss: 138. if (sz0 == def_NameBtnClose) 139. EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL)); 140. else if (sz0 == def_NameBtnMove) 141. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, ""); 142. break; 143. } 144. m_Info.bClick = false; 145. break; 146. case CHARTEVENT_MOUSE_MOVE: 147. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 148. if (m_Info.bClick && (m_Info.weight > 1)) 149. { 150. switch (m_Info.ev) 151. { 152. case evMsgCloseTakeProfit: 153. EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 154. break; 155. case evMsgCloseStopLoss: 156. EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 157. break; 158. } 159. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 160. } 161. break; 162. } 163. } 164. //+------------------------------------------------------------------+ 165. }; 166. //+------------------------------------------------------------------+ 167. #undef macro_LineInFocus 168. //+------------------------------------------------------------------+ 169. #undef def_Btn_Close 170. #undef def_PathBtns 171. //+------------------------------------------------------------------+ 172. #undef def_NameBtnMove 173. #undef def_NameBtnClose 174. #undef def_NameHLine 175. //+------------------------------------------------------------------+
C_ElementsTrade.mqh
O resultado de quando executarmos este código é visto na animação logo abaixo.
Note que agora temos o sistema funcionando perfeitamente bem. Assim como era esperado que ele viesse a funcionar. Já que o código passou por algumas mudanças, e muitas delas muito simples não necessitando de algum destaque especial. Quero que você, meu caro leitor, preste atenção a dois pontos em especial neste código. O primeiro é o constructor. Observe que ele está um pouco diferente do que era originalmente visto até pouco tempo. O segundo ponto é justamente o procedimento UpdatePrice. Veja, que os procedimentos onde era criados os objetos que era feito no constructor. Agora são feitos aqui no procedimento UpdatePrice. Com o código em si é bastante simples não vejo necessidade de entrar em detalhes nele. Mesmo por que ele, tudo que foi de fato feito, foi remover o que era feito no constructor e passar para este local daqui. Nada assim tão espetacular.
Porém quero que você, meu caro leitor, volte a sua atenção a uma outra coisa. Para ser mais específico. Quero que você preste atenção a animação vista logo acima. E me responda sinceramente. Caso você, como operador, viesse a tentar usar este sistema, que acabamos de implementar. Você ficaria na dúvida se estaria ajustando o take profit ou stop loss? E mais, você saberia se precisaria levar a linha do indicador de mouse para um preço maior ou menor a fim de recolocar a linha de preço no servidor? Talvez esta última questão não tenha ficado clara o suficiente. Vamos tentar deixá-la um pouco melhor de ser compreendida. Para que você, meu caro leitor e entusiasta, consiga compreender o problema que ainda temos de resolver.
Olhando apenas a animação você pode nitidamente notar que estou em uma posição vendida. Isto por conta que a linha de take profit está em um valor abaixo do preço de abertura da posição. Ok, mas vamos supor que você como operador tivesse removido ambas as linhas do gráfico. Ou melhor dizendo, você as tivesse removido da posição presente no servidor. Então ao olhar esta mesma animação, você não saberia apenas olhando o indicador de posição, se estamos vendidos ou comprados. Este é o primeiro ponto.
O segundo ponto é: No caso de estarmos vendidos, e ambos valores de take profit e stop loss não estivessem na posição. Ou seja, os objetos que permitem mover a linha de preço, estariam ambos, no preço de abertura. Como você poderia saber, apenas olhando o gráfico, se deverá colocar a linha de stop loss, acima ou abaixo do preço atual? Difícil responder esta questão não é mesmo? Porém temos uma solução bastante simples para este problema. No entanto, como você pode observar, nesta classe C_ElementsTrade. Em momento algum estamos acessando dados da posição. Os dados estão vindo da classe C_IndicatorPosition. E como não quero contaminar a classe C_ElementsTrade com dados que futuramente teriam que ser removidos dela. Isto por conta que a usaremos também quando formos trabalhar com ordens pendentes. Será preciso fazer um pouco mais de esforço. Mas isto será visto no próximo artigo.
Considerações finais
Neste artigo, mostrei como você, meu caro e estimado leitor, pode sem muito esforço. Conseguir modificar o indicador de posição a fim de que ele venha a ser capaz de fazer bem mais coisas do que originalmente era capaz de fazer. Vimos como incluir a capacidade de podermos mover tanto os preços quanto também criar as linhas de preço. E isto diretamente no gráfico. Algo que muitos imaginariam ser extremamente complicado e de difícil solução. Porém você deve ter notado que fizemos tudo isto, com muita facilidade e com um mínimo de esforço. Tudo que foi preciso fazer é parar e pensar um pouco.
E é justamente este o propósito desta série de artigos. Não estou aqui para de fato mostrar como você pode criar uma aplicação. Porém é muito mais motivador e interessante de aprender a programação, quando de fato vemos algo sendo criado. Espero que você, meu caro leitor, que esteja iniciando neste fantástico mundo da programação, consiga de fato entender o objetivo destes artigos. E que eles lhe sirvam como uma boa base teórica e motivacional para procurar aprender cada vez mais. Pois não existem problemas impossíveis. Existem sim pessoas que não conseguem ver uma solução simples, para grandes problemas.
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