Simulação de mercado: Position View (XVI)
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 (XV), finalmente chegamos em um ponto em que foi possível explicar de forma bastante clara. Como, mas principalmente o porquê, do sistema de replay / simulador está sendo implementado da maneira como você o está vendo ser implementado. Acredito que muitos de você, não imaginavam ser possível fazer tais coisas usando o MQL5. Já que não encontrei referências anteriores fazendo uso exatamente do que foi explicado no artigo anterior.
Sei que à primeira vista, este tipo de coisa, assusta um pouco. E muitas das vezes, mais confunde e atordoa entusiastas. Porém meu caro leitor, não é meu intuito fazer com que você desista ou ache que o que estou explicando é algo inalcançável. Muito pelo contrário. Quero sim, motivar você a de fato, estudar e procurar aprender novas técnicas de programação. Que consiga começar a pensar nas coisas de uma forma um pouco mais ampla, e que saia da mesmice de sempre.
Então neste artigo, vamos fazer algo um pouco, mais simples, porém necessário. Isto para que você, meu caro e estimado leitor, consiga deglutir adequadamente o conteúdo visto no artigo anterior. Aqui vamos deixar o indicador de posição, um pouco mais agradável.
Dando mais alguns passos
Muito bem, como você deve ter notado. Nosso indicador de posição, até o momento tem os controles meio que soltos. Porém, apesar de isto não atrapalhar em nada o seu uso. Podemos deixar as coisas um pouco mais agradáveis ao usuário final. Para fazer isto, precisamos criar alguns elementos. Adicionar algumas outras informações e promover alguns ajustes. Coisa pouca, mas que deixará o uso um pouco mais agradável de forma geral.
Adicionar um objeto de fundo, ao indicador é algo muito simples, direto e fácil de ser feito. Veja abaixo as modificações que precisam ser feitas e onde elas ocorreram. Note que se trata de um fragmento de código. Então você precisará do código completo, que pode ser conseguido nos artigos anteriores.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_NameHLine m_Info.szPrefixName + "#HLINE" 005. #define def_NameBtnClose m_Info.szPrefixName + "#CLOSE" 006. #define def_NameBtnMove m_Info.szPrefixName + "#MOVE" 007. #define def_NameInfoDirect m_Info.szPrefixName + "#DIRECT" 008. #define def_NameObjLabel m_Info.szPrefixName + "#PROFIT" 009. #define def_NameBackGround m_Info.szPrefixName + "#BACKGROUND" 010. //+------------------------------------------------------------------+ . . . 049. //+------------------------------------------------------------------+ 050. void UpdateViewPort(const double price) 051. { 052. int x, y; 053. 054. ChartTimePriceToXY(0, 0, 0, price, x, y); 055. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290)); 056. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 057. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 058. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 059. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 060. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XDISTANCE, x + 10); 061. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2)); 062. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 063. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 064. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 065. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 066. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XDISTANCE, x - 10); 067. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); 068. } 069. //+------------------------------------------------------------------+ 070. inline void CreateLinePrice(void) 071. { 072. string szObj; 073. 074. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 075. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 076. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 077. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 078. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 079. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 080. macro_LineInFocus(false); 081. CreateObjectGraphics(szObj = def_NameBackGround, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 082. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 083. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 084. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 085. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, m_Info.Text.Width + (m_Info.ev == evMsgClosePositionEA ? 40 : 32)); 086. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, m_Info.Text.Height + 5); 087. } 088. //+------------------------------------------------------------------+ 089. inline void CreateBoxInfo(const bool bMove) 090. { 091. string szObj; 092. const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0}; 093. 094. CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 095. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 096. ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c)); 097. ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? (m_Info.ev == evMsgCloseTakeProfit ? clrDarkGreen : clrMaroon) : (m_Info.bIsBuy ? clrDarkGreen : clrMaroon))); 098. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15)); 099. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 100. } 101. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Observe que na linha nove adicionamos uma nova definição. Este será o nome do objeto que será criado. Como entre o código das definições e o código do procedimento UpdateViewPort, nada mudou. Não faz sentido recolocar todo o código aqui novamente. Assim para simplificar, estou mostrando apenas os trechos, ou procedimentos, que realmente sofreram mudanças.
Note que nas linhas 66 e 67 é onde dizemos a localização do objeto que estará no fundo. Aqui mora uma questão interessante. Veja que o cálculo na linha 67 é o mesmo da linha 86. Onde dizemos a altura do objeto a ser criado. Porém na linha 67, dividimos o valor por dois, para posicionar da maneira correta o objeto na tela.
Já o valor dez, usado para subtrair X na linha 66. Vem do fato de que a imagem usada no botão de fechar tem 16 pixel por 16 pixel. E como esta mesma imagem está centralizada, precisamos deslocar oito posições para a esquerda. Porém se fizéssemos apenas isto, o objeto de fundo começaria exatamente no limite esquerdo da imagem do botão de fechar. Então para deixar um certo espaço, adicionamos dois a este valor, totalizando os tais dez que é usado no posicionamento em relação ao eixo X na linha 66. Algo muito simples e direto.
Agora vamos dar um pulinho na linha 97. Observe que nesta linha mudei, a composição das cores. Isto por que, caso ela fosse mantida, não teríamos a devida noção de onde estaria o objeto, que seleção para movimentação. Se bem que depois tornaremos isto um pouco melhor. Mas por hora, precisamos saber exatamente onde ele se encontra. Assim a cor do mesmo foi intensificada, de maneira que ele seja mais visível quando o objeto de fundo estiver no indicador. Agora vamos ver como o objeto de fundo foi criado. Para isto vamos ao trecho de código entre as linhas 81 e 86. É neste trecho que o objeto de fundo é criado. Como é um tipo de tarefa relativamente corriqueira de ser feita. Apenas estou mostrando onde o objeto é criado. E quais propriedades estamos definindo nele. Acredito que não será problema, para você meu caro leitor, entender este trecho do código.
Bem, com isto temos as coisas relativamente concluídas. E digo relativamente, pois no momento em que você abrir uma posição. O indicador de posição será criado e posicionado corretamente. Apresentando seus elementos da maneira adequada. Porém temos dois problemas. Se bem, que se você estiver usando um computador, moderno, com um sistema operacional bem organizado e sem muitas aplicações rodando no MetaTrader 5. Não temos nenhum problema. Apenas um pequeno incomodo, que acontece quando você clica no botão para remover o take profit, ou o stop loss. Mas depois decide que é hora de criar novamente o take profit ou stop loss. Este problema é o fato de que, parte do objeto que representa o objeto de movimentação. Ser ocultado pelo objeto de fundo. Para resolver isto é algo muito simples de ser feito. Basta irmos no procedimento UpdatePrice e o modificar como mostrado abaixo.
149. //+------------------------------------------------------------------+ 150. inline void UpdatePrice(const double open, const double price) 151. { 152. ObjectsDeleteAll(0, m_Info.szPrefixName); 153. if (price > 0) 154. { 155. CreateLinePrice(); 156. CreateButtonClose(); 157. } 158. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 159. if (price > 0) 160. CreateObjectInfoText(); 161. m_Info.open = open; 162. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 163. if (m_Info.ev != evMsgClosePositionEA) 164. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 165. } 166. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Note que foi efetuada apenas uma pequena mudança no código. E isto resolve completamente o nosso problema. Agora você deve estar preocupado, imaginando que durante a movimentação do mouse, a fim de mudar o valor de take profit ou stop loss. Teremos um número excessivo de chamadas, ou um inconveniente do tipo: Remover os objetos na linha 152 e logo em seguida os criar novamente. Bem se você por ventura chegou a pensar isto, apenas olhando este código. Sugiro que leia os artigos anteriores. Isto por que você ainda não entendeu como as coisas funcionam aqui.
Ok, nosso indicador de posição está bem legal, e visualmente agradável. Porém ainda temos alguns problemas. Como por exemplo: Quando você movimenta as linhas de take profit ou stop loss, elas estão colidindo. Fazendo com que os objetos venham a ficar uns sobre os outros em alguns momentos. Futuramente criaremos uma solução mais adequada para isto. Porém, neste momento, podemos fazer algo mais simples. Já que em contas do tipo NETTING teremos apenas a presença de um único indicador de posição no gráfico. Apesar de contas do tipo HEDGING permitirem mais de um indicador, neste momento não iremos nos preocupar com a presença de outros indicadores. Mas isto será apenas por hora.
Assim a solução mais simples de todas é fazer com que os objetos se desloquem de forma adequada no eixo X. Isto para que eles não colidam uns com os outros. Neste momento atual, tudo que precisamos fazer é mudar os valores em uma posição do código. Você pode ver onde fazer isto, no fragmento abaixo.
049. //+------------------------------------------------------------------+ 050. void UpdateViewPort(const double price) 051. { 052. int x, y; 053. 054. ChartTimePriceToXY(0, 0, 0, price, x, y); 055. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 260 : 370)); 056. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 057. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0));
Fragmento de C_ElementsTrade
Compare este fragmento com os anteriores e você notará onde foi feita a modificação. Pelo motivo de ser algo bastante simples de entender, não vou entra em muitos detalhes sobre as mudanças. Podemos então passar para a próxima coisa a ser feita.
Agora vamos pensar no seguinte: Muitos operadores gostam de visualizar o financeiro e não um valor em termos de pontos. Como podemos permitir que isto aconteça, modificando o mínimo possível o nosso código? Lembrando o seguinte: Quero que o indicador permita ao operador visualizar tanto o resultado em termos de pontos, quanto o resultado em termos de financeiro. Bem, em uma aplicação pessoal, muito provavelmente eu simplesmente colocaria um dos valores como descrição. Ou seja, assim que o ponteiro do mouse, estivesse sobre algum elemento do indicador. Este apresentaria o valor que estaria na propriedade do objeto. A propriedade que deveria ser modificada é a OBJPROP_TOOLTIP. Se você colocar algum texto nesta propriedade, ele será apresentado quando o mouse estiver sobre o objeto.
Mas muitos operadores, podem achar isto um tanto quanto complicado, ou desagradável. Isto sem falar que muitos achariam os dados bastante confusos de serem interpretados. Então devemos apresentar eles, da mesma maneira que estamos apresentando os valores em pontos. Mas para saber o financeiro envolvido, precisamos de duas outras informações. A do volume atualmente em aberto, e o valor financeiro de um ponto. Com estas duas informações poderemos apresentar o valor financeiro, da mesma forma como estamos apresentando o valor em pontos. Simples e direto.
Demonstrando o Financeiro
Ok. Neste ponto, existem algumas coisas que podemos fazer, e outras que devemos fazer. Vamos começar tratando o seguinte cenário. Ignoraremos completamente qualquer outra coisa. E vamos fazer com que os valores agora sejam em termos financeiros. Para isto a primeira coisa que precisamos fazer. É ir ao código principal do indicador de posição, e modificar o que é visto no 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.128" 008. #property link "https://www.mql5.com/pt/articles/13391" 009. #property indicator_chart_window 010. #property indicator_plots 0 011. //+------------------------------------------------------------------+ 012. #define def_ShortName "Position View" 013. //+------------------------------------------------------------------+ 014. #include <Market Replay\Order System\C_ElementsTrade.mqh> 015. #include <Market Replay\Defines.mqh> 016. //+------------------------------------------------------------------+ 017. input ulong user00 = 0; //For Expert Advisor use 018. //+------------------------------------------------------------------+ 019. struct st00 020. { 021. ulong ticket; 022. string szShortName, 023. szSymbol; 024. double priceOpen, 025. var; 026. char digits; 027. bool bIsBuy; 028. }m_Infos; 029. //+------------------------------------------------------------------+ 030. C_ElementsTrade *Open = NULL, *Stop = NULL, *Take = NULL; 031. //+------------------------------------------------------------------+ 032. bool CheckCatch(ulong ticket) 033. { 034. double vv, vs; 035. 036. ZeroMemory(m_Infos); 037. m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket); 038. if (!PositionSelectByTicket(m_Infos.ticket)) return false; 039. if (ObjectFind(0, m_Infos.szShortName) >= 0) 040. { 041. m_Infos.ticket = 0; 042. return false; 043. } 044. m_Infos.szSymbol = PositionGetString(POSITION_SYMBOL); 045. m_Infos.digits = (char)SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); 046. vs = SymbolInfoDouble(PositionGetString(POSITION_SYMBOL), SYMBOL_TRADE_TICK_SIZE); 047. vv = SymbolInfoDouble(PositionGetString(POSITION_SYMBOL), SYMBOL_TRADE_TICK_VALUE); 048. m_Infos.var = vs / vv; 049. IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName); 050. EventChartCustom(0, evUpdate_Position, ticket, 0, ""); 051. 052. return true; 053. } 054. //+------------------------------------------------------------------+ 055. inline void ProfitNow(void) 056. { 057. double ask, bid; 058. 059. ask = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_ASK); 060. bid = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_BID); 061. if (Open != NULL) 062. (*Open).ViewValue((m_Infos.bIsBuy ? bid - m_Infos.priceOpen : m_Infos.priceOpen - ask)); 063. } 064. //+------------------------------------------------------------------+ 065. int OnInit() 066. { 067. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 068. if (!CheckCatch(user00)) 069. { 070. ChartIndicatorDelete(0, 0, def_ShortName); 071. return INIT_FAILED; 072. } 073. 074. return INIT_SUCCEEDED; 075. } 076. //+------------------------------------------------------------------+ 077. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 078. { 079. ProfitNow(); 080. 081. return rates_total; 082. } 083. //+------------------------------------------------------------------+ 084. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 085. { 086. double volume; 087. 088. if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam); 089. if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam); 090. if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam); 091. switch (id) 092. { 093. case CHARTEVENT_CUSTOM + evUpdate_Position: 094. if (lparam != m_Infos.ticket) break; 095. if (!PositionSelectByTicket(m_Infos.ticket)) 096. { 097. ChartIndicatorDelete(0, 0, m_Infos.szShortName); 098. return; 099. }; 100. 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)); 101. 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); 102. 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); 103. volume = PositionGetDouble(POSITION_VOLUME); 104. (*Open).UpdatePrice(0, m_Infos.priceOpen = PositionGetDouble(POSITION_PRICE_OPEN), volume, m_Infos.var); 105. (*Take).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_TP), volume, m_Infos.var); 106. (*Stop).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_SL), volume, m_Infos.var); 107. ProfitNow(); 108. break; 109. } 110. ChartRedraw(); 111. }; 112. //+------------------------------------------------------------------+ 113. void OnDeinit(const int reason) 114. { 115. delete Open; 116. delete Take; 117. delete Stop; 118. } 119. //+------------------------------------------------------------------+
Código Principal
Vou dar uma rápida explicação, sobre o que mudou. Note que na linha 25, agora temos uma nova variável. Esta é inicializada, na linha 48, com os dados obtidos nas linhas 46 e 47. Este valor é calculado aqui, para evitar que tenhamos que o calcular a todo momento. Mas já que ele não mudará durante todo o tempo em que o indicador de posição permanecerá no gráfico. Não será preciso nos preocupar com ele. Agora este mesmo valor é usado junto com outro, que se encontra presente na linha 103. Nesta linha 103, fazemos a captura do volume que está em aberto. Isto por que o volume apenas mudará, caso o Expert Advisor, envie um evento para que os indicadores de posição tenham que ser atualizados. Neste caso é preciso que o volume em aberto, seja em alguns casos, atualizado.
Mas agora quero que você observe as linhas 104 a 106, que é justamente onde estes dois novos valores são usados. Ou seja, agora devemos voltar a nossa atenção ao procedimento UpdatePrice, que terá de ser modificado. Então uma vez que terminamos de fazer as mudanças aqui no código principal, podemos voltar a nossa atenção novamente a classe C_ElementsTrade.
Ok. Sabemos que precisaremos de espaço para armazenar dois novos valores double. Isto por que não iremos nos preocupar com o que o código principal estará nos informando. Pois ele sempre irá nos informar o valor em termos de pontos. Dependerá da classe C_ElementsTrade, modelar e calcular corretamente o valor a ser apresentado. Isto em termos financeiros. Assim precisamos adicionar duas novas variáveis a nossa estrutura dentro da classe. Isto pode ser visto no fragmento abaixo.
26. class C_ElementsTrade : private C_Mouse 27. { 28. private : 29. //+------------------------------------------------------------------+ 30. struct st00 31. { 32. struct st_01 33. { 34. uchar Width, 35. Height, 36. digits; 37. }Text; 38. ulong ticket; 39. string szPrefixName, 40. szDescr; 41. EnumEvents ev; 42. double price, 43. open, 44. volume, 45. tickValue; 46. bool bClick, 47. bIsBuy; 48. char weight; 49. color _color; 50. }m_Info; 51. //+------------------------------------------------------------------+ 52. void UpdateViewPort(const double price)
Fragmento de C_ElementsTrade
Aqui nas linhas 44 e 45, estão as nossas novas variáveis. Já que o constructor garantirá que os valores sejam inicializados em ZERO, não precisamos nos preocupar com isto. Podemos ir direto ao código UpdatePrice, e fazer a inicialização destas mesmas variáveis. Isto pode ser visto no fragmento logo a seguir.
151. //+------------------------------------------------------------------+ 152. inline void UpdatePrice(const double open, const double price, const double vol = 0, const double var = 0) 153. { 154. ObjectsDeleteAll(0, m_Info.szPrefixName); 155. m_Info.volume = (vol > 0 ? vol : m_Info.volume); 156. m_Info.tickValue = (var > 0 ? var : m_Info.tickValue); 157. if (price > 0) 158. { 159. CreateLinePrice(); 160. CreateButtonClose(); 161. CreateObjectInfoText(); 162. } 163. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 164. m_Info.open = open; 165. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 166. if (m_Info.ev != evMsgClosePositionEA) 167. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 168. } 169. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Agora preste atenção. Note que os valores padrão, ou default dos dois últimos argumentos são zero. Por que estou fazendo isto? O motivo, é que este procedimento é chamado em dois locais. O primeiro é no código principal. Nele receberemos os valores que serão diferentes de zero. Porém existe um outro local que é no procedimento DispatchMessage da classe C_ElementsTrade. Neste local o valor iria ser zero. Assim para não correr o risco de colocar um valor errado. Já que poderia ser tentador repassar os valores que acabamos de declarar na classe. Eu simplesmente declaro aqui no procedimento, que os valores default serão ZERO.
Uma vez feito isto, observe nas linhas 155 e 156, onde fazendo a checagem se o valor dos argumentos são zero ou maiores que zero. Caso sejam maiores, inicializaremos ou atualizar. Dependendo do caso. Os valores que declaramos nas linhas 44 e 45.
Com isto não precisamos mais nos preocupar em saber quais são os volumes, ou valor do tick. A classe já sabe este valor. Então a próxima modificação deverá ser na função ViewValue. Esta é que concluirá a tarefa de nos apresentar o valor pretendido. Originalmente esta função pode ser vista abaixo:
169. //+------------------------------------------------------------------+ 170. void ViewValue(const double profit) 171. { 172. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, StringFormat("%." + (string)m_Info.Text.digits + "f", (profit < 0 ? -(profit) : profit))); 173. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 174. ChartRedraw(); 175. } 176. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Note que na linha 172 é onde formatamos o valor para ser apresentado ao operador. Mas aqui o valor está sendo a quantidade de pontos. Ou na verdade, a variação do preço, não em termos de pontos, mas em valor absoluto. Assim, tudo que precisamos fazer é gerar uma fatoração este valor, que conhecemos, originalmente, pelos valores que agora temos em mãos. Desta maneira, o novo código do procedimento ViewValue, pode ser visto logo abaixo.
169. //+------------------------------------------------------------------+ 170. void ViewValue(const double profit) 171. { 172. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, StringFormat("$ %." + (string)m_Info.Text.digits + "f", ((profit < 0 ? -(profit) : profit) / m_Info.var) * m_Info.volume)); 173. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 174. ChartRedraw(); 175. } 176. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Ok. Isto em tese, resolve o nosso problema em querer apresentar valores financeiros. Mas por que estou dizendo, que resolve em tese? O motivo é simples. Agora temos um problema de espaço. Isto por que, os valores a serem apresentados, podem ser maiores do que o espaço do objeto OBJ_EDIT. E quando isto ocorrer a informação apresentada será truncada. Deixando o operador completamente confuso quanto aos dados sendo vistos. Mas para efetuar testes. Sim, já temos a apresentação de valores financeiros no indicador de posição. Bastando apenas compilar o código.
Muito bem. Agora temos um novo problema em mãos, e para resolver ele, precisaremos pensar um pouco. Para facilitar explicar o que precisa ser observado. Vamos ver o código completo da classe C_ElementsTrade. Este pode ser visto abaixo. Já com todas as modificações feitas até o momento.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_NameHLine m_Info.szPrefixName + "#HLINE" 005. #define def_NameBtnClose m_Info.szPrefixName + "#CLOSE" 006. #define def_NameBtnMove m_Info.szPrefixName + "#MOVE" 007. #define def_NameInfoDirect m_Info.szPrefixName + "#DIRECT" 008. #define def_NameObjLabel m_Info.szPrefixName + "#PROFIT" 009. #define def_NameBackGround m_Info.szPrefixName + "#BACKGROUND" 010. //+------------------------------------------------------------------+ 011. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1)); 012. //+------------------------------------------------------------------+ 013. #define def_PathBtns "Images\\Market Replay\\Orders\\" 014. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 015. #resource "\\" + def_Btn_Close; 016. //+------------------------------------------------------------------+ 017. #include "..\Auxiliar\C_Mouse.mqh" 018. //+------------------------------------------------------------------+ 019. #ifdef def_FontName 020. "Why are you trying to do this?" 021. #else 022. #define def_FontName "Lucida Console" 023. #define def_FontSize 10 024. #endif 025. //+------------------------------------------------------------------+ 026. class C_ElementsTrade : private C_Mouse 027. { 028. private : 029. //+------------------------------------------------------------------+ 030. struct st00 031. { 032. struct st_01 033. { 034. uchar Width, 035. Height, 036. digits; 037. }Text; 038. ulong ticket; 039. string szPrefixName, 040. szDescr; 041. EnumEvents ev; 042. double price, 043. open, 044. volume, 045. var; 046. bool bClick, 047. bIsBuy; 048. char weight; 049. color _color; 050. }m_Info; 051. //+------------------------------------------------------------------+ 052. void UpdateViewPort(const double price) 053. { 054. int x, y; 055. 056. ChartTimePriceToXY(0, 0, 0, price, x, y); 057. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 260 : 370)); 058. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 059. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 060. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 061. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 062. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XDISTANCE, x + 10); 063. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2)); 064. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 065. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 066. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 067. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 068. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XDISTANCE, x - 10); 069. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); 070. } 071. //+------------------------------------------------------------------+ 072. inline void CreateLinePrice(void) 073. { 074. string szObj; 075. 076. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 077. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 078. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 079. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 080. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 081. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 082. macro_LineInFocus(false); 083. CreateObjectGraphics(szObj = def_NameBackGround, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 084. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 085. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 086. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 087. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, m_Info.Text.Width + (m_Info.ev == evMsgClosePositionEA ? 40 : 32)); 088. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, m_Info.Text.Height + 5); 089. } 090. //+------------------------------------------------------------------+ 091. inline void CreateBoxInfo(const bool bMove) 092. { 093. string szObj; 094. const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0}; 095. 096. CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 097. ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings"); 098. ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c)); 099. ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? (m_Info.ev == evMsgCloseTakeProfit ? clrDarkGreen : clrMaroon) : (m_Info.bIsBuy ? clrDarkGreen : clrMaroon))); 100. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15)); 101. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 102. } 103. //+------------------------------------------------------------------+ 104. inline void CreateObjectInfoText(void) 105. { 106. string szObj; 107. 108. CreateObjectGraphics(szObj = def_NameObjLabel, OBJ_EDIT, clrNONE, (EnumPriority)(ePriorityDefault)); 109. ObjectSetString(0, szObj, OBJPROP_FONT, def_FontName); 110. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, def_FontSize); 111. ObjectSetInteger(0, szObj, OBJPROP_COLOR, clrBlack); 112. ObjectSetInteger(0, szObj, OBJPROP_BORDER_COLOR, m_Info._color); 113. ObjectSetInteger(0, szObj, OBJPROP_ALIGN, ALIGN_CENTER); 114. ObjectSetInteger(0, szObj, OBJPROP_READONLY, true); 115. ObjectSetInteger(0, szObj, OBJPROP_YSIZE, m_Info.Text.Height); 116. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, m_Info.Text.Width); 117. } 118. //+------------------------------------------------------------------+ 119. inline void CreateButtonClose(void) 120. { 121. string szObj; 122. 123. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault)); 124. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 125. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 126. } 127. //+------------------------------------------------------------------+ 128. public : 129. //+------------------------------------------------------------------+ 130. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, char digits, string szDescr = "\n", const bool IsBuy = true) 131. :C_Mouse(0, "") 132. { 133. uint w, h; 134. 135. ZeroMemory(m_Info); 136. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 137. m_Info._color = _color; 138. m_Info.szDescr = szDescr; 139. m_Info.bIsBuy = IsBuy; 140. m_Info.Text.digits = digits; 141. TextSetFont(def_FontName, def_FontSize * -10); 142. TextGetSize(StringFormat("%." + (string)digits + "f", 8888.88888), w, h); 143. m_Info.Text.Width = (uchar) w + 4; 144. m_Info.Text.Height = (uchar) h + 4; 145. } 146. //+------------------------------------------------------------------+ 147. ~C_ElementsTrade() 148. { 149. ObjectsDeleteAll(0, m_Info.szPrefixName); 150. } 151. //+------------------------------------------------------------------+ 152. inline void UpdatePrice(const double open, const double price, const double vol = 0, const double var = 0) 153. { 154. ObjectsDeleteAll(0, m_Info.szPrefixName); 155. m_Info.volume = (vol > 0 ? vol : m_Info.volume); 156. m_Info.var = (var > 0 ? var : m_Info.var); 157. if (price > 0) 158. { 159. CreateLinePrice(); 160. CreateButtonClose(); 161. CreateObjectInfoText(); 162. } 163. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 164. m_Info.open = open; 165. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 166. if (m_Info.ev != evMsgClosePositionEA) 167. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 168. } 169. //+------------------------------------------------------------------+ 170. void ViewValue(const double profit) 171. { 172. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, StringFormat("$ %." + (string)m_Info.Text.digits + "f", ((profit < 0 ? -(profit) : profit) / m_Info.var) * m_Info.volume)); 173. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 174. ChartRedraw(); 175. } 176. //+------------------------------------------------------------------+ 177. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 178. { 179. string sz0; 180. long _lparam = lparam; 181. double _dparam = dparam; 182. 183. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 184. switch (id) 185. { 186. case (CHARTEVENT_KEYDOWN): 187. if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break; 188. _lparam = (long) m_Info.ticket; 189. _dparam = 0; 190. EventChartCustom(0, evUpdate_Position, _lparam, 0, ""); 191. case CHARTEVENT_CUSTOM + evMsgSetFocus: 192. if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)) 193. UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price); 194. macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev)); 195. EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, ""); 196. m_Info.bClick = false; 197. case CHARTEVENT_CHART_CHANGE: 198. UpdateViewPort(m_Info.price); 199. if (m_Info.ev != evMsgClosePositionEA) 200. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 201. break; 202. case CHARTEVENT_OBJECT_CLICK: 203. sz0 = GetPositionsMouse().szObjNameClick; 204. if (m_Info.bClick) 205. { 206. if (sz0 == def_NameBtnMove) 207. EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, ""); 208. if (sz0 == def_NameBtnClose) 209. EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, PositionGetDouble(m_Info.ev == evMsgCloseTakeProfit ? POSITION_SL : POSITION_TP), PositionGetString(POSITION_SYMBOL)); 210. } 211. m_Info.bClick = false; 212. break; 213. case CHARTEVENT_MOUSE_MOVE: 214. m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick); 215. if (m_Info.weight > 1) 216. { 217. UpdateViewPort(_dparam = GetPositionsMouse().Position.Price); 218. if (m_Info.ev != evMsgClosePositionEA) 219. ViewValue(m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam); 220. if (m_Info.bClick) 221. { 222. if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss)) 223. EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL)); 224. EventChartCustom(0, evMsgSetFocus, 0, 0, ""); 225. } 226. } 227. break; 228. } 229. } 230. //+------------------------------------------------------------------+ 231. }; 232. //+------------------------------------------------------------------+ 233. #undef macro_LineInFocus 234. //+------------------------------------------------------------------+ 235. #undef def_Btn_Close 236. #undef def_PathBtns 237. #undef def_FontName 238. #undef def_FontSize 239. //+------------------------------------------------------------------+ 240. #undef def_NameBackGround 241. #undef def_NameObjLabel 242. #undef def_NameInfoDirect 243. #undef def_NameBtnMove 244. #undef def_NameBtnClose 245. #undef def_NameHLine 246. //+------------------------------------------------------------------+
C_ElementTrade
Todo o problema, se deve justamente a questão da largura do objeto OBJ_EDIT. Esta largura é ajustada na linha 116. Porém o valor colocado na propriedade OBJPROP_XSIZE, é obtido na linha 142. Ou seja, no constructor da classe. Estes pontos que estou mostrando, não são o nosso problema. No bem da verdade, eles são de certa forma a nossa solução. No entanto, na linha 57, temos sim um pequeno inconveniente. Isto por que, mesmo que venhamos a ajustar a largura do objeto OBJ_EDIT. Teremos a possibilidade de que os objetos do indicador possam vir a se sobrepor. A solução para evitar esta sobreposição será vista mais para frente. Já que será preciso fazer diversas coisas para evitar que ela aconteça. Porém, até lá podemos conviver com tal possibilidade. Mas para evitar que a sobreposição aconteça e dificulte o uso do indicador. Precisaremos ajustar os valores na linha 57, também.
Além de tudo isto, vamos fazer uma outra coisa. Vamos adicionar a possibilidade de que o operador, possa chavear entre os modos de visualização disponíveis. E para fazer isto precisaremos mudar algumas coisas neste código visto acima. Para separar melhor as coisas, vamos a um novo tópico.
Pequenas mudanças para melhores resultados
A parte mais engraçada de tudo que faremos. É que, se fosse feita uma modificação na linha 142. A fim de termos mais espaço no objeto OBJ_EDIT, resolveríamos todos os problemas com um esforço bem menor. Porém, isto não é de fato empolgante. Quero mostrar como podemos fazer com que a dimensão do objeto OBJ_EDIT, se ajuste dinamicamente para uma largura mais adequada. Isto para apresentar os valores no gráfico. Bem, esta não é de fato a parte complicada. A parte complicada é fazer com que os objetos não venham a se colidir, ou se sobrepor no gráfico.
Mas antes de nos preocupar com isto. Vamos fazer o seguinte: Vamos permitir que o procedimento ViewValue, possa dizer ao objeto OBJ_EDIT qual deverá ser a largura usada. Isto para que tenhamos um valor totalmente dinâmico. Isto vai gerar um outro problema imediato. Mas vamos primeiro fazer isto. Assim para conseguir efetuar tal mudança, removeremos algumas partes mencionadas no tópico anterior. E as colocaremos dentro do procedimento ViewValue. Com isto algumas mudanças que podem ser vistas no fragmento abaixo.
051. //+------------------------------------------------------------------+ 052. void UpdateViewPort(const double price) 053. { 054. int x, y; 055. 056. ChartTimePriceToXY(0, 0, 0, price, x, y); 057. x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 260 : 370)); 058. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 059. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 060. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 061. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 062. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XDISTANCE, x + 10); 063. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2)); 064. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 065. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 066. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + m_Info.Text.Width + 20); 067. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 068. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XDISTANCE, x - 10); 069. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); 070. } 071. //+------------------------------------------------------------------+ 072. inline void CreateLinePrice(void) 073. { 074. string szObj; 075. 076. CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 077. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 078. ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH)); 079. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 080. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 081. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr); 082. macro_LineInFocus(false); 083. CreateObjectGraphics(szObj = def_NameBackGround, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault)); 084. ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color); 085. ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT); 086. ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER); 087. } 088. //+------------------------------------------------------------------+ . . . 101. //+------------------------------------------------------------------+ 102. inline void CreateObjectInfoText(void) 103. { 104. string szObj; 105. 106. CreateObjectGraphics(szObj = def_NameObjLabel, OBJ_EDIT, clrNONE, (EnumPriority)(ePriorityDefault)); 107. ObjectSetString(0, szObj, OBJPROP_FONT, def_FontName); 108. ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, def_FontSize); 109. ObjectSetInteger(0, szObj, OBJPROP_COLOR, clrBlack); 110. ObjectSetInteger(0, szObj, OBJPROP_BORDER_COLOR, m_Info._color); 111. ObjectSetInteger(0, szObj, OBJPROP_ALIGN, ALIGN_CENTER); 112. ObjectSetInteger(0, szObj, OBJPROP_READONLY, true); 113. } 114. //+------------------------------------------------------------------+ . . . 123. //+------------------------------------------------------------------+ 124. inline void AdjustDinamic(const string szTxt) 125. { 126. uint w, h; 127. 128. TextSetFont(def_FontName, def_FontSize * -10); 129. TextGetSize(szTxt, w, h); 130. m_Info.Text.Height = (uchar) h + 4; 131. m_Info.Text.Width = (uchar) w + 4; 132. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XSIZE, m_Info.Text.Width); 133. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YSIZE, m_Info.Text.Height); 134. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XSIZE, m_Info.Text.Width + (m_Info.ev == evMsgClosePositionEA ? 40 : 32)); 135. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YSIZE, m_Info.Text.Height + 5); 136. } 137. //+------------------------------------------------------------------+ 138. public : 139. //+------------------------------------------------------------------+ 140. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, char digits, string szDescr = "\n", const bool IsBuy = true) 141. :C_Mouse(0, "") 142. { 143. ZeroMemory(m_Info); 144. m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 145. m_Info._color = _color; 146. m_Info.szDescr = szDescr; 147. m_Info.bIsBuy = IsBuy; 148. m_Info.Text.digits = digits; 149. } 150. //+------------------------------------------------------------------+ . . . 173. //+------------------------------------------------------------------+ 174. void ViewValue(const double profit) 175. { 176. string szTxt; 177. 178. szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f", ((profit < 0 ? -(profit) : profit) / m_Info.var) * m_Info.volume); 179. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt); 180. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 181. AdjustDinamic(szTxt); 182. ChartRedraw(); 183. } 184. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Você pode comparar este fragmento com o código completo no tópico anterior. E muito facilmente conseguirá notar onde as mudanças ocorreram. Se bem, que na linha 124, surgiu um novo procedimento. Este visa deixar modelagem totalmente dinâmica. De forma que tanto o OBJ_EDIT, quanto o OBJ_BITMAP_LABEL possam se adaptar ao tamanho da informação a ser apresentada.
Agora veja que no procedimento ViewValue, mudamos um pouco o mesmo. Isto para que possamos fazer com que a largura de OBJ_EDIT possa ser dinamicamente ajustada. Mas podemos melhorar esta modelagem. Já que na linha 181 estamos sempre atualizando esta dimensão. Mesmo quando a largura do texto não muda. Para evitar que a linha 181, chame a todo instante o procedimento AdjustDinamic. Bastará que armazenemos o tamanho do texto a ser apresentado. Se ele não mudar, não faz sentido chamar AdjustDinamic.
Você, caro leitor, pode estar imaginando: Isto é simples, basta usarmos uma variável estática para fazer este armazenamento. Bem, se você não entendeu o que foi dito. Seria como se o código de ViewValue ficasse como mostrado abaixo:
173. //+------------------------------------------------------------------+ 174. void ViewValue(const double profit) 175. { 176. static int sizeTxt = 0; 177. string szTxt; 178. 179. szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f", ((profit < 0 ? -(profit) : profit) / m_Info.var) * m_Info.volume); 180. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt); 181. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 182. if (StringLen(szTxt) != sizeTxt) 183. { 184. AdjustDinamic(szTxt); 185. sizeTxt = StringLen(szTxt); 186. } 187. ChartRedraw(); 188. } 189. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Pensar desta forma não estaria errado. E em muitos casos esta solução de fato funcionaria. Mas, porém, toda via e entretanto. Aqui, e justamente aqui neste código, esta solução não funcionará. Mas por que? O motivo é que a classe C_ElementsTrade é compartilhada entre três segmentos do código. Um para apresentar o take profit, outro para o stop loss e outro para o preço de abertura. Todos estes três segmentos compartilharão a memória de alguma forma. Assim, se você usar uma variável estática no código. O compilador alocará uma posição compartilhada na memória. Preste atenção a isto. Pois é muito comum se tentar fazer tal coisa. E os resultados obtidos, não serem exatamente os esperados durante a fase de planejamento. Variáveis estáticas devem ser trabalhadas com extremo cuidado e atenção. Assim como variáveis globais. Que podem ser visíveis dentro de todo o código. Daqui a pouco, daremos um uso mais adequado esta mesma construção.
Se você, meu caro leitor e entusiasta não entendeu o que acabei de dizer. Releia o trecho até conseguir compreender. Pois entender isto é importante. Ainda mais quando desejamos fazer uso de determinados métodos de programação. Muito bem, então a variável estática não nos ajuda nesta questão. Mas a ideia a ser implementada é mais ou menos esta que acabei de mostrar no fragmento acima. Então vamos ver como ela de fato deverá ser implementada. Isto é visto no fragmento logo abaixo.
025. //+------------------------------------------------------------------+ 026. class C_ElementsTrade : private C_Mouse 027. { 028. private : 029. //+------------------------------------------------------------------+ 030. struct st00 031. { 032. struct st_01 033. { 034. uchar Width, 035. Height, 036. digits; 037. }Text; 038. ulong ticket; 039. string szPrefixName, 040. szDescr; 041. EnumEvents ev; 042. double price, 043. open, 044. volume, 045. var; 046. bool bClick, 047. bIsBuy; 048. char weight; 049. color _color; 050. int sizeText; 051. }m_Info; 052. //+------------------------------------------------------------------+ . . . 156. //+------------------------------------------------------------------+ 157. inline void UpdatePrice(const double open, const double price, const double vol = 0, const double var = 0) 158. { 159. m_Info.sizeText = 0; 160. ObjectsDeleteAll(0, m_Info.szPrefixName); 161. m_Info.volume = (vol > 0 ? vol : m_Info.volume); 162. m_Info.var = (var > 0 ? var : m_Info.var); 163. if (price > 0) 164. { 165. CreateLinePrice(); 166. CreateButtonClose(); 167. CreateObjectInfoText(); 168. } 169. CreateBoxInfo(m_Info.ev != evMsgClosePositionEA); 170. m_Info.open = open; 171. UpdateViewPort(m_Info.price = (price > 0 ? price : open)); 172. if (m_Info.ev != evMsgClosePositionEA) 173. ViewValue(m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price); 174. } 175. //+------------------------------------------------------------------+ 176. void ViewValue(const double profit) 177. { 178. string szTxt; 179. 180. szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f", ((profit < 0 ? -(profit) : profit) / m_Info.var) * m_Info.volume); 181. ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt); 182. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, (profit >= 0 ? clrPaleGreen : clrCoral)); 183. if (StringLen(szTxt) != m_Info.sizeText) 184. { 185. AdjustDinamic(szTxt); 186. m_Info.sizeText = StringLen(szTxt); 187. } 188. ChartRedraw(); 189. } 190. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Observe que na linha 50 adicionamos uma nova variável. Esta fará o mesmo trabalho da variável estática. Porém, como ela não estará em uma região compartilhada da memória. Ela será ajustada em cada um dos segmentos que estaremos a fazer uso. Ou seja, cada um dos três segmentos, poderá ter um valor diferente para esta variável. Note que neste fragmento estou deixando a vista o procedimento UpdatePrice. O motivo disto é justamente para resetar o valor desta variável declarada na linha 50. E isto é feito na linha 159. Mas por que? O motivo é que durante a movimentação das linhas de take profit ou stop loss. A largura para o objeto OBJ_EDIT, pode vir a ser modificada radicalmente. Então para garantir que tenhamos sempre uma largura adequada, resetamos o valor aqui.
Mas de qualquer maneira, veja que a tarefa de colocar o valor na variável é o mesmo, que foi visto no fragmento anterior. Você pode notar isto olhando a linha 186. Com isto resolvemos um dos nossos problemas. Agora precisamos resolver os demais. Como já abordamos muitas coisas aqui neste artigo. E não quero tornar o mesmo extremamente complicado, para você meu caro e estimado leitor. Vamos finalizar ele, vendo como ajustar a posição do eixo X de forma dinâmica entre cada um dos segmentos usados no indicador de posição.
O código para fazer isto pode ser visto no fragmento logo abaixo:
052. //+------------------------------------------------------------------+ 053. short UpdateViewPort(const double price, short size = 0, uint ui = 0) 054. { 055. static short _SizeControls; 056. static short _Width; 057. uint x, y; 058. 059. if (size > 0) 060. { 061. size += (short)(ui + 8); 062. _SizeControls = (_SizeControls > size ? _SizeControls : size); 063. size = (short)(_SizeControls - ui - 12); 064. _Width = (_Width > size ? _Width : size); 065. }else 066. { 067. ChartTimePriceToXY(0, 0, 0, price, x, y); 068. x = 125 + (m_Info.ev == evMsgClosePositionEA ? 0 : (m_Info.ev == evMsgCloseTakeProfit ? _SizeControls : (_SizeControls * 2))); 069. ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x); 070. ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0)); 071. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x); 072. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 073. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XDISTANCE, x + 10); 074. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2)); 075. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + _Width + 20); 076. ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y); 077. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + _Width + 20); 078. ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y); 079. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XDISTANCE, x - 10); 080. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); 081. } 082. return _Width; 083. } 084. //+------------------------------------------------------------------+ . . . 136. //+------------------------------------------------------------------+ 137. inline void AdjustDinamic(const string szTxt) 138. { 139. uint w, h; 140. 141. TextSetFont(def_FontName, def_FontSize * -10); 142. TextGetSize(szTxt, w, h); 143. m_Info.Text.Height = (uchar) h + 4; 144. m_Info.Text.Width = (uchar) w + 4; 145. m_Info.Text.Width = UpdateViewPort(0, m_Info.Text.Width, h = 32); 146. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_XSIZE, m_Info.Text.Width); 147. ObjectSetInteger(0, def_NameObjLabel, OBJPROP_YSIZE, m_Info.Text.Height); 148. ObjectSetInteger(0, def_NameBackGround, OBJPROP_XSIZE, m_Info.Text.Width + h + (m_Info.ev == evMsgClosePositionEA ? 8 : 0)); 149. ObjectSetInteger(0, def_NameBackGround, OBJPROP_YSIZE, m_Info.Text.Height + 5); 150. } 151. //+------------------------------------------------------------------+
Fragmento de C_ElementsTrade
Neste fragmento, que parece um tanto quanto maluco e sem nenhuma lógica. Está acontecendo algo meio que mágico. Como muitos poderiam entender. Mas não. O que estamos fazendo aqui, é justamente dando uma utilidade prática e viável para variáveis estáticas. Note que estamos fazendo a combinação de variáveis estáticas, que serão compartilhadas na memória. Com variáveis convencionais, ou seja, não serão compartilhadas na memória. Mas por que fazer esta maluquice vista neste fragmento? O motivo é simplicidade. Por mais estranho que possa parecer. É mais simples declarar nas linhas 55 e 56, duas variáveis estáticas, que poderão ser lidas por todos os segmentos da classe C_ElementsTrade. Do que criar uma variável global, ou mesmo uma variável estática no código principal.
Você poderia fazer assim. Mas ao colocar as variáveis como sendo estáticas dentro de uma função. Que pertence a classe C_ElementsTrade. Ocultamos e encapsulamos o código de uma maneira bastante eficiente. Já que o código principal que fará uso desta classe, não precisa saber como a classe funciona. Ou se ela precisa ou não de algum tipo especial de variável. Pois é muito mais fácil, você caro leitor ao tentar usar a classe C_ElementsTrade. Esquecer ou não entender, a presença de uma variável global no código principal. Do que simplesmente encapsular todo o código desta maneira como estou mostrando.
Diferente do que acontecia antes, que era um efeito colateral indesejável. Aqui na função UpdateViewPort, queremos de fato que tanto a variável _SizeControl, como a variável _Width, sejam de fato compartilhadas. Ao executar isto, teremos exatamente o resultado visto na animação abaixo.

Note que nesta animação, ao movermos a linha de take profit para uma outra posição. Cuja largura do objeto OBJ_EDIT, venha a ser mudado. Ao finalizar a movimentação, os demais objetos OBJ_EDIT presentes no indicador de posição serão atualizados. Sei que parece ser algo mágico ou impossível de ser feito. Já que você, meu caro leitor, não estaria de fato, esperando por tal acontecimento.
Considerações finais
Neste artigo, fizemos as modificações necessárias para que o indicador de posição pudesse nos apresentar um resultado financeiro. Isto para que o operador, viesse a ter uma noção do valor financeiro que estaria sendo obtido em uma posição aberta. Apesar de alguns operadores gostarem de visualizar outro tipo de informação. Acredito que este artigo conseguiu atingir seu objetivo. Trazendo para você, meu caro leitor, um conhecimento que muitos não tem. Mesmo fazendo uso da linguagem MQL5 a muito tempo. Tal conhecimento é justamente como fazer uso de variáveis estáticas para conseguir um compartilhamento de memória. Isto para evitar precisar declarar uma variável global no código principal. O que tem efeitos colaterais bastante desagradáveis em códigos de classe. Já que variáveis globais, tendem a quebrar o encapsulamento da classe.
Sei que o conteúdo deste artigo, pode ser algo muito confuso no começo. Mas procure estudar o que foi mostrado aqui e está sendo mostrando nestes artigos da série de como construir um sistema de replay/simulador. De qualquer maneira, para aqueles que desejam experimentar as aplicações a fim se saber, se realmente vale o esforço de estudar este material. No anexo, deixarei os códigos já compilados.
| 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.
Portfolio Risk Model using Kelly Criterion and Monte Carlo Simulation
Do básico ao intermediário: Classes (III)
Métodos de conjunto para aprimorar previsões numéricas em MQL5
Criando um Painel de Administração de Trading em MQL5 (Parte VIII): Painel de Análises
- 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