preview
Desenvolvendo um sistema de Replay (Parte 59): Um novo futuro

Desenvolvendo um sistema de Replay (Parte 59): Um novo futuro

MetaTrader 5Exemplos | 31 julho 2024, 21:07
126 1
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 58): Voltando a trabalhar no serviço, mencionei que o sistema passou por algumas mudanças, e que existia um motivo para que fosse feito um pequeno delay entre a colocação do template no gráfico, e a atualização do gráfico pelo serviço. Caso isto, não viesse a ocorrer, os módulos forçariam o serviço a ser encerrado de forma prematura. Talvez eu tenha deixado muitos de vocês, com a pulga atrás da orelha, imaginando como isto poderia ser possível. Mas aqui vamos ver isto, com um pouco mais de detalhes.

Para começar de fato a explicar, e para que você possa ter a devida noção do que explicarei. Veja o vídeo 01, presente logo abaixo.


Vídeo de demonstração de como as coisas podem falhar

Note que neste vídeo, estou mostrando exatamente o que acontece, sem cortes ou truques. Mas por que isto acontece ?!?! Para entender é preciso recapitular o que foi visto no artigo passado. Mas aqui, irei apenas dar uma rápida passada no que foi dito lá.


Entendendo o motivo do serviço ser encerrado

Existe um motivo do serviço ser encerrado, de forma prematura, assim que o MetaTrader 5, aplica o template. O motivo é simples: O indicador, ou melhor dizendo, modulo de controle, está fazendo com que o gráfico seja fechando. Ainda não conseguiu entender ?!?! Bem, então vamos olhar o código do presente no indicador de controle, que está fazendo isto. Este pode ser visto, no fragmento logo abaixo.

53. void OnDeinit(const int reason)
54. {
55.    switch (reason)
56.    {
57.       case REASON_TEMPLATE:
58.          Print("Modified template. Replay // simulation system shutting down.");
59.       case REASON_INITFAILED:
60.       case REASON_PARAMETERS:
61.       case REASON_REMOVE:
62.       case REASON_CHARTCLOSE:
63.          ChartClose(user00);
64.          break;
65.    }
66.    delete control;
67. }

Fragmento de código do módulo de controle

Observe que na linha 63, existe uma chamada que diz ao MetaTrader 5, para fechar o gráfico. Ok, mas quem está de fato fazendo esta chamada ?!?! Isto por conta que se fosse a troca de template, a mensagem presente na linha 58, seria vista na caixa de mensagens do MetaTrader 5. Mas se você observou, tal mensagem não foi impressa. Desta forma, você pode pensar que não é o fato do MetaTrader 5, está trocando o template, que é de fato responsável por fazer com que o gráfico seja fechado.

De certa forma, até em um certo ponto, compreendo a sua dúvida com relação a isto, e uma possível descrença neste fato. Porém o fato, é quem está removendo o indicador de controle, do gráfico, fazendo com que ele seja fechado, é sim a aplicação do template no gráfico pelo MetaTrader 5. No entanto, mas já que isto é feito de uma forma assíncrona, as coisas não acontecem como você talvez imagine.

O que de fato acontece, é que em um dado momento, o MetaTrader 5, concluirá a aplicação do template no gráfico. No momento que isto ocorrer, o MetaTrader 5, enviará um evento Deinit para o indicador. Este tratará o evento, na função OnDeinit, mas diferente do que você possa, estar pensando, o valor presente na variável reason, não é REASON_TEMPLATE, e sim REASON_REMOVE. Isto por que o MetaTrader 5 estará removendo o indicador de controle do gráfico, concluindo assim a aplicação do template. E por este motivo que a mensagem presente na linha 58 não é impressa, na caixa de mensagens do terminal.

Mas agora vem a questão: Por que o MetaTrader 5, não aplica o template de forma síncrona ?!?! Ou seja, assim que a chamada ChartApplyTemplate, presente na linha 87, do arquivo de cabeçalho C_Replay.mqh, é executada o template não é aplicado integramente no gráfico ?!?! O motivo disto é velocidade. Dentro do template, podem existir diversos indicadores, ou mesmo um Expert Advisor, que precisa que um determinado número de barras esteja presente no gráfico, para que de fato eles nos dê alguma indicação útil.

Se o MetaTrader 5, fosse esperar que a chamada ChartApplyTemplate, fosse completada de forma síncrona, a plataforma poderia ficar preciosos segundos totalmente parada, podendo até mesmo travar, caso alguma coisa presente no template, cause alguma falha grave. Mas fazendo o trabalho de forma assíncrona, a plataforma ganha velocidade, mas dificulta algumas coisas para os programadores com menos conhecimento. E uma destas coisas é justamente esta: O fato de que o programador, não está ciente de que algumas das chamadas serão executadas de forma assíncrona, enquanto outras são executadas imediatamente.

Como não adianta você forçar uma atualização do gráfico, via chamada ChartRedraw, a fim de que o template seja implantado imediatamente, temos que fazer algo um pouco diferente. E é justamente o que foi feito, e pode ser visto no vídeo de demonstração.

Mas existe uma outra coisa. Você poderia me questionar, o por que, de não sanar as coisas, de outra forma, como por exemplo, colocando o indicador de controle dentro do template. De fato, isto resolveria o problema. Mas iria nos gerar um outro. Nas versões anteriores, estávamos fazendo uso de uma variável global de terminal, a fim de evitar que o indicador de controle, viesse a aparecer em um gráfico indesejado. Mas agora não estamos mais fazendo isto. Agora a forma de bloqueio, é exatamente outro. Ou seja, não podemos deixar que o template venha a conter o indicador de controle, caso contrário teríamos problemas, que seriam bastante complicados de serem resolvidos.

Lembra que no artigo anterior, informei que o código de ambos os módulos, tanto o de controle, como o de mouse, havia passado por mudanças. E o motivo, foi uma reviravolta que foi dada no momento que voltamos a nossa atenção ao serviço de replay / simulação. Pois bem, vamos então ver como está o novo código de ambos indicadores. Lembrando que focarei, apenas nos pontos em que de fato as coisas mudaram.


Um novo código por conta do serviço de replay / simulação

O código fonte do indicador, ou melhor dizendo, módulo de controle, pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.59"
07. #property link "https://www.mql5.com/pt/articles/12075"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. #property indicator_buffers 1
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Service Graphics\C_Controls.mqh>
13. //+------------------------------------------------------------------+
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. input long user00 = 0;      //ID
17. //+------------------------------------------------------------------+
18. double m_Buff[];
19. int    m_RatesTotal = 0;
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.    ResetLastError();   
24.    if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", new C_Mouse(user00, "Indicator Mouse Study"))) == POINTER_INVALID)
25.       SetUserError(C_Terminal::ERR_PointerInvalid);
26.    if ((_LastError != ERR_SUCCESS) || (user00 == 0))
27.    {
28.       Print("Control indicator failed on initialization.");
29.       return INIT_FAILED;
30.    }
31.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
32.    ArrayInitialize(m_Buff, EMPTY_VALUE);
33.    
34.    return INIT_SUCCEEDED;
35. }
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.    return m_RatesTotal = rates_total;
40. }
41. //+------------------------------------------------------------------+
42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
43. {
44.    (*control).DispatchMessage(id, lparam, dparam, sparam);
45.    if (_LastError >= ERR_USER_ERROR_FIRST + C_Terminal::ERR_Unknown)
46.    {
47.       Print("Internal failure in the messaging system...");
48.       ChartClose(user00);
49.    }
50.    (*control).SetBuffer(m_RatesTotal, m_Buff);
51. }
52. //+------------------------------------------------------------------+
53. void OnDeinit(const int reason)
54. {
55.    switch (reason)
56.    {
57.       case REASON_TEMPLATE:
58.          Print("Modified template. Replay // simulation system shutting down.");
59.       case REASON_INITFAILED:
60.       case REASON_PARAMETERS:
61.       case REASON_REMOVE:
62.       case REASON_CHARTCLOSE:
63.          ChartClose(user00);
64.          break;
65.    }
66.    delete control;
67. }
68. //+------------------------------------------------------------------+
69. 

Código fonte do indicador de controle

Note que não mostrarei o código presente no arquivo de cabeçalho, já que este não passou por nenhuma mudança. Mas apesar de podermos ver todo o código do módulo de controle, logo acima. Nele temos apenas e somente uma única diferença, e está na linha 26. Veja que ali, estamos duas condições. Uma que é o valor de _LastError, e a outra que é o valor de user00. Esta user00, é definida na linha 16, sendo ela a responsável por receber do serviço de replay / simulador, o valor da id do gráfico onde o módulo de controle deverá estar presente.

Agora preste bastante atenção. Normalmente, você como usuário, não irá de fato conseguir definir o valor que será informado na input da linha 16. Isto naturalmente. Mas e se você tentar trapacear o sistema, a fim de salvar toda a configuração como sendo um arquivo de template. Isto poderia fazer com que as coisas viessem a funcionar, nos poupando de todo aquele aborrecimento, causado pela aplicação do template. Certo ?!?! Errado. Isto não irá de fato funcionar. Não da forma como você espera.

Se você, depois que o serviço tiver carregado todas as coisas, e o MetaTrader 5, ter estabilizado o gráfico, salvar este gráfico já pronto, como sendo um template, poderá notar, se abrir o arquivo de template, a presença de algo. Este algo pode ser visto no fragmento logo abaixo.

01. <indicator>
02. name=Custom Indicator
03. path=Services\Market Replay.ex5::Indicators\Market Replay.ex5
04. apply=0
05. show_data=1
06. scale_inherit=0
07. scale_line=0
08. scale_line_percent=50
09. scale_line_value=0.000000
10. scale_fix_min=0
11. scale_fix_min_val=0.000000
12. scale_fix_max=0
13. scale_fix_max_val=0.000000
14. expertmode=1610613824
15. fixed_height=-1
16. 
17. <graph>
18. name=
19. draw=0
20. style=0
21. width=1
22. color=
23. </graph>
24. <inputs>
25. user00=130652731570824061
26. </inputs>
27. </indicator>

Fragmento do arquivo de Template

A posição em que este fragmento de fato estará não importa aqui. Então a numeração das linhas são apenas para nos ajudar a referenciar as coisas e assim facilitar a explicação.

Note que na linha 1 temos uma tag de abertura, e na linha 27 uma tag que fecha a estrutura. Entre estas duas tags, temos diversas informações, entre elas qual a localização do indicador a ser colocado no gráfico. Isto pode ser visto na linha 3. Ótimo. Agora preste atenção a linha 24. Ali temos um tag que abre a parte das inputs que são esperadas pelo indicador que está declarado na linha 3. Assim como a linha 26, fecha a parte referente as inputs.

Pois bem, na linha 25, referenciamos a input, que no código fonte do indicador, está sendo declarada na linha 16, a fim de receber a ID do gráfico. Note que nesta linha 25, do fragmento, estamos enviando um valor para o indicador de controle, a fim de que a condição testada na linha 26, no código fonte do indicador, seja falsa. Assim o indicador seria carregado e tudo funcionaria bem. Correto ?!?! Novamente, você está errado. Pois assim que o serviço de replay / simulação for colocar o indicador de controle no gráfico, da forma correta. O código do indicador dirá que já existe uma outra instância sendo executada ali. Quando isto ocorrer a linha 24, do código do indicador de controle, gerará um resultado que fará com que a linha 25 seja executada. Fazendo assim com que _LastError não tenha mais o valor esperado na linha 26. Ou seja, todo o sistema falhará, fazendo com que o MetaTrader 5 gere um evento Deinit. E este será tratado pelo procedimento presente na linha 53, a tal OnDeinit. Onde teremos a execução partindo de REASON_INITFAILED, fechando o gráfico e fazendo o serviço de replay / simulador ser encerrado.

Veja como as coisas se comportam lindamente, quando fazemos uso adequado de tudo que a plataforma MetaTrader 5, tem a nos oferecer. Mas esta foi a questão do indicador de controle. Mas, e quanto ao indicador de mouse. O que aconteceu com ele ?!?!

Com relação ao indicador de mouse, a coisa é um pouco mais interessante. Isto por que, ficou decidido que permitiremos que ele faça algo, mas não somente ele. No entanto, ele será usado para começar a permitir tal coisa. Por conta disto, foi preciso adicionar e remover algumas coisas do modulo responsável pelo indicador de mouse. Então para delimitar, de maneira adequada as coisas. Vamos colocar ele no próximo tópico.


Pequenas mudanças, para grandes interesses

Apesar de que o módulo de mouse, se voltado primeiramente a ser colocado na janela principal, assim como outras coisas. O MQL5 nos permite fazer um pouco mais do que isto. Sinceramente, não havia pensado e fazer certas coisas. Mas já que o MetaTrader 5, pode ser configurado e ajustado de maneira a permitir, que façamos determinadas coisas rapidamente. Ficou decidido, que seria preciso adicionar, e modificar algumas coisas no código. Então, acompanhe as mudanças e adições que foram feitas, começando com a classe C_Terminal, que pode ser vista logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+
010.    protected:
011.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
012. //+------------------------------------------------------------------+
013.       struct st_Terminal
014.       {
015.          ENUM_SYMBOL_CHART_MODE    ChartMode;
016.          ENUM_ACCOUNT_MARGIN_MODE  TypeAccount;
017.          long           ID;
018.          string         szSymbol;
019.          int            Width,
020.                         Height,
021.                         nDigits,
022.                         SubWin;
023.          double         PointPerTick,
024.                         ValuePerPoint,
025.                         VolumeMinimal,
026.                         AdjustToTrade;
027.       };
028. //+------------------------------------------------------------------+
029.    private   :
030.       st_Terminal m_Infos;
031.       struct mem
032.       {
033.          long   Show_Descr,
034.                 Show_Date;
035.          bool   AccountLock;
036.       }m_Mem;
037. //+------------------------------------------------------------------+
038.       void CurrentSymbol(void)
039.          {
040.             MqlDateTime mdt1;
041.             string sz0, sz1;
042.             datetime dt = macroGetDate(TimeCurrent(mdt1));
043.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
044.       
045.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
046.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
047.             switch (eTS)
048.             {
049.                case DOL   :
050.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
051.                case IND   :
052.                case WIN   : sz1 = "GJMQVZ";       break;
053.                default    : return;
054.             }
055.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
056.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
057.          }
058. //+------------------------------------------------------------------+
059.    public   :
060. //+------------------------------------------------------------------+      
061.       C_Terminal(const long id = 0, const uchar sub = 0)
062.          {
063.             m_Infos.ID = (id == 0 ? ChartID() : id);
064.             m_Mem.AccountLock = false;
065.             m_Infos.SubWin = (int) sub;
066.             CurrentSymbol();
067.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
068.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
069.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
070.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
071.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
072.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
073.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
074.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
075.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
076.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
077.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
078.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
079.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
080.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
081.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
082.             ResetLastError();
083.          }
084. //+------------------------------------------------------------------+
085.       ~C_Terminal()
086.          {
087.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
088.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
089.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
090.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
091.          }
092. //+------------------------------------------------------------------+
093. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
094.          {
095.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
096.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
097.          }
098. //+------------------------------------------------------------------+
099. inline const st_Terminal GetInfoTerminal(void) const
100.          {
101.             return m_Infos;
102.          }
103. //+------------------------------------------------------------------+
104. const double AdjustPrice(const double arg) const
105.          {
106.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
107.          }
108. //+------------------------------------------------------------------+
109. inline datetime AdjustTime(const datetime arg)
110.          {
111.             int nSeconds= PeriodSeconds();
112.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
113.             
114.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
115.          }
116. //+------------------------------------------------------------------+
117. inline double FinanceToPoints(const double Finance, const uint Leverage)
118.          {
119.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
120.             
121.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
122.          };
123. //+------------------------------------------------------------------+
124.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
125.          {
126.             static string st_str = "";
127.             
128.             switch (id)
129.             {
130.                case CHARTEVENT_CHART_CHANGE:
131.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
132.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
133.                   break;
134.                case CHARTEVENT_OBJECT_CLICK:
135.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
136.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
137.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
138.                   break;
139.                case CHARTEVENT_OBJECT_CREATE:
140.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
141.                   st_str = sparam;
142.                   break;
143.             }
144.          }
145. //+------------------------------------------------------------------+
146. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const
147.          {
148.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
149.             ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0);
150.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
151.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
152.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
153.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
154.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
155.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
156.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
157.          }
158. //+------------------------------------------------------------------+
159.       bool IndicatorCheckPass(const string szShortName)
160.          {
161.             string szTmp = szShortName + "_TMP";
162.             
163.             if (_LastError != ERR_SUCCESS) return false;            
164.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
165.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
166.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
167.             {
168.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
169.                Print("Only one instance is allowed...");
170.                SetUserError(C_Terminal::ERR_NoMoreInstance);
171.                
172.                return false;
173.             }
174.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
175.             ResetLastError();
176.    
177.             return true;
178.          }
179. //+------------------------------------------------------------------+
180. };

Código fonte do arquivo de cabeçalho C_Terminal.mqh

Ao passar os olhos no código do arquivo de cabeçalho, que contém a classe C_Terminal, você pode não conseguir notar que existem algumas coisas diferentes aqui. Isto por conta que as mudanças, são bem sutis, porém elas nos permitem fazer muitas coisas. Principalmente coisas que precisaremos fazer no futuro próximo.

Basicamente, todas as mudanças que aconteceram, ocorreram por conta da linha 22. Esta variável não existia antes. Mas agora foi adicionada para que possamos fazer algo muito específico, porém bastante interessante. Direcionar objetos para uma sub janela especifica.

Por conta que esta variável foi adicionada e pode ser lida fora da classe, precisamos garantir que ela tenha um valor adequado. Isto logo de início. Por conta disto, na linha 61, fizemos uma mudança no constructor da classe. Observe que agora, o constructor, também irá receber um parâmetro extra. Tal parâmetro pode receber valores entre 0 e 255, o que é muito mais do que o suficiente. Já que raramente colocamos mais de que 2 ou 3 sub janelas no gráfico. Mas existe um detalhe, e observando a linha 65, resolvemos este com uma declaração de conversão explicita de tipo. E por que não usamos logo o tipo uchar na variável, ao invés de fazer esta conversão aqui, na linha 65 ?!?! O motivo é retro compatibilidade. O MQL5, espera receber o tipo inteiro com sinal, se declaramos a variável com este tipo, simplifica muito a nossa vida depois. Da mesma forma, o fato de que o parâmetro passado seja do tipo uchar, nos permite delimitar muito facilmente, a quantidade de sub janelas a 255. Por este motivo é que as coisas estão sendo feitas assim.

Agora indo para a linha 149, podemos ver onde este valor está sendo utilizado. Note que ele servirá para informar ao MetaTrader 5, em qual janela do gráfico, o objeto deverá ser colocado. Este tipo de coisa não é novidade para muitos que já tem algum conhecimento sobre MQL5. Sendo algo bastante comum, diga-se por passagem. No entanto, a coisa muda de figura, ao observarmos a função IndicadorCheckPass, que se inicia na linha 159.

Normalmente quando colocamos um indicador em uma janela, não precisamos informar, em qual janela o indicador irá ser plotado. No entanto, quando usamos objetos gráficos, a coisa se torna um pouco mais complicada. Isto por que temos que saber qual é a janela em que o objeto será colocado. Sem saber isto, a chamada feita na linha 149, irá sempre colocar o objeto na janela errada. Então preste atenção ao como descobriremos qual é a janela em que o indicador foi colocado.

Para descobrir, qual a janela, e isto de forma bem rápida, usamos a linha 164, onde damos um nome temporário ao indicador que está sendo colocado. Logo depois, na linha 165, usamos uma chamada do MQL5, que é a ChartWindowFind, para que o MetaTrader 5, venha a nós informar exatamente qual é a janela onde o indicador está. Lembre-se, que estamos usando um nome temporário, e que nenhum outro indicador deverá ter um nome parecido, caso contrário, poderemos ter um falso positivo aqui. Caso não venhamos a receber um valor positivo, ajustamos a indicação para apontar para a janela principal, ou seja, a janela com índex zero. Depois disto, na linha 166, garantimos que apenas uma única instância estará presente no gráfico. O restante da função permanece como era antes, não merecendo assim ser comentada novamente.

Muito bem, agora já podemos colocar o módulo de indicador de mouse em qualquer sub janela. Mas para de que fato ele funcione, minimamente bem, será preciso mudar algumas coisas no código das classes. Então vamos ver rapidamente o que mudou no arquivo de cabeçalho C_Mouse.mqh. Este pode ser visto na íntegra logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_"
007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0))
008. //+------------------------------------------------------------------+
009. class C_Mouse : public C_Terminal
010. {
011.    public   :
012.       enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
013.       enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
014.       struct st_Mouse
015.       {
016.          struct st00
017.          {
018.             short    X_Adjusted,
019.                      Y_Adjusted,
020.                      X_Graphics,
021.                      Y_Graphics;
022.             double   Price;
023.             datetime dt;
024.          }Position;
025.          uchar      ButtonStatus;
026.          bool       ExecStudy;
027.       };
028. //+------------------------------------------------------------------+
029.    protected:
030. //+------------------------------------------------------------------+
031.       void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
032.          {
033.             if (!m_OK) return;
034.             CreateObjectGraphics(szName, OBJ_BUTTON);
035.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true);
036.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
037.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack);
038.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor);
039.             ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console");
040.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10);
041.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
042.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x);
043.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
044.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 
045.             ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18);
046.          }
047. //+------------------------------------------------------------------+
048.    private   :
049.       enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
050.       struct st01
051.       {
052.          st_Mouse Data;
053.          color    corLineH,
054.                   corTrendP,
055.                   corTrendN;
056.          eStudy   Study;
057.       }m_Info;
058.       struct st_Mem
059.       {
060.          bool     CrossHair,
061.                   IsFull;
062.          datetime dt;
063.          string   szShortName,
064.                   szLineH,
065.                   szLineV,
066.                   szLineT,
067.                   szBtnS;
068.       }m_Mem;
069.       bool m_OK;
070. //+------------------------------------------------------------------+
071.       void GetDimensionText(const string szArg, int &w, int &h)
072.          {
073.             TextSetFont("Lucida Console", -100, FW_NORMAL);
074.             TextGetSize(szArg, w, h);
075.             h += 5;
076.             w += 5;
077.          }
078. //+------------------------------------------------------------------+
079.       void CreateStudy(void)
080.          {
081.             if (m_Mem.IsFull)
082.             {
083.                CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH);
084.                CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH);
085.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szLineT, OBJPROP_WIDTH, 2);
086.                CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy);
087.             }
088.             m_Info.Study = eStudyCreate;
089.          }
090. //+------------------------------------------------------------------+
091.       void ExecuteStudy(const double memPrice)
092.          {
093.             double v1 = GetInfoMouse().Position.Price - memPrice;
094.             int w, h;
095.             
096.             if (!CheckClick(eClickLeft))
097.             {
098.                m_Info.Study = eStudyNull;
099.                ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
100.                if (m_Mem.IsFull)   ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
101.             }else if (m_Mem.IsFull)
102.             {
103.                string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
104.                   MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0));
105.                GetDimensionText(sz1, w, h);
106.                ObjectSetString(GetInfoTerminal().ID, m_Mem.szBtnS, OBJPROP_TEXT, sz1);                                                
107.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
108.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szBtnS, OBJPROP_XSIZE, w);
109.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szBtnS, OBJPROP_YSIZE, h);
110.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w);
111.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));            
112.                ObjectMove(GetInfoTerminal().ID, m_Mem.szLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
113.                ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
114.             }
115.             m_Info.Data.ButtonStatus = eKeyNull;
116.          }
117. //+------------------------------------------------------------------+
118. inline void DecodeAlls(int xi, int yi)
119.          {
120.             int w = 0;
121. 
122.             xi = (xi > 0 ? xi : 0);
123.             yi = (yi > 0 ? yi : 0);
124.             ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X_Graphics = (short)xi, m_Info.Data.Position.Y_Graphics = (short)yi, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
125.             m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt);
126.             m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price);
127.             ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, xi, yi);
128.             yi -= (int)ChartGetInteger(GetInfoTerminal().ID, CHART_WINDOW_YDISTANCE, GetInfoTerminal().SubWin);
129.             m_Info.Data.Position.X_Adjusted = (short) xi;
130.             m_Info.Data.Position.Y_Adjusted = (short) yi;
131.          }
132. //+------------------------------------------------------------------+
133.    public   :
134. //+------------------------------------------------------------------+
135.       C_Mouse(const long id, const string szShortName)
136.          :C_Terminal(id),
137.          m_OK(false)
138.          {
139.             m_Mem.szShortName = szShortName;
140.          }
141. //+------------------------------------------------------------------+
142.       C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
143.          :C_Terminal(id)
144.          {
145.             if (!(m_OK = IndicatorCheckPass(m_Mem.szShortName = szShortName))) SetUserError(C_Terminal::ERR_Unknown);
146.             if (_LastError != ERR_SUCCESS) return;
147.             m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
148.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
149.              ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
150.             ZeroMemory(m_Info);
151.             m_Info.corLineH  = corH;
152.             m_Info.corTrendP = corP;
153.             m_Info.corTrendN = corN;
154.             m_Info.Study = eStudyNull;
155.             if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
156.                CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH);
157.             ChartRedraw(GetInfoTerminal().ID);
158.          }
159. //+------------------------------------------------------------------+
160.       ~C_Mouse()
161.          {
162.             if (!m_OK) return;
163.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
164.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(GetInfoTerminal().ID, m_Mem.szShortName) != -1);
165.              ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
166.             ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
167.          }
168. //+------------------------------------------------------------------+
169. inline bool CheckClick(const eBtnMouse value) 
170.          {
171.             return (GetInfoMouse().ButtonStatus & value) == value;
172.          }
173. //+------------------------------------------------------------------+
174. inline const st_Mouse GetInfoMouse(void)
175.          {
176.             if (!m_OK)
177.             {
178.                double Buff[];
179.                uCast_Double loc;
180.                int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName);
181. 
182.                ZeroMemory(m_Info.Data);
183.                if (CopyBuffer(handle, 0, 0, 1, Buff) == 1)
184.                {
185.                   loc.dValue = Buff[0];
186.                   m_Info.Data.ButtonStatus = loc._8b[0];
187.                   DecodeAlls((int)loc._16b[1], (int)loc._16b[2]);
188.                }
189.                IndicatorRelease(handle);
190.             }
191. 
192.             return m_Info.Data;
193.          }
194. //+------------------------------------------------------------------+
195. inline void SetBuffer(const int rates_total, double &Buff[])
196.          {
197.             uCast_Double info;
198.             
199.             info._8b[0] = (uchar)(m_Info.Study == C_Mouse::eStudyNull ? m_Info.Data.ButtonStatus : 0);
200.             info._16b[1] = (ushort) m_Info.Data.Position.X_Graphics;
201.             info._16b[2] = (ushort) m_Info.Data.Position.Y_Graphics;
202.             Buff[rates_total - 1] = info.dValue;
203.          }
204. //+------------------------------------------------------------------+
205.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
206.          {
207.             int w = 0;
208.             static double memPrice = 0;
209.       
210.             if (m_OK)      
211.             {
212.                C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
213.                switch (id)
214.                {
215.                   case (CHARTEVENT_CUSTOM + evHideMouse):
216.                      if (m_Mem.IsFull)   ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szLineH, OBJPROP_COLOR, clrNONE);
217.                      break;
218.                   case (CHARTEVENT_CUSTOM + evShowMouse):
219.                      if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH);
220.                      break;
221.                   case CHARTEVENT_MOUSE_MOVE:
222.                      DecodeAlls((int)lparam, (int)dparam);
223.                      if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price);
224.                      if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0);
225.                      m_Info.Data.ButtonStatus = (uchar) sparam;
226.                      if (CheckClick(eClickMiddle))
227.                         if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
228.                      if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
229.                      {
230.                         ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
231.                         if (m_Mem.IsFull)   ObjectMove(GetInfoTerminal().ID, m_Mem.szLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
232.                         m_Info.Study = eStudyExecute;
233.                      }
234.                      if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
235.                      m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
236.                      break;
237.                   case CHARTEVENT_OBJECT_DELETE:
238.                      if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH))
239.                         CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH);
240.                      break;
241.                }
242.             }
243.          }
244. //+------------------------------------------------------------------+
245. };
246. //+------------------------------------------------------------------+
247. #undef macro_NameObjectStudy
248. //+------------------------------------------------------------------+

Código fonte do arquivo de cabeçalho C_Mouse.mqh

A primeira coisa que logo nos chama a atenção, é visto nas linhas seis e sete. Note que diferente do que acontecia antes, aqui geramos uma definição de nomes um pouco diferente. Isto é necessário para que não tenhamos conflitos, entre os nomes de objetos já presentes no gráfico. Desta forma, o nome passa a ser dependente tanto da janela, quanto do número de objetos presentes no gráfico. Ou seja, cada nome passa a ser único.

Existem algumas diferenças menores neste código, mas nada que realmente merece assim algum destaque. No entanto, entre as linhas 64 e 67, temos a declaração de novas variáveis. Estas são usadas para armazenar os nomes dos objetos que serão criados. Mas apenas para que você consiga entender como isto irá se dar, veja na linha 83, um exemplo de atribuição de um nome a um dos objetos. Nesta linha fica bastante claro, como o uso da macro irá se dar a fim de conseguirmos gerar e atribuir o nome a uma das variáveis.

Agora, apesar de grande parte do código, não merecer assim grande destaque, isto por que as modificações foram singelas, e apenas para melhor o suporte ao que precisamos. Existe uma parte no código que de fato merece alguma explicação. Apesar de não estar 100% como gostaríamos que ele estivesse, já nos é suficiente para conseguirmos fazer o que queremos. Estou falando do procedimento que se inicia na linha 118, o DecodeAlls.

Este procedimento funciona maravilhosamente bem, quando o módulo de indicador do mouse, se encontra na janela principal, ou seja, na janela de índex zero. Mas quando colocamos o módulo de mouse, em uma outra janela, os problemas começavam a acontecer. Apesar de termos resolvido, muitos deles, ainda assim outros permanecem, e você poderá ver isto no vídeo que estará no final deste artigo.

Mas o grande detalhe, e este pode deixar você, caro leitor, totalmente pasmo e desorientado, é por que da linha 128. Por que esta linha existe ?!?! E por que ela não existia antes ?!?! Para entender isto, é preciso que você entenda uma outra coisa. O módulo de indicador de mouse, inicialmente, era voltado para ser apresentado apenas na janela de índex zero. Esta janela tem seu ponto Y inicial, no topo da mesma. Ou seja, o Y sempre começa com o valor ZERO. Mas quando adicionamos janelas extras, o Y da janela principal não muda, mas os das janelas extras mudam. Sendo deslocados em um dado valor. Mas para o sistema operacional, ou seja, o WINDOWS, isto não faz a mínima diferença, e ele informa ao MetaTrader 5, exatamente onde o mouse se encontra.

Já o MetaTrader 5, ajusta o valor de posição do mouse, de maneira que ele fique dentro da janela do gráfico. Desta forma valores fora da janela poderão ter um valor negativo, ou positivo. O valor será negativo, caso o mouse esteja acima da área de trabalho da janela. Se você não sabe o que é a área de trabalho da janela, veja a imagem 01, onde toda a região onde se encontra o bitmap, é considerada área de trabalho.

Image 01

Imagem 01 - Entendendo a área de trabalho

Note que as bordas e a barra de título não fazem parte a área de trabalho da janela. Então, caso o ponteiro do mouse, entre na região da barra de título, o MetaTrader 5, e não o sistema operacional, corrigirá o valor de forma que ele fique negativo. Então entenda bem isto, o valor de Y, fica negativo quando entra na região da barra de título, não por que o sistema operacional fez isto, mas sim por que o MetaTrader 5, corrigiu o valor de maneira que ele permaneça dentro da área de trabalho.

Desta forma, quando você adiciona algo, que cria uma região, que seria uma faixa na qual o gráfico deixará de fazer parte, na janela principal, o MetaTrader 5 não entenderá que se trata de uma outra janela. Mesmo que a definimos assim, para o MetaTrader 5, isto não acontece, e ele continuará a ver a janela de forma completa, e toda a área de trabalho estará presente dentro das bordas da janela principal. No entanto, e felizmente, o MetaTrader 5, nos permite saber onde a região começa, ele não nos informa onde ela termina, depende de nós procuramos uma forma de saber isto. Mas o simples fato de saber onde ela começa já irá nos beneficiar.

Para saber onde a região se inicia, fazemos uma chamada a ChartGetInteger, mas usaremos como parâmetros, a constante CHART_WINDOW_YDISTANCE, e o número da sub janela. Novamente dizemos que é uma sub janela, para facilitar as coisas, no entanto tal denominação não é de toda correta.

Este valor retornado pela chamada que está na linha 128 é subtraído do valor convertido. Lembrando que o valor convertido, é o que está sendo informado pelo MetaTrader 5, fazendo assim parte da janela. Sem esta correção, feita na linha 128, assim que o ponteiro do mouse, entrasse em uma posição que o MetaTrader 5, iria nos informa como fazendo parte da região de sub janela, iriamos ter uma falsa indicação da posição da linha de horizontal. Mas fazendo esta correção tal coisa não ocorrerá. Pelo menos, não da forma como ocorreria normalmente.

Uma próxima questão, que de fato merece ser mencionada aqui, se encontra na linha 145, onde armazenamos o nome do curto do indicador. Mas por que fazer isto ?!?! O motivo de fato é visto na linha 164. Sem saber o nome do indicador de mouse, não iriamos conseguir testar e verificar se já podemos ou não informar ao MetaTrader 5, para deixar de enviar eventos de mouse para nós. Alguns poderiam questionar, se não poderíamos deixar o evento ligado direto. Mas isto, de deixar o evento ligado sem fazer uso dele, de certa forma é bobagem. O motivo é que cada vez que o mouse se movesse, o MetaTrader 5 teria que disparar um evento de mouse, e se ninguém de fato o está usando, isto apenas colocaria algo inútil na fila de eventos. Então para evitar fazer isto, desligamos assim que não precisamos mais o envio de eventos de mouse. Mas sem saber o nome do módulo de indicador de mouse, como poderíamos saber se existe um indicador que precisa receber o evento ?!?! Não teria como. Então para facilitar, guardamos o nome do indicador, e usamos este mesmo nome para verificar se já podemos ou não desligar o evento de mouse.

Fora estas mudanças mencionadas, que de fato merecem ser mencionadas, existem outras. Mas por conta de que são coisas relativamente muito mais simples, não entrarei em detalhes sobre elas. Par encontrá-las, bastará você comparar este código do arquivo de cabeçalho C_Mouse.mqh, com os anteriores. Então isto ficará, como sendo uma tarefa, para você aprender um pouco mais, sobre como codificar e modificar as coisas, sem gerar grandes problemas no código final.

Mas como é de ser esperado, o código do arquivo C_Study.mqh, também passou por algumas mudanças. Mas assim como as mudanças que não foram mencionadas no código do arquivo de cabeçalho C_Mouse.mqh, este de C_Study.mqh, não será de fato comentado. Apenas colocarei ele na íntegra, para que você possa saber como o código foi de fato modificado.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. //+------------------------------------------------------------------+
008. class C_Study : public C_Mouse
009. {
010.    private   :
011. //+------------------------------------------------------------------+
012.       struct st00
013.       {
014.          eStatusMarket  Status;
015.          MqlRates       Rate;
016.          string         szInfo,
017.                         szBtn1,
018.                         szBtn2,
019.                         szBtn3;
020.          color          corP,
021.                         corN;
022.          int            HeightText;
023.          bool           bvT, bvD, bvP;
024.          datetime       TimeDevice;
025.       }m_Info;
026. //+------------------------------------------------------------------+
027.       const datetime GetBarTime(void)
028.          {
029.             datetime dt;
030.             int i0 = PeriodSeconds();
031.             
032.             if (m_Info.Status == eInReplay)
033.             {
034.                if ((dt = m_Info.TimeDevice) == ULONG_MAX) return ULONG_MAX;
035.             }else dt = TimeCurrent();
036.             if (m_Info.Rate.time <= dt)
037.                m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
038. 
039.             return m_Info.Rate.time - dt;
040.          }
041. //+------------------------------------------------------------------+
042.       void Draw(void)
043.          {
044.             double v1;
045.             
046.             if (m_Info.bvT)
047.             {
048.                ObjectSetInteger(GetInfoTerminal().ID, m_Info.szBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18);
049.                ObjectSetString(GetInfoTerminal().ID, m_Info.szBtn1, OBJPROP_TEXT, m_Info.szInfo);
050.             }
051.             if (m_Info.bvD)
052.             {
053.                v1 = NormalizeDouble((((GetInfoMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
054.                ObjectSetInteger(GetInfoTerminal().ID, m_Info.szBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
055.                ObjectSetInteger(GetInfoTerminal().ID, m_Info.szBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
056.                ObjectSetString(GetInfoTerminal().ID, m_Info.szBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
057.             }
058.             if (m_Info.bvP)
059.             {
060.                v1 = NormalizeDouble((((iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0) - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
061.                ObjectSetInteger(GetInfoTerminal().ID, m_Info.szBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
062.                ObjectSetInteger(GetInfoTerminal().ID, m_Info.szBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
063.                ObjectSetString(GetInfoTerminal().ID, m_Info.szBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
064.             }
065.          }
066. //+------------------------------------------------------------------+
067. inline void CreateObjInfo(EnumEvents arg)
068.          {
069.             switch (arg)
070.             {
071.                case evShowBarTime:
072.                   C_Mouse::CreateObjToStudy(2, 110, m_Info.szBtn1 = (def_ExpansionPrefix + (string)ObjectsTotal(0)), clrPaleTurquoise);
073.                   m_Info.bvT = true;
074.                   break;
075.                case evShowDailyVar:
076.                   C_Mouse::CreateObjToStudy(2, 53, m_Info.szBtn2 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
077.                   m_Info.bvD = true;
078.                   break;
079.                case evShowPriceVar:
080.                   C_Mouse::CreateObjToStudy(58, 53, m_Info.szBtn3 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
081.                   m_Info.bvP = true;
082.                   break;
083.             }
084.          }
085. //+------------------------------------------------------------------+
086. inline void RemoveObjInfo(EnumEvents arg)
087.          {
088.             string sz;
089.             
090.             switch (arg)
091.             {
092.                case evHideBarTime:
093.                   sz = m_Info.szBtn1;
094.                   m_Info.bvT = false;
095.                   break;
096.                case evHideDailyVar:
097.                   sz = m_Info.szBtn2;
098.                   m_Info.bvD   = false;
099.                   break;
100.                case evHidePriceVar:
101.                   sz = m_Info.szBtn3;
102.                   m_Info.bvP = false;
103.                   break;
104.             }
105.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
106.             ObjectDelete(GetInfoTerminal().ID, sz);
107.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
108.          }
109. //+------------------------------------------------------------------+
110.    public   :
111. //+------------------------------------------------------------------+
112.       C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
113.          :C_Mouse(IdParam, szShortName, corH, corP, corN)
114.          {
115.             if (_LastError != ERR_SUCCESS) return;
116.             ZeroMemory(m_Info);
117.             m_Info.Status = eCloseMarket;
118.             m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
119.             m_Info.corP = corP;
120.             m_Info.corN = corN;
121.             CreateObjInfo(evShowBarTime);
122.             CreateObjInfo(evShowDailyVar);
123.             CreateObjInfo(evShowPriceVar);
124.          }
125. //+------------------------------------------------------------------+
126.       void Update(const eStatusMarket arg)
127.          {
128.             datetime dt;
129.             
130.             switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
131.             {
132.                case eCloseMarket   :
133.                   m_Info.szInfo = "Closed Market";
134.                   break;
135.                case eInReplay      :
136.                case eInTrading   :
137.                   if ((dt = GetBarTime()) < ULONG_MAX)
138.                   {
139.                      m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
140.                      break;
141.                   }
142.                case eAuction      :
143.                   m_Info.szInfo = "Auction";
144.                   break;
145.                default            :
146.                   m_Info.szInfo = "ERROR";
147.             }
148.             Draw();
149.          }
150. //+------------------------------------------------------------------+
151. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
152.          {
153.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
154.             switch (id)
155.             {
156.                case CHARTEVENT_CUSTOM + evHideBarTime:
157.                   RemoveObjInfo(evHideBarTime);
158.                   break;
159.                case CHARTEVENT_CUSTOM + evShowBarTime:
160.                   CreateObjInfo(evShowBarTime);
161.                   break;
162.                case CHARTEVENT_CUSTOM + evHideDailyVar:
163.                   RemoveObjInfo(evHideDailyVar);
164.                   break;
165.                case CHARTEVENT_CUSTOM + evShowDailyVar:
166.                   CreateObjInfo(evShowDailyVar);
167.                   break;
168.                case CHARTEVENT_CUSTOM + evHidePriceVar:
169.                   RemoveObjInfo(evHidePriceVar);
170.                   break;
171.                case CHARTEVENT_CUSTOM + evShowPriceVar:
172.                   CreateObjInfo(evShowPriceVar);
173.                   break;
174.                case (CHARTEVENT_CUSTOM + evSetServerTime):
175.                   m_Info.TimeDevice = (datetime)dparam;
176.                   break;
177.                case CHARTEVENT_MOUSE_MOVE:
178.                   Draw();
179.                   break;
180.             }
181.             ChartRedraw(GetInfoTerminal().ID);
182.          }
183. //+------------------------------------------------------------------+
184. };
185. //+------------------------------------------------------------------+
186. #undef def_ExpansionPrefix
187. #undef def_MousePrefixName
188. //+------------------------------------------------------------------+

Código fonte do arquivo de cabeçalho C_Study.mqh

Mas de qualquer forma, o que de fato me interessa mostrar aqui, neste artigo, é o próximo código. Ou seja, o código fonte do indicador, que pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.59"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12075"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
14. //+------------------------------------------------------------------+
15. C_Study *Study       = NULL;
16. //+------------------------------------------------------------------+
17. input color user02   = clrBlack;                       //Price Line
18. input color user03   = clrPaleGreen;                   //Positive Study
19. input color user04   = clrLightCoral;                  //Negative Study
20. //+------------------------------------------------------------------+
21. C_Study::eStatusMarket m_Status;
22. int m_posBuff = 0;
23. double m_Buff[];
24. //+------------------------------------------------------------------+
25. int OnInit()
26. {
27.    ResetLastError();
28.    Study = new C_Study(0, "Indicator Mouse Study", user02, user03, user04);
29.    if (_LastError != ERR_SUCCESS) return INIT_FAILED;
30.    if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
31.    {
32.       MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
33.       OnBookEvent((*Study).GetInfoTerminal().szSymbol);
34.       m_Status = C_Study::eCloseMarket;
35.    }else
36.       m_Status = C_Study::eInReplay;
37.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
38.    ArrayInitialize(m_Buff, EMPTY_VALUE);
39.    
40.    return INIT_SUCCEEDED;
41. }
42. //+------------------------------------------------------------------+
43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
44. {
45.    m_posBuff = rates_total;
46.    (*Study).Update(m_Status);   
47.    
48.    return rates_total;
49. }
50. //+------------------------------------------------------------------+
51. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
52. {
53.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
54.    (*Study).SetBuffer(m_posBuff, m_Buff);
55.    
56.    ChartRedraw((*Study).GetInfoTerminal().ID);
57. }
58. //+------------------------------------------------------------------+
59. void OnBookEvent(const string &symbol)
60. {
61.    MqlBookInfo book[];
62.    C_Study::eStatusMarket loc = m_Status;
63.    
64.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
65.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
66.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
67.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
68.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
69.    if (loc != m_Status) (*Study).Update(m_Status);
70. }
71. //+------------------------------------------------------------------+
72. void OnDeinit(const int reason)
73. {
74.    if (reason != REASON_INITFAILED)
75.    {
76.       if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
77.          MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
78.    }
79.    delete Study;
80. }
81. //+------------------------------------------------------------------+
82. 

Código fonte do indicador de Mouse

Neste código, logo de cara, você pode notar que não estamos mais fazendo uso de algumas inputs, ou seja, agora a forma de pensar neste indicador, foi modificada. O motivo disto, é para que você tenha liberdade de colocar este indicador, onde melhor lhe parecer que ele deverá ficar. Já que ele poderá vim a ser utilizado para outros propósitos, além dos quais imagino. Sendo assim, não irei impor um ponto onde você deverá colocar ele. Assim sendo, não será mais preciso, que as inputs que informavam a ID do gráfico, assim como o status do ativo, venham a ser configuradas pelo usuário.

Mas quero que você repare, no fato de que na linha 36, fizemos uma pequena mudança. Então caso este módulo, venha a ser colocado no gráfico, do ativo que estará sendo usado pelo serviço de replay / simulador, a linha 36 ajustará as coisas de forma automática. Com isto, agora o usuário não terá mais a possibilidade de fazer aquelas mudanças que antes eram executadas, como por exemplo, ter que informar o ID do gráfico. Um detalhe: Apesar de agora não precisar ser informado o ID, tudo que foi dito nos artigos anteriores, com relação as chamadas, continuam valendo, pelo menos até este momento em que escrevo este artigo.


Conclusão

Com um pouco de trabalho, e fazendo uso de um conhecimento anterior, foi possível mostrar a você, como algumas coisas no MetaTrader 5 trabalham. É verdade, que se você tem acompanhado os artigos, deve ter tido a impressão de que aparentemente não estamos fazendo progresso. Quando na verdade, estamos progredindo, só que de forma um pouco mais lenta. Isto pelo motivo, de que estou tendo que bolar e testar coisas, das quais não sabia se de fato eram possíveis de serem feitas no MetaTrader 5.

Grande parte das pessoas que se autointitulam programadores, simplesmente dizem que não podemos fazer as coisas no MetaTrader 5. De que a plataforma não conta com isto, ou aquilo. No entanto, tenho visto que todas estas pessoas de fato não sabem o que estão falando.

Então para concluir este artigo, deixo o vídeo, onde mostro como o módulo de indicador do mouse, está trabalhando. Note com atenção no vídeo, de que existe uma falha. E estou ciente dela. Mas já que ela não é assim tão grave, deixarei para corrigir ela em outro momento. Quando eu já tiver um nível maior de conhecimento de como o MetaTrader 5 de fato funciona, em alguns aspectos que ainda não consegui compreender.

Mas quero que você, caro leitor, participe deste aprendizado. Então continuarei a mostrar como o sistema que será usado para o replay / simulador, além de poder também ser usado no mercado real, e na conta demo, vai sendo desenvolvido.


Vídeo de demonstração do novo indicador de mouse em funcionamento

Arquivos anexados |
Anexo.zip (420.65 KB)
Últimos Comentários | Ir para discussão (1)
Уроборос
Уроборос | 5 ago 2024 em 12:42
I have a suggestion.
Based on your market replication, do something a little different that can be useful and interesting not only to you, but to many other people.
I would do it myself, but I am not a professional programmer. And study all your codes thoroughly. And I cannot implement my idea on their basis due to lack of time.
And if you find my proposal interesting and/or consider making a commercial product based on this idea, then I would like to get free access to it in payment for the idea.
And if you agree, then I am ready to voice it.
Redes neurais de maneira fácil (Parte 79): consultas agregadas de características (FAQ) Redes neurais de maneira fácil (Parte 79): consultas agregadas de características (FAQ)
No artigo anterior, nos familiarizamos com um dos métodos de detecção de objetos em imagens. No entanto, o processamento de imagens estáticas é um pouco diferente do trabalho com séries temporais dinâmicas, como aquelas relacionadas à dinâmica dos preços que estamos analisando. Neste artigo, quero apresentar a você o método de detecção de objetos em vídeo, que é mais relevante para a nossa tarefa atual.
Do básico ao intermediário: Variáveis (III) Do básico ao intermediário: Variáveis (III)
Aqui iremos ver como usar variáveis e constantes predefinidas pela linguagem MQL5. Além disto iremos dar uma rápida pincelada em um outro tipo especial de variável, que são as funções. Existem diversas situações em que saber como trabalhar da forma correta com tais variáveis, pode ser a diferença entre uma aplicação que funciona e uma que não funciona. O requisito para entender o que será visto aqui, é ter compreendido o que foi visto nos artigos anteriores.
EA de grid-hedge modificado em MQL5 (Parte III): Otimização de uma estratégia de cobertura simples (I) EA de grid-hedge modificado em MQL5 (Parte III): Otimização de uma estratégia de cobertura simples (I)
Na terceira parte, retornamos aos EAs Simple Hedge e Simple Grid, desenvolvidos anteriormente. Agora, vamos melhorar o Simple Hedge EA por meio de análise matemática e abordagem de força bruta (brute force) com o objetivo de otimizar o uso da estratégia. Este artigo se aprofunda na otimização matemática da estratégia, estabelecendo a base para a futura pesquisa de otimização baseada em código nas partes seguintes.
O Problema da Discordância: Mergulhando Mais Fundo na Complexidade da Explicabilidade em IA O Problema da Discordância: Mergulhando Mais Fundo na Complexidade da Explicabilidade em IA
Neste artigo, exploramos o desafio de entender como a IA funciona. Modelos de IA frequentemente tomam decisões de maneiras que são difíceis de explicar, levando ao que é conhecido como o "problema da discordância". Esta questão é fundamental para tornar a IA mais transparente e confiável.