preview
Do básico ao intermediário: Objetos e sub janelas (II)

Do básico ao intermediário: Objetos e sub janelas (II)

MetaTrader 5Exemplos |
32 0
CODE X
CODE X

Introdução

No artigo anterior Do básico ao intermediário: Objetos e sub janelas (I), vimos como poderíamos direcionar objetos criados em um indicador, para dentro da sub janela. Porém, se você prestou atenção, deve ter notado que menciono a existência de uma falha naquele procedimento. Isto, justamente por conta do mecanismo utilizado para reconhecer o índice de subjanela no gráfico, contém um problema. Contudo, temos um outro problema, que, no meu entender, merece ser destacado e resolvido primeiro. Problema este que não foi mencionado no artigo anterior. Isto por conta do fato de que você possivelmente não entenderia o mesmo. Isto antes de ter testado pessoalmente o que foi mostrado naquele artigo.

Neste artigo, iremos abordar algumas formas de se resolver certos erros que podem aparecer ao se utilizar o que vem sendo mostrado. Sendo assim, gostaria que você desse um tempo nas distrações do dia a dia e focasse no que será explicado. Pois alguns erros podem ser um tanto quanto difíceis de entender a sua origem.


Objetos e sub janelas (II)

Se você vem praticando e estudando cada um destes artigos, já deve ter compreendido que o objetivo da linguagem MQL5 é justamente o de permitir que programadores possam, de alguma maneira, controlar o comportamento e o funcionamento do MetaTrader 5. Isto com o objetivo de fazer com que a plataforma gere algum tipo de resultado previamente esperado. Esta é a parte simples, da qual poucos de fato conseguem compreender.

A parte complicada é entender que nem sempre você conseguirá fazer as coisas sem que antes entenda certos mecanismos e conceitos adotados no MetaTrader 5. Um destes conceitos foi explicado anteriormente e trata justamente do fato de que NÃO PODEMOS adicionar duas instâncias de um mesmo indicador em um mesmo gráfico. Isto é fato, e algo garantido pelos mecanismos internos presentes no MetaTrader 5. Porém, se algum argumento ou parâmetro de entrada do indicador for alterado, poderemos adicionar uma segunda instância no mesmo gráfico. Já que neste caso o MetaTrader 5 interpretará esta segunda, ou nova instância, na verdade não está ainda presente no gráfico.

Ok, mas por que motivo, razão ou circunstância, você está mencionando novamente? Bem, meu amigo leitor, o motivo é que existem alguns problemas nos códigos vistos até aqui, que podem tornar as coisas um tanto quanto complicadas. E tais problemas surgem justamente quando vamos refinar algo no próprio código. Assim, preciso que você entenda muito bem o seguinte: apesar de os códigos até aqui funcionarem, como pode ser visto em cada um dos artigos, dadas as animações que representam exatamente o que o código está fazendo. Ao mexer nos códigos, os mesmos podem gerar resultados um tanto quanto confusos. Isto por conta justamente de que existem detalhes faltando em cada um dos códigos.

Para tornar isto mais claro e fácil de entender, vamos utilizar um dos códigos presentes no anexo do artigo anterior. Ao utilizarmos um daqueles códigos, o resultado será o que podemos visualizar na animação logo abaixo.

Animação 01

Até este ponto, que podemos ver na animação 01, tudo está funcionando bem. Demonstrando que o resultado obtido é exatamente o que era esperado. Agora, se você abrir a lista de indicadores presentes neste mesmo gráfico visto na animação 01, irá ver o seguinte.

Imagem 01

Agora vem a questão: Se você tentar adicionar novamente o indicador visto na animação 01, o MetaTrader 5 impedirá o código de ser executado novamente. Isto impedirá que uma nova subjanela seja criada no gráfico. Porém, este impedimento se deve justamente ao fato de que este ponto destacado na imagem 01 está presente no gráfico. Agora, se você remover este indicador destacado na imagem 01, a coisa muda de figura.

Para tornar a coisa mais clara de se entender, você deverá fazer com que o conteúdo visto na imagem 01 fique com o visto na imagem 02, logo abaixo.

Imagem 02

Note que agora, já não teremos o bloqueio do MetaTrader 5, em adicionar novamente o indicador visto na animação 01. Obviamente você pensará que isto é devido ao que foi visto no artigo anterior. Onde a nova janela possivelmente irá receber o objeto que está presente no código. Mas como foi explicado antes, isto não irá ocorrer justamente por conta do nome do objeto a ser usado, indicar que ele já está presente no gráfico. Porém, esta ainda não é a questão. A questão é: Por que motivos, quando removemos o indicador da lista, conforme visto na imagem 02, a sub janela criada por ele não desapareceu junto?

Justamente por ela não ter sido removida com o indicador, é que acabamos por criar uma potencial falha em nossa aplicação. Isto pelo simples fato, de que as coisas não estão completamente amarradas entre si. Desta forma, o resultado agora poderá ser algo parecido com o que é visto logo abaixo.

Animação 02

Note como um simples detalhe, passou a fazer toda a diferença naquele que será o resultado. Acabando, por assim dizer, deixando as coisas muito mais confusas e dando ao usuário uma péssima experiência com a nossa aplicação. Então como podemos corrigir este tipo de falha? Isto a fim de evitar que este tipo problema? Bem, para isto, a melhor e mais simples maneira será atrelar todos os códigos de forma que eles fiquem amarrados entre si. Desta forma, caso algo seja removido, o código principal será notificado e poderá tomar as providências. Garantindo que nada permanecerá ou ser removido do gráfico de maneira indevida, ou incorreta. Note que existem dois tipos de situação sendo mencionada aqui.

A primeira é a de que quando algo não deveria ser removido, esteja sendo retirado do gráfico. Já a segunda se refere a permanência ou não de coisas que deveriam ser removidas quando o código principal está sendo retirado do gráfico.

É muito importante você entender que existem estas duas situações. Pois cada uma exige um tratamento totalmente distinto. Legal, uma vez feita esta distinção, podemos partir para as devidas correções. Lembrando que os mecanismos mostrados aqui, podem e devem ser adaptados para cada situação específica. Vamos começar fazendo com que ao removermos o código principal do gráfico, ele também remova o indicador presente na sub janela, que ele mesmo criou. Apesar de a princípio, remover o indicador possa ser algo simples. Já que tudo que precisamos fazer, é dizer onde e qual o nome do indicador a ser removido. Esta remoção em alguns casos pode ficar um tanto quanto complexa. Mas vamos pelo caso mais simples primeiro. Assim, precisaremos observar antes os códigos que estaremos utilizando.

O primeiro código é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_height          150
08. #property indicator_plots           0
09. //+------------------------------------------------------------------+
10. #define def_ShortName "Test 123."
11. //+------------------------------------------------------------------+
12. class C_MiniChart
13. {
14.     private :
15. //+----------------+
16.         string  m_szNameObject;
17.         int     m_Sub;
18. //+----------------+
19.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
20. //+----------------+
21.         int WhatSubWin(void)
22.         {
23.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
24.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
25.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
26.                         return c0;
27.             return INVALID_HANDLE;
28.         }
29. //+----------------+
30.     public  :
31. //+----------------+
32.         C_MiniChart(string szName)
33.         {
34.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
35.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
36.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
37.             SetInteger(OBJPROP_XDISTANCE, 10);
38.             SetInteger(OBJPROP_YDISTANCE, 0);
39.             SetInteger(OBJPROP_XSIZE, 300);
40.             SetInteger(OBJPROP_YSIZE, indicator_height);
41.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
42.             SetInteger(OBJPROP_DATE_SCALE, false);
43.             SetInteger(OBJPROP_PRICE_SCALE, false);
44.             SetInteger(OBJPROP_SELECTABLE, true); 
45.             SetInteger(OBJPROP_SELECTED, false);
46.         }
47. //+----------------+
48.         ~C_MiniChart()
49.         {
50.             ObjectDelete(0, m_szNameObject);
51.             ChartRedraw();
52.         }
53. //+----------------+
54. }*gl_MiniChart;
55. //+------------------------------------------------------------------+
56. int OnInit(void)
57. {
58.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
59. 
60.     gl_MiniChart = new C_MiniChart("Mini Chart");
61.    
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnDeinit(const int reason)
71. {
72.     delete gl_MiniChart;
73. };
74. //+------------------------------------------------------------------+

Código 01

Já o segundo código é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Código 02

Agora preste atenção, pois isto que será explicado é muito importante. Para remover um indicador do gráfico, usaremos a função ChartIndicatorDelete, o que não envolve nenhuma complicação. Já que a documentação explica muito bem que tipo de informação precisamos utilizar. Porém, e esta é a parte importante, é que o terceiro parâmetro a ser repassado a função ChartIndicatorDelete, precisa ser o nome do indicador que estaremos removendo. Mas qual nome? Bem, isto depende de cada caso específico. Por isto você precisa prestar muita atenção ao que estará sendo implementado dentro do seu código. No nosso caso específico, o nome a ser utilizado está sendo declarado na linha dez do código 01. Porém, a função deverá ser usada no código 02. Hum, como assim? Isto parece não fazer sentido.

Entretanto, como o que estamos implementando aqui é algo bem simples, justamente para ser didático, fazer isto é algo bastante direto. Bastará modificar o código 02, como mostrado logo abaixo.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int gl_subWin;
14. //+------------------------------------------------------------------+
15. int OnInit(void)
16. {
17.     ChartIndicatorAdd(0, gl_subWin = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
18.     
19.     return INIT_SUCCEEDED;
20. };
21. //+------------------------------------------------------------------+
22. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
23. {
24.     return rates_total;
25. };
26. //+------------------------------------------------------------------+
27. void OnDeinit(const int reason)
28. {
29.     ChartIndicatorDelete(0, gl_subWin, "Test 123.");
30. };
31. //+------------------------------------------------------------------+

Código 03

Observe que na linha treze deste código 03 estamos declarando uma variável. Esta será inicializada na linha dezessete, onde capturamos qual a sub janela onde o indicador que será colocado no gráfico se encontra. Porém, note que na linha vinte e nove, deste código 03, estamos utilizando uma informação que está de fato presente na linha dez do código 01. Este tipo de coisa criará a tal amarra que desejamos. Mas, e sempre existe um, mas, esta forma de implementar as funcionalidades, apesar de funcionar, também acaba tornando a coisa muito menos práticas. Dificultando assim a manutenção conforme o código for crescendo com o tempo. Não se esqueça que isto que estamos vendo aqui, é apenas e tão somente uma demonstração. Então, não se iluda com esta aparente simplicidade vista aqui. Contudo, antes de nos aprofundarmos ainda mais nisto. Vamos ver o resultado do que acabamos de conseguir ao implementar o código desta nova maneira.

Isto é mostrado na animação logo abaixo.

Animação 03

Magnifico. Esta simples demonstração vista na animação 03, já nos garante um certo alívio e controle sobre o nosso código. Mas como foi dito, não podemos, ou melhor dizendo, não devemos aceitar as coisas desta maneira, ainda mais em um código que esteja em desenvolvimento. Assim a melhor alternativa será fazer algumas modificações, a fim de tornar as coisas um pouco mais adequadas. Pelo menos, pensando na questão do desenvolvimento futuro do código.

Para conseguir isto, começaremos a trabalhar com arquivos de inclusão, ou os chamados arquivos de cabeçalho. Esta é a parte fácil. Primeiro criamos um arquivo em um diretório comum para todo e qualquer código que faça parte da aplicação final. Normalmente este arquivo estaria no diretório include dentro da raiz. Mas, em alguns casos é mais simples e prático, manter este arquivo de cabeçalho com os arquivos do código-fonte da aplicação. Porém, para manter uma certa organização, passamos a colocar tudo em uma pasta include, dentro da pasta do projeto principal. Isto parece soar um tanto quanto confuso e complicado. No entanto, na prática, você verá que isto é bem mais simples do que parece. Basicamente fazemos assim:

1. //+------------------------------------------------------------------+
2. #define def_Resource "Code 03.ex5"
3. //+------------------------------------------------------------------+
4. #define def_ShortName "Test 123."
5. //+------------------------------------------------------------------+

Código 04

Este código 04 é será nosso arquivo de cabeçalho. Agora vamos voltar nossa atenção aos códigos dos indicadores, e os mudaremos como pode ser visto logo abaixo.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\defines.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_height          150
10. #property indicator_plots           0
11. //+------------------------------------------------------------------+
12. class C_MiniChart
13. {
14.     private :
15. //+----------------+
16.         string  m_szNameObject;
17.         int     m_Sub;
18. //+----------------+
19.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
20. //+----------------+
21.         int WhatSubWin(void)
22.         {
23.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
24.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
25.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
26.                         return c0;
27.             return INVALID_HANDLE;
28.         }
29. //+----------------+
30.     public  :
31. //+----------------+
32.         C_MiniChart(string szName)
33.         {
34.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
35.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
36.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
37.             SetInteger(OBJPROP_XDISTANCE, 10);
38.             SetInteger(OBJPROP_YDISTANCE, 0);
39.             SetInteger(OBJPROP_XSIZE, 300);
40.             SetInteger(OBJPROP_YSIZE, indicator_height);
41.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
42.             SetInteger(OBJPROP_DATE_SCALE, false);
43.             SetInteger(OBJPROP_PRICE_SCALE, false);
44.             SetInteger(OBJPROP_SELECTABLE, true); 
45.             SetInteger(OBJPROP_SELECTED, false);
46.         }
47. //+----------------+
48.         ~C_MiniChart()
49.         {
50.             ObjectDelete(0, m_szNameObject);
51.             ChartRedraw();
52.         }
53. //+----------------+
54. }*gl_MiniChart;
55. //+------------------------------------------------------------------+
56. int OnInit(void)
57. {
58.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
59. 
60.     gl_MiniChart = new C_MiniChart("Mini Chart");
61.    
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnDeinit(const int reason)
71. {
72.     delete gl_MiniChart;
73. };
74. //+------------------------------------------------------------------+

Código 05

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\defines.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_plots           0
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. #define def_ShortNamePrinc def_ShortName + "_main"
14. //+------------------------------------------------------------------+
15. int gl_subWin;
16. //+------------------------------------------------------------------+
17. int OnInit(void)
18. {
19.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNamePrinc);
20.     ChartIndicatorAdd(0, gl_subWin = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
21.     
22.     return INIT_SUCCEEDED;
23. };
24. //+------------------------------------------------------------------+
25. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
26. {
27.     return rates_total;
28. };
29. //+------------------------------------------------------------------+
30. void OnDeinit(const int reason)
31. {
32.     ChartIndicatorDelete(0, gl_subWin, def_ShortName);
33. };
34. //+------------------------------------------------------------------+

Código 06

Note que aqui no código 06, adicionei a linha treze para podermos dar um nome para o indicador principal. Isto será bastante útil em breve. Mas por hora, o que nos interessa é que agora podemos ajustar as coisas de maneira bem mais simples e prática. Sem risco de gerar um monstro incontrolável com o passar do tempo. Existe outra forma de fazer algo muito parecido, mas sem usar um arquivo de cabeçalho para isto. Porém, vamos fazer uso deste modo mais simples por hora. Com isto, o resultado da execução destes códigos visto acima, será o mesmo da animação 03. Sendo assim podemos passar para o que seria o próximo passo.

Agora precisamos tomar algumas decisões, que definirão o que será feito a seguir. De qualquer forma, preciso que você já tenha no mínimo, uma base sobre como eventos são gerados e tratados. Para isto, existem artigos anteriores, nesta mesma sequência, onde foi explicado justamente estas tais bases necessárias. Em resumo, você precisará ter entendido, dois artigos:

Do básico ao intermediário: Eventos (I)

Do básico ao intermediário: Eventos (II)

Entender estes dois artigos, é de suma importância para entender o que faremos daqui para frente. Já que independente da decisão a ser tomada, estes dois artigos são a base para entender o que acontecerá a seguir. A ideia aqui é iniciar uma comunicação entre códigos diferentes. E para fazer isto, utilizaremos eventos. Assim, os dois indicadores passarão a se comunicar e efetuem aquilo que ficará decidido que eles deverão fazer.

Não se esqueça de que neste ponto, decisões tomadas deverão ser avaliadas calmamente.

Pois bem, a primeira decisão se refere ao comportamento do código quando removermos algum objeto criado por algum de nossos indicadores. Este tipo de coisa, que a princípio parece bobagem, pode mudar completamente a experiência do usuário ou operador. Mas também define como iremos de fato implementar o nosso código. A princípio, vamos seguir a seguinte ideia: se algum objeto criado por algum dos indicadores for removido, o próprio indicador, que criou tal objeto também deverá ser removido do gráfico. Sei que esta é uma decisão, um tanto quanto punitiva para o usuário ou operador. Mas como aqui o objetivo não é o de criar algo específico, mas sim o de demonstrar como certas coisas podem ser feitas. Por isto, não vejo problema em tal abordagem.

Saber se um objeto foi ou não removido do gráfico, não é uma das tarefas mais complicadas. No entanto, fazer isto de forma manual, é no mínimo pura perda de tempo. Já que podemos dizer ao MetaTrader 5, que ele nos informe, qual o objeto removido do gráfico. Sabendo isto, fica muito mais fácil e simples implementar o restante do procedimento que desejamos fazer.

Só que existe um pequeno problema. Por padrão, alguns eventos não são disparados pelo MetaTrader 5. Precisamos dizer a ele, que desejamos ser informados quando tais eventos aconteçam. E um destes eventos, é justamente o de remoção de objetos do gráfico. Mas não se preocupe, a forma de dizermos ao MetaTrader 5 que desejamos ser informados a respeito da remoção de algum objeto, é algo bem simples. Tudo que precisamos fazer é enviar um comando para o MetaTrader 5, via código MQL5. A questão, não é como, mas sim onde colocar tal comando.

Explico: Você não precisa necessariamente, adicionar o comando em todos os indicadores, ou códigos. Basta colocar o comando em um dos códigos, que sempre que ocorrer o tal evento, o MetaTrader 5 irá nos informar sobre ele. Isto focando em um gráfico específico. Pessoalmente, gosto de utilizar este tipo de comando em um ponto-chave do código. Normalmente dentro do que seria o arquivo principal. Ou em alguns momentos, no arquivo onde objetos realmente estão sendo criados, e precisam ser observados. Tal decisão de onde colocar o comando depende de você e de ninguém mais. Ficando assim ao seu critério onde utilizar ele. Como quero simplificar ao máximo as coisas. Vamos usar o comando, naquele que pode ser considerado o arquivo principal do código. Assim, modificando o código 06, temos o que podemos ver logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\defines.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_plots           0
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. #define def_ShortNamePrinc def_ShortName + "_main"
14. //+------------------------------------------------------------------+
15. int gl_subWin;
16. //+------------------------------------------------------------------+
17. int OnInit(void)
18. {
19.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNamePrinc);
20.     ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
21.     ChartIndicatorAdd(0, gl_subWin = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
27. {
28.     return rates_total;
29. };
30. //+------------------------------------------------------------------+
31. void OnDeinit(const int reason)
32. {
33.     ChartIndicatorDelete(0, gl_subWin, def_ShortName);
34.     ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
35. };
36. //+------------------------------------------------------------------+

Código 07

Note que na linha vinte deste código 07, estamos dizendo ao MetaTrader 5, que desejamos receber uma notificação quando um objeto for removido do gráfico. Como boa prática de programação, é sempre muito educado, informarmos também que não queremos mais receber estas mesmas notificações, quando assim não precisarmos mais delas. Devido a isto, também incluímos na linha trinta e quatro uma chamada onde desligamos o recebimento de tais notificações. Mas isto pode ser feito em momentos especiais, onde não queremos notificações por estarmos removendo algum objeto desnecessário do gráfico. Muito bem, esta é a primeira parte. Agora vamos à segunda, que é justamente o de decidir o que irá acontecer quando algum objeto for removido.

Podemos fazer duas coisas aqui. A primeira é simplesmente recriar o tal objeto. Já a segunda é a de remover o indicador, ou aplicação que criou tal objeto. Como esta segunda abordagem, em princípio é um pouco mais simples. Tanto pelo ponto de vista de implementação, quando da necessidade de não precisarmos ficar preocupados com parâmetros internos do próprio objeto. Vamos adotar ela como mecanismo inicial de tratamento.

Aqui é onde as coisas começam a ficar um tanto quanto interessantes. Mas podem se tornar bastante confusas também. Para entender isto, veja o código 05. Nele é onde criamos os tais objetos que irão ser mostrados no gráfico. Mas note uma coisa. O nome do objeto, se encontra sendo declarado na linha sessenta do código 05. Fazer as coisas desta forma, a princípio, não é algo que irá nos gerar problemas. Porém, pode acontecer de o usuário, ou operador ter colocado um objeto com o mesmo nome no gráfico. Isto antes de adicionar o indicador no gráfico. Sendo assim, podemos ter um tipo de inconsistência sendo criada. Para contornar isto, precisamos que o objeto que será criado pelo nosso código, tenha um nome único e de fácil reconhecimento. Por isto, vamos mudar como este objeto tem seu nome sendo gerado.

Para não criamos um arquivo gigante sem necessidade. Vamos remover a classe de dentro deste código 05, criando assim outro arquivo de cabeçalho. Este é visto logo abaixo, já com a correção na forma de implementar o nome do objeto.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "defines.mqh"
05. //+------------------------------------------------------------------+
06. class C_MiniChart
07. {
08.     private :
09. //+----------------+
10.         string  m_szNameObject;
11.         int     m_Sub;
12. //+----------------+
13.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
14. //+----------------+
15.         int WhatSubWin(void)
16.         {
17.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
18.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
19.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
20.                         return c0;
21.             return INVALID_HANDLE;
22.         }
23. //+----------------+
24.     public  :
25. //+----------------+
26.         C_MiniChart(string szName)
27.         {
28.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
29.             ObjectCreate(0, m_szNameObject = def_ShortName + "#" + szName + "#" + (string)ObjectsTotal(0, -1, -1) , OBJ_CHART, m_Sub, 0, 0);
30.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
31.             SetInteger(OBJPROP_XDISTANCE, 10);
32.             SetInteger(OBJPROP_YDISTANCE, 0);
33.             SetInteger(OBJPROP_XSIZE, 300);
34.             SetInteger(OBJPROP_YSIZE, indicator_height);
35.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
36.             SetInteger(OBJPROP_DATE_SCALE, false);
37.             SetInteger(OBJPROP_PRICE_SCALE, false);
38.             SetInteger(OBJPROP_SELECTABLE, true); 
39.             SetInteger(OBJPROP_SELECTED, false);
40.         }
41. //+----------------+
42.         ~C_MiniChart()
43.         {
44.             ObjectDelete(0, m_szNameObject);
45.             ChartRedraw();
46.         }
47. //+----------------+
48.         bool operator==(const string szArg) { return (szArg == m_szNameObject); }
49. //+----------------+
50. };
51. //+------------------------------------------------------------------+

Código 08

A correção a fim de criar um nome único, está sendo feita na linha vinte e nove deste código 08. Agora note que na linha quarenta e oito estamos criando um operador sobrecarregado a fim de testar a própria classe. Isto é importante para podermos checar se o objeto removido, foi ou não um dos que criamos dentro dos indicadores. Nesta mesma sequência, explicamos a sobrecarga de operadores, veja os artigos para mais detalhes, sendo o primeiro Do básico ao intermediário: Sobrecarga de operadores (I). Como tenho falando, o conhecimento vai se acumulando com o tempo. Agora podemos ver como ficou o antigo código 05. Este é mostrado logo abaixo na íntegra.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\C_MiniChart.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_height          150
10. #property indicator_plots           0
11. //+------------------------------------------------------------------+
12. C_MiniChart *gl_MiniChart;
13. //+------------------------------------------------------------------+
14. int OnInit(void)
15. {
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
17.     gl_MiniChart = new C_MiniChart("Mini Chart");
18.    
19.     return INIT_SUCCEEDED;
20. };
21. //+------------------------------------------------------------------+
22. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
23. {
24.     return rates_total;
25. };
26. //+------------------------------------------------------------------+
27. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
28. {
29.     switch (id)
30.     {
31.         case CHARTEVENT_OBJECT_DELETE:
32.             if (gl_MiniChart == sparam)
33.             {
34.                 ChartIndicatorDelete(0, ChartWindowFind(0, def_ShortName), def_ShortName);
35.                 OnDeinit(REASON_INITFAILED);
36.             }
37.             break;      
38.     }
39. };
40. //+------------------------------------------------------------------+
41. void OnDeinit(const int reason)
42. {
43.     delete gl_MiniChart;
44. };
45. //+------------------------------------------------------------------+

Código 09

Apesar deste código não ser muito bonito, ele é bastante funcional. Porém, é preciso ficar atento a alguns detalhes para evitar futuras surpresas. Um destes detalhes, é o que está acontecendo na linha trinta e um. Observe que aqui estamos capturando o evento CHARTEVENT_OBJECT_DELETE. Porém, toda via e, entretanto, este evento não está sendo ligado aqui, neste código 09, mas sim no código 07. Já que o código 07 é considerado o código principal da aplicação. O próximo detalhe, é que na linha trinta e quatro estamos removendo o próprio indicador do gráfico, para logo em seguida remover o código usando para isto uma chamada a OnDeinit.

Mas por que fazer isto? O motivo é que queremos limpar completamente, qualquer presença do indicador no gráfico. Isto para que a próxima etapa seja um tanto quanto mais simples de ser executada. Podemos melhorar um pouco mais as coisas aqui. Mas por hora, isto já será o suficiente. Agora vamos mexer no código principal, fazendo com que o código 07, fique como mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\defines.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_plots           0
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. #define def_ShortNamePrinc def_ShortName + "_main"
14. //+------------------------------------------------------------------+
15. int gl_subWin;
16. //+------------------------------------------------------------------+
17. int OnInit(void)
18. {
19.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNamePrinc);
20.     ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
21.     ChartIndicatorAdd(0, gl_subWin = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
27. {
28.     return rates_total;
29. };
30. //+------------------------------------------------------------------+
31. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
32. {
33.     Print(__FILE__, " -> ", __FUNCTION__, " ?? ", EnumToString((ENUM_CHART_EVENT)id), " >> ", sparam);
34. };
35. //+------------------------------------------------------------------+
36. void OnDeinit(const int reason)
37. {
38.     ChartIndicatorDelete(0, gl_subWin, def_ShortName);
39.     ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
40. };
41. //+------------------------------------------------------------------+

Código 10

Este código 10 ainda não está finalizado. Antes de fazer isto, quero que você veja e entenda uma coisa aqui. Observe na linha trinta e três, que estaremos imprimindo algo no terminal. É justamente esta informação que será impressa, é que desejo que você entenda. Sendo assim, compilamos os códigos e jogamos no gráfico a aplicação recém compilada. Desta forma, uma vez que os indicadores estejam presentes no gráfico, poderemos fazer o que é visto na animação logo abaixo.

Animação 04

Hum, esta animação 04, me pareceu um tanto quanto intrigante. De fato, ela é um tanto quanto intrigante, mas, ao mesmo tempo, interessante. Agora quero que você observe algo em particular nesta animação. E para isto, vamos usar a imagem vista logo abaixo.

Imagem 03

Preste atenção a este ponto destacado na imagem 03. Apesar de muitos não entenderem bem o que está sendo impresso aqui. Quero que você observe com atenção esta região destacada. Note que o valor de sparam está indicando o nome do objeto que foi removido do gráfico. Mas, como durante a definição do nome, utilizarmos também, parte do nome curto do indicador, podemos usar isto ao nosso favor agora.

Para isto, bastará que isolemos o nome do indicador e verificar se ele é o mesmo que o nosso código principal colocou no gráfico. Caso seja, podemos direcionar a execução do código a fim de conseguir um determinado tipo de resultado. Desta maneira chegamos à parte, onde aquelas decisões tomadas lá no início, farão toda a diferença. Perceba que até aqui, fizemos tudo para remover a presença do indicador auxiliar do gráfico. Mas isto poderia ser feito de outras formas.

Como foi visto, o código 10 é capaz de interceptar os eventos de remoção do objeto. Assim sendo, poderíamos tratar as coisas de forma bastante direta. Já que o objetivo primário seria justamente o de remover os indicadores, caso algum objeto colocado por eles no gráfico fosse removido. Mas como já fizemos parte da remoção diretamente no código do indicador auxiliar, como pode ser visto no código 09. Aqui e agora, precisamos apenas remover o próprio indicador principal do gráfico, isto assim que ele reconhecer que o indicador auxiliar foi removido do gráfico. Para fazer isto da maneira correta e sem tropeços, precisamos modificar o código 10, para o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\defines.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_plots           0
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. #define def_ShortNamePrinc def_ShortName + "_main"
14. //+------------------------------------------------------------------+
15. int gl_subWin;
16. //+------------------------------------------------------------------+
17. int OnInit(void)
18. {
19.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNamePrinc);
20.     ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
21.     ChartIndicatorAdd(0, gl_subWin = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
27. {
28.     return rates_total;
29. };
30. //+------------------------------------------------------------------+
31. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
32. {
33.     switch (id)
34.     {
35.         case CHARTEVENT_OBJECT_DELETE:
36.             {
37.                 string szRet[];
38.                 StringSplit(sparam, '#', szRet);
39.                 if (szRet[0] == def_ShortName)
40.                     ChartIndicatorDelete(0, 0, def_ShortNamePrinc);
41.             }
42.             break;
43.     }
44. };
45. //+------------------------------------------------------------------+
46. void OnDeinit(const int reason)
47. {
48.     ChartIndicatorDelete(0, gl_subWin, def_ShortName);
49.     ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
50. };
51. //+------------------------------------------------------------------+

Código 11

Agora veja como a questão acabou sendo resolvida, para o que a via sido decidido. Tudo que foi preciso fazer foi adicionar uma forma de filtrar o parâmetro sparam, a fim de buscar o nome do indicador. Uma vez que isto tenha sido conseguido, usamos a linha trinta e nove para testar se o nome bate com aquele definido lá no código 04. Com isto, se este teste for verdadeiro, a próxima coisa a ser feita, será encerrar o indicador principal, conforme mostrado na linha quarenta. E isto encerrará completamente a presença de ambos indicadores no gráfico.

Esta abordagem mostrada aqui, é apenas um dos caminhos que podemos seguir de modo a ter um bom controle sobre como nosso código está sendo apresentado ao usuário. Sei que a princípio, este tipo de modelagem, acaba por punir o usuário, ou operador, que remove de maneira indiscriminada objetos do gráfico. Mas, a meu ver, esta abordagem é consideravelmente mais simples, justamente por que não precisamos nos ater a manter certas informações a respeito de posicionamento e tão pouco características de cada objeto presente no gráfico. O que facilita bastante a compreensão do código.

No entanto, você pode estar se perguntando, ao ver o que este código 11 está fazendo. Será que não teria uma forma mais simples de fazer tudo isto? Já que antes de começarmos, o código principal, já era capaz de remover o indicador auxiliar do gráfico. E sim meu caro leitor. Por conta disto, no anexo, você terá acesso a dois códigos diferentes. Onde um mostrará o que foi feito aqui, enquanto o outro mostrará que precisamos de bem menos código no final. Para selecionar um ou outro, bastará modificar um detalhe no código 04. Então preste atenção: No anexo, originalmente o código 04 será o mesmo visto até este ponto do artigo. Contudo, no anexo, você terá outro código para o indicador auxiliar, este é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\C_MiniChart.mqh"
07. //+------------------------------------------------------------------+
08. #property indicator_chart_window
09. #property indicator_height          150
10. #property indicator_plots           0
11. //+------------------------------------------------------------------+
12. C_MiniChart *gl_MiniChart;
13. //+------------------------------------------------------------------+
14. int OnInit(void)
15. {
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
17.     gl_MiniChart = new C_MiniChart("Mini Chart");
18.    
19.     return INIT_SUCCEEDED;
20. };
21. //+------------------------------------------------------------------+
22. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
23. {
24.     return rates_total;
25. };
26. //+------------------------------------------------------------------+
27. void OnDeinit(const int reason)
28. {
29.     delete gl_MiniChart;
30. };
31. //+------------------------------------------------------------------+

Código 12

Note que este código 12 é praticamente idêntico ao código 09, com exceção do fato de que este código 12, não conta com o tratador de eventos, que está presente no código 09 visto neste artigo. Entretanto, apesar desta numeração mostrada aqui no artigo. No anexo, o tal código 09 estará com o nome de CODE 03.MQ5 devendo assim o código presente no arquivo de cabeçalho, ser o mesmo visto no código 04 deste artigo. Caso você queira ver as coisas ocorrendo, visando utilizar este código 12, deverá mudar o conteúdo do arquivo DEFINES.MQH, para o que é visto logo abaixo.
1. //+------------------------------------------------------------------+
2. #define def_Resource "Code 06.ex5"
3. //+------------------------------------------------------------------+
4. #define def_ShortName "Test 123."
5. //+------------------------------------------------------------------+

Código 13

Com isto o compilador irá mudar do CODE 03.MQ5, para o CODE 06.MQ5, que é justamente o tal código 12 visto a pouco. Isto servirá para que você consiga estudar com mais calma, cada detalhe a respeito de como remover indicadores de um gráfico. Quando isto se tornar necessário ser feito. E como os resultados da execução são exatamente iguais. Não vejo motivos para demonstrar novamente isto aqui.


Considerações finais

Este artigo, assim como os códigos presentes no anexo, deverão ser estudados com calma e bastante atenção. Isto por que, cada detalhe mostrado aqui, faz toda a diferença na qualidade, e no tipo de resultado que você irá conseguir gerar em códigos futuros. É importante ressaltar que todo o conteúdo mostrado aqui, visa única e exclusivamente demonstrar a melhor forma possível de evitar ficar testando de maneira desnecessária a presença ou não de certos objetos, indicadores ou elementos no gráfico. Podemos fazer com que o MetaTrader 5 passa cada vez mais a nos auxiliar neste tipo de tarefa, de forma que nosso real trabalho, será apenas o de tratar da melhor forma possível os dados que estão sendo repassados pelo MetaTrader 5 para nossa aplicação.

Existe outra maneira de efetuar este mesmo tipo de atividade. Porém, por ser bem mais trabalhosa, porque precisamos manter uma lista de propriedades e objetos de maneira a repor as informações quando tais objetos sejam removidos, será possivelmente demonstrada em outro artigo futuro.

De qualquer maneira, agora você já sabe que nem tudo realmente precisa ser programado como muitos imaginam. Se entendermos como o MetaTrader 5 de fato trabalha, podemos criar aplicações de forma muito mais rápida e com muito menos risco de que elas falhem. Justamente por que nosso código conterá apenas aquilo que realmente é necessário.

Arquivo MQ5 Descrição
Indicators\Code 01  Demonstração básica
Indicators\Code 02  Demonstração básica
Indicators\Code 03  Demonstração básica
Indicators\Code 04  Demonstração básica
Indicators\Code 05  Demonstração básica
Indicators\Code 06  Demonstração básica
Arquivos anexados |
Anexo.zip (5.51 KB)
Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização de Código (III): Módulo de Comunicação Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização de Código (III): Módulo de Comunicação
Junte-se a nós para uma discussão aprofundada sobre os mais recentes avanços no design de interfaces em MQL5 enquanto apresentamos o Painel de Comunicações redesenhado e continuamos nossa série sobre a construção do Novo Painel de Administração utilizando princípios de modularização. Desenvolveremos a classe CommunicationsDialog passo a passo, explicando detalhadamente como herdá-la da classe Dialog. Além disso, utilizaremos arrays e a classe ListView em nosso desenvolvimento. Obtenha insights práticos para elevar suas habilidades em desenvolvimento MQL5 — leia o artigo e participe da discussão na seção de comentários!
Rede neural na prática: Perceptron Rede neural na prática: Perceptron
Este artigo apresenta o perceptron como base de uma rede neural e detalha sua implementação em MQL5. Explicamos funções de ativação e suas derivadas, a distinção entre forward e backpropagation e o uso de custo por mínimo quadrado e por gradiente. Você aprenderá a treinar pesos e viés, validar com scripts de teste e ajustar a taxa de aprendizagem para obter convergência estável.
Ciência de Dados e ML (Parte 34): Decomposição de séries temporais, desmembrando o mercado de ações até o núcleo Ciência de Dados e ML (Parte 34): Decomposição de séries temporais, desmembrando o mercado de ações até o núcleo
Em um mundo repleto de dados ruidosos e imprevisíveis, identificar padrões significativos pode ser desafiador. Neste artigo, exploraremos a decomposição sazonal, uma poderosa técnica analítica que ajuda a separar os dados em seus principais componentes: tendência, padrões sazonais e ruído. Ao decompor os dados dessa forma, podemos revelar insights ocultos e trabalhar com informações mais limpas e interpretáveis.
Automatizando Estratégias de Trading em MQL5 (Parte 11): Desenvolvendo um Sistema de Trading em Grade Multi-Nível Automatizando Estratégias de Trading em MQL5 (Parte 11): Desenvolvendo um Sistema de Trading em Grade Multi-Nível
Neste artigo, desenvolvemos um Expert Advisor de sistema de trading em grade multi-nível usando MQL5, com foco na arquitetura e no design de algoritmo por trás das estratégias de grid trading. Exploramos a implementação de lógica de grade em múltiplas camadas e técnicas de gerenciamento de risco para lidar com diferentes condições de mercado. Por fim, fornecemos explicações detalhadas e dicas práticas para guiá-lo na construção, teste e refinamento do sistema de trading automatizado.