preview
Simulação de mercado: Position View (XVI)

Simulação de mercado: Position View (XVI)

MetaTrader 5Testador |
177 0
Daniel Jose
Daniel Jose

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.

ArquivoDescriçã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.mq5Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação)
Indicators\Market Replay.mq5Cria os controles para interação com o serviço de replay/simulador (É necessário o Mouse Study para interação)
Indicators\Mouse Study.mq5Permite 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.mq5Responsável pela indicação de ordens de mercado, permitindo interação e controle das mesmas
Indicators\Position View.mq5Responsável pela indicação de posições de mercado, permitindo interação e controle das mesmas
Services\Market Replay.mq5Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema)
Arquivos anexados |
Anexo.zip (779.24 KB)
Portfolio Risk Model using Kelly Criterion and Monte Carlo Simulation Portfolio Risk Model using Kelly Criterion and Monte Carlo Simulation
Por décadas, traders vêm utilizando a fórmula do Critério de Kelly para determinar a proporção ideal de capital a ser alocada em um investimento ou aposta, a fim de maximizar o crescimento de longo prazo enquanto minimiza o risco de ruína. No entanto, seguir cegamente o Critério de Kelly utilizando o resultado de um único backtest costuma ser perigoso para traders individuais, pois, na negociação ao vivo, a vantagem de trading diminui com o tempo, e o desempenho passado não é garantia de resultado futuro. Neste artigo, apresentarei uma abordagem realista para aplicar o Critério de Kelly para alocação de risco de um ou mais EAs no MetaTrader 5, incorporando resultados de simulação de Monte Carlo provenientes do Python.
Do básico ao intermediário: Classes (III) Do básico ao intermediário: Classes (III)
Neste artigo será demonstrado como podemos controlar melhor o nosso código. Isto quando estivermos efetuando uma programação orientada em objetos. Apesar de que ainda, estamos apenas no inicio do que pretendo abordar quando o assunto é programação orientada em objetos. Mas o que será visto aqui, lhe ajudará a entender diversas coisas. Minimizando assim futuras dúvidas que podem surgir.
Métodos de conjunto para aprimorar previsões numéricas em MQL5 Métodos de conjunto para aprimorar previsões numéricas em MQL5
Neste artigo, apresentamos a implementação de vários métodos de aprendizagem de conjunto em MQL5 e examinamos sua eficácia em diferentes cenários.
Criando um Painel de Administração de Trading em MQL5 (Parte VIII): Painel de Análises Criando um Painel de Administração de Trading em MQL5 (Parte VIII): Painel de Análises
Hoje, aprofundamos a incorporação de métricas de trading úteis dentro de uma janela especializada integrada ao EA do Painel de Administração. Esta discussão foca na implementação em MQL5 para desenvolver um Painel de Análises e destaca o valor dos dados que ele fornece aos administradores de trading. O impacto é amplamente educacional, pois lições valiosas são extraídas do processo de desenvolvimento, beneficiando tanto desenvolvedores iniciantes quanto experientes. Este recurso demonstra as oportunidades ilimitadas que esta série de desenvolvimento oferece ao equipar gestores de operações com ferramentas avançadas de software. Além disso, exploraremos a implementação das classes PieChart e ChartCanvas como parte da expansão contínua das capacidades do painel de Administração de Trading.