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

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

MetaTrader 5Exemplos |
21 0
CODE X
CODE X

Introdução

No artigo anterior Do básico ao intermediário: Objetos e sub janelas (II), vimos como poderíamos lidar de maneira o mais simples e prática possível com a questão de algum objeto colocado por nosso código no gráfico, fosse removido. Seja de forma intencional, seja por erro do operador ou usuário. No entanto, aquela forma de tratar este tipo de situação, pode acabar gerando um certo desagrado por parte de alguns usuários. Justamente por conta de que para ele, o fato de remover algum objeto do gráfico, não deveria fazer com que toda a aplicação, fosse também removida.

Como existe uma segunda forma de lidar com remoção acidental de objetos. Isto considerando que o usuário, tenha removido algo sem se dar conta, da real importância, deste ou daquele objeto. Deixei para tratar deste tema neste artigo. Então aqui veremos como repor um objeto que foi acidentalmente removido do gráfico. Lembrando que o objetivo aqui, será voltado mostrar como isto poderia ser conseguido. Podendo inclusive, os conceitos que serão mostrados, serem aplicados em outros tipos de situação igualmente parecida. Então vamos ao que interessa.


Objetos e sub janelas (III)

Apesar do foco aqui, em tese ser o tratamento da integridade de uma sub janela e de objetos colocados nela. O que será visto, se estende muito além deste tópico. Podendo inclusive ser utilizado, quando você não estiver fazendo de fato uso de sub janelas. Mas apenas querendo que um ou outro objeto, permaneça no gráfico, enquanto a aplicação continuar presente ali. Por este motivo, vamos começar com algo mais simples, e depois estenderemos o conceito, para um caso onde precisarmos de sub janelas.

Muito bem, até no artigo anterior, criamos um tipo de conceito voltado a lhe ajudar a criar aplicações bem complexas com um mínimo de esforço. Isto por que, aqui no MetaTrader 5, indicadores são a base de toda uma gama de coisas que no final culmina no que muitos conhecem como Expert Advisores. Ainda não começamos a falar dos tais Expert Advisores, justamente por conta de que sem os indicadores, tais códigos, que seriam os Expert Advisores, não servem para muito coisa. No entanto, tendo uma base bem solida e construída de maneira adequada e correta e entendendo o que podemos ou não fazer dentro das possibilidades dos indicadores. Construir e projetar um Expert Advisor, se torna uma das tarefas mais simples e fáceis de serem feitas.

Porém, sem que esta base tenha sido construída antes. Qualquer coisa ligada aos Expert Advisores se tornará confusa e muito sem sentido. Apesar de você poder achar que tudo isto é pura perda de tempo, entender como lidar com situações diferentes, é mais importante do que, sair criando códigos a esmo. E no final, você acabará em uma situação na qual não conseguirá resolver, justamente por não ter paciência, ou entendimento sobre como planejar o que pretende implementar.

Ok, com base no que já foi visto, temos a seguinte situação: conseguimos criar um indicador, que pode ou não ser usado em uma sub janela, apenas pelo simples fato de o integrarmos a um segundo indicador. Sendo este primeiro, um indicador que antes era o principal, passando a ser um indicador auxiliar. Portanto, não precisamos necessariamente fazer todo o trabalho de uma só fez. Podemos dividir ele em pequenas etapas, que são mais simples e fáceis de lidar, e somente depois unir tudo, em um bloco maior e mais complexo.

Com base neste princípio, podemos pressupor a seguinte condição: O código visto logo abaixo, pode ser perfeitamente analisado, implementado conforme necessitamos, e depois integrado a um sistema bem maior.

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.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
18.    gl_MiniChart = new C_MiniChart("Mini Chart");
19. 
20.    return INIT_SUCCEEDED;
21.   };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24.   {
25.    return rates_total;
26.   };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29.   {
30.    switch(id)
31.      {
32.       case CHARTEVENT_OBJECT_DELETE:
33.          if(gl_MiniChart == sparam)
34.            {
35.             ChartIndicatorDelete(0, ChartWindowFind(0, def_ShortName), def_ShortName);
36.             OnDeinit(REASON_INITFAILED);
37.            }
38.          break;
39.      }
40.   };
41. //+------------------------------------------------------------------+
42. void OnDeinit(const int reason)
43.   {
44.    delete gl_MiniChart;
45.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
46.   };
47. //+------------------------------------------------------------------+
48. //+------------------------------------------------------------------+
49. 

Código 01

Se você executar este código 01, e remover o objeto que ele criou. Irá perceber que o próprio código também será removido do gráfico. Isto foi visto e explicado no artigo anterior. Portanto, não vejo necessidade de entrar em mais detalhes de como este código 01 funciona. No entanto, aqui vamos tratar esta mesma situação de maneira diferente. Ou seja, ao invés de simplesmente remover o próprio indicador, ou outro código qualquer do gráfico, em que ele se encontra. Iremos fazer com que o objeto removido, volte a ser apresentado. Criando assim, uma mudança completa e total de paradigma, de como as coisas podem ser feitas.

Para começar, quero que você entenda o seguinte: quanto mais você desejar que as coisas voltem a ser como estavam antes de tudo ser desmontado. Maior será seu trabalho, para garantir que todas as informações necessárias estejam de fato disponíveis. Isto quando for necessário recriar algo que foi destruído. Em alguns casos, podemos fazer uso de simples estruturas de dados. Enquanto em outros casos, será necessário utilizar um pequeno banco de dados. Felizmente, para ambas as situações, o MQL5 tem a capacidade e todos os mecanismos que precisamos para efetuar as devidas análises e correções. A única questão será: Quanto esforço você está disposto a colocar na solução do problema?

Bem, pensando desta forma, precisaremos começar de algum ponto. E este ponto, é o local onde o menor esforço será utilizado. Quando digo menor esforço, estou me referindo ao fato de que, toda e qualquer modificação ou personalização que um usuário, ou operador tenha feito, será completamente perdida. Forçando assim com que todas as modificações ou personalizações precisarão ser refeitas pelo usuário.

Está tomada de decisão, apesar de parecer um tanto quanto drástica, ainda assim é melhor que a alternativa vista no artigo anterior. Ou que está presente neste código 01. Então para conseguir com que este código 01, ao invés de ser removido do gráfico, caso o objeto que ele criou, seja removido, precisaremos modificar ele de maneira bem sutil. Porém, que mudará completamente o comportamento do código. Esta modificação é vista 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. void Initilize(void)
15.   {
16.    gl_MiniChart = new C_MiniChart("Mini Chart");
17.    ChartRedraw();
18.   }
19. //+------------------------------------------------------------------+
20. int OnInit(void)
21.   {
22.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
23.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
24.    Initilize();
25. 
26.    return INIT_SUCCEEDED;
27.   };
28. //+------------------------------------------------------------------+
29. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
30.   {
31.    return rates_total;
32.   };
33. //+------------------------------------------------------------------+
34. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
35.   {
36.    switch(id)
37.      {
38.       case CHARTEVENT_OBJECT_DELETE:
39.          if(gl_MiniChart == sparam)
40.             Initilize();
41.          break;
42.      }
43.   };
44. //+------------------------------------------------------------------+
45. void OnDeinit(const int reason)
46.   {
47.    delete gl_MiniChart;
48.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
49.   };
50. //+------------------------------------------------------------------+
51. //+------------------------------------------------------------------+
52. 

Código 02

Este código 02 corrige parte do nosso problema. E admitindo que você, vem estudando os artigos desta sequência, não creio que você tenha dificuldades em entender o que estamos fazendo aqui. Apenas olhando este código 02 e o analisando, você logo percebe o objetivo a ser alcançado. Mas este código 02, apesar de funcionar, contém algumas pequenas falhas. Para entender isto, preciso que você primeiro veja, como grande parte dos testes são feitos. A maioria dos programadores iniciantes, fazem simplesmente o que é visto na animação logo abaixo.

Animação 01

O que não está errado. Porém, é considerado um teste incompleto, e pode gerar conclusões erradas. No entanto, o objetivo seria justamente testar se o objeto está ou não sendo recriado. E como deu para ver, sim, ele está sendo recriado. Assim você considera que o resultado é satisfatório e segue em frente. Entretanto, ao procurar observar mais atentamente, tudo que está ocorrendo, você se depara com o que podemos ver na animação logo abaixo.


Animação 02

Perceba o seguinte: O que está sendo mostrado aqui na animação 02, somente está ocorrendo, por conta que o terminal está aberto. Muitas vezes, grande parte dos iniciantes não mantém o terminal aberto. Desta forma, diversos avisos, ou mesmo algum alerta pode passar desapercebido por um longo período. E quando você vai tentar resolver, já não conseguirá, justamente por conta de que o código já pode estar complexo demais para que todas as falhas sejam encontradas. Mas como esta animação 02, ficou um tanto quanto rápida. Vamos ver a parte que nos interessa em destaque na imagem logo abaixo.


Imagem 01

Mas como assim? De onde esta falha surgiu? Não vejo nada de errado no código 02. Já que basicamente apenas fizemos uma simples alteração no que seria o código 01. Não entendo por que de esta falha ter sido apresentada. Bem, meu caro leitor, está falha demonstra um dos motivos pelo qual o MQL5, NÃO CONTER OPERAÇÕES COM PONTEIROS. Ponteiros são coisas maravilhosas, mas quando mal compreendidos, se tornam uma pedra em nosso sapato. Nestes códigos vistos acima, temos um ponteiro sendo declarado na linha doze. Porém, ele somente é inicializado quando usamos o operador new. Agora preste atenção, se, e somente si, a inicialização acontecer uma única vez, que seria no momento em que o tratador OnInit fosse executado. A tal mensagem vista, em destaque, na imagem 01, NÃO SERIA APRESENTADA.

Porém, toda via e, entretanto, no código 02, o ponteiro poderá ser inicializado mais de uma vez. Hã! Como assim? Sempre achei que uma variável não poderia fazer uso de duas posições diferentes na memória. E de fato, elas não podem fazer isto. O problema é que quando a linha vinte e quatro é executada, neste código 02, temos a primeira inicialização do ponteiro. Porém, se o usuário deletar o objeto do gráfico, teremos uma segunda inicialização do mesmo ponteiro. Isto por conta da linha quarenta. Justamente por conta desta segunda inicialização, alguma coisa não será de fato removida da memória, quando a linha quarenta e sete for executada. Por conta disto, que a tal mensagem vista em destaque na imagem 01 nos é apresentada. Demonstrando que algo está errado aqui.

Mas como podemos contornar este tipo de problema? Basicamente, existem duas soluções. A primeira seria a de não utilizar ponteiros em seus códigos. Porém, isto limita bastante as coisas, podendo inclusive tornar seu código menos claro, conforme ele for ficando cada vez maior. A segunda solução é garantir que antes de ser inicializado, um ponteiro esteja livre de qualquer informação anterior. Como está, a meu ver, esta seria a melhor das soluções. Assim adotaremos ela como resposta a fim de resolver a mensagem em destaque na imagem 01. No entanto, existe um segundo problema, neste código 02. Este segundo problema, de certa forma é menos prejudicial ao sistema. Porém, dependendo do que esteja sendo implementado, pode ser que ele nos cause dores de cabeça. E o tal problema está dentro do procedimento OnDeinit. Agora pare e pense um pouco. Na linha quarenta e sete, deste código 02, estamos removendo o objeto do gráfico. Certo? Até aí, nada de errado.

No entanto, perceba que ao fazermos esta remoção, ainda estamos pedindo para que o MetaTrader 5, nos informe, caso algum objeto seja removido do gráfico. Por conta disto, o MetaTrader 5 irá colocar na fila de eventos, um que indique que algum objeto foi removido do gráfico. Portanto, se o código demorar um pouco mais para sair, teremos a execução da linha quarenta. Forçando assim que o objeto, destruído, pelo fato do código está sendo encerrado, volte a ser recriado. O que em muitas situações pode não ocorrer, mas em outras poderá acontecer. Então precisamos corrigir isto, antes de as coisas começarem a sair completamente do nosso controle. Feita esta analise, podemos modificar o código 02, para o código 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. void Initilize(void)
15.   {
16.    delete gl_MiniChart;
17.    gl_MiniChart = new C_MiniChart("Mini Chart");
18.    ChartRedraw();
19.   }
20. //+------------------------------------------------------------------+
21. int OnInit(void)
22.   {
23.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
24.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
25.    Initilize();
26. 
27.    return INIT_SUCCEEDED;
28.   };
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31.   {
32.    return rates_total;
33.   };
34. //+------------------------------------------------------------------+
35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
36.   {
37.    switch(id)
38.      {
39.       case CHARTEVENT_OBJECT_DELETE:
40.          if(gl_MiniChart == sparam)
41.             Initilize();
42.          break;
43.      }
44.   };
45. //+------------------------------------------------------------------+
46. void OnDeinit(const int reason)
47.   {
48.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
49.    delete gl_MiniChart;
50.   };
51. //+------------------------------------------------------------------+

Código 03

Estas simples mudanças feitas aqui no código 03, irá garantir que não teremos aqueles problemas mencionados acima. Agora vamos ver como o código lidará com a remoção do objeto do gráfico. Isto pode ser visualizado na animação logo abaixo.


Animação 03

Simplesmente perfeito. Funcionou maravilhosamente bem. Sem nenhuma falha aparente. Agora perceba que mesmo após mover o objeto na tela, o mesmo foi recolocado no ponto inicial. Isto é o tal custo baseado no mínimo de esforço, do qual menciono no início do artigo. Já que ao fazermos isto, decidimos ignorar toda e qualquer configuração, ou personalização que poderia ter sido feita. Forçando assim o usuário a refazer tudo deste o início. Bem, este seria um custo baixo, já que o indicador, continua ativo no gráfico, diferente do que era feito antes. Onde simplesmente removíamos o indicador do gráfico.

De qualquer maneira, podemos melhorar um pouco isto. Tornando a experiência do usuário, ou operador, um tanto quanto menos desagradável. Mas ao decidirmos fazer isto, entramos na questão de que, quanto mais coisas desejarmos recriar, de forma que o usuário não perceba a diferença, antes e após ter removido o objeto do gráfico. Mais esforço precisará ser empregado. Assim sendo, vamos aumentar só um pouco o esforço que será feito. Já que o objetivo aqui, é puramente didático.

Ok, agora vem a parte das decisões a serem tomadas. Como quero deixar as coisas bem simples, e, ao mesmo tempo, lhe dar uma ideia de como fazer uma recriação mais completa. Vamos fazer o seguinte: Iremos armazenar a posição e as dimensões do objeto gráfico. E apesar de fazermos somente isto, você notará que o trabalho necessário para conseguir fazer com que tudo funcione, será bem maior do que o mostrado até aqui. Para começar, precisaremos modificar o código original da classe. O original é visto logo abaixo.

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)
29.          return;
30.       ObjectCreate(0, m_szNameObject = def_ShortName + "#" + szName + "#" + (string)ObjectsTotal(0, -1, -1), OBJ_CHART, m_Sub, 0, 0);
31.       ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
32.       SetInteger(OBJPROP_XDISTANCE, 10);
33.       SetInteger(OBJPROP_YDISTANCE, 0);
34.       SetInteger(OBJPROP_XSIZE, 300);
35.       SetInteger(OBJPROP_YSIZE, indicator_height);
36.       SetInteger(OBJPROP_PERIOD, PERIOD_M15);
37.       SetInteger(OBJPROP_DATE_SCALE, false);
38.       SetInteger(OBJPROP_PRICE_SCALE, false);
39.       SetInteger(OBJPROP_SELECTABLE, true);
40.       SetInteger(OBJPROP_SELECTED, false);
41.      }
42.    //+----------------+
43.                     ~C_MiniChart()
44.      {
45.       ObjectDelete(0, m_szNameObject);
46.       ChartRedraw();
47.      }
48.    //+----------------+
49.    bool              operator==(const string szArg) { return (szArg == m_szNameObject); }
50.    //+----------------+
51.   };
52. //+------------------------------------------------------------------+

Código 04

Muito bem, como quero deixar você experimentar isto, sem necessitar digitar estes códigos. No anexo, a versão modificada estará com outro nome. Apenas para facilitar a identificação e também lhe permitir testar as coisas de modo local. Legal, então vamos começar criando um mecanismo para armazenar o sistema de posicionamento do objeto. Depois você entenderá, porque este mecanismo precisa ser pensado uma coisa separada do restante do código. Desta forma, modificando o código da classe visto acima, temos o que é visto logo abaixo.

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.    struct  st_Memory
14.      {
15.       short          x, y, w, h;
16.       bool           selectable;
17.      } m_Memory;
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 < (short)(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)
35.          return;
36.       m_Memory.x = 10;
37.       m_Memory.y = 0;
38.       m_Memory.w = 300;
39.       m_Memory.h = indicator_height;
40.       m_Memory.selectable = true;
41.       m_szNameObject = def_ShortName + "#" + szName + "#" + (string)ObjectsTotal(0, -1, -1);
42.       RestoreMemory();
43.       ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
44.       SetInteger(OBJPROP_PERIOD, PERIOD_M15);
45.       SetInteger(OBJPROP_DATE_SCALE, false);
46.       SetInteger(OBJPROP_PRICE_SCALE, false);
47.       SetInteger(OBJPROP_SELECTED, false);
48.      }
49.    //+----------------+
50.                     ~C_MiniChart()
51.      {
52.       ObjectDelete(0, m_szNameObject);
53.       ChartRedraw();
54.      }
55.    //+----------------+
56.    void              RestoreMemory(void)
57.      {
58.       if(ObjectFind(0, m_szNameObject) < 0)
59.          ObjectCreate(0, m_szNameObject, OBJ_CHART, m_Sub, 0, 0);
60.       SetInteger(OBJPROP_XDISTANCE, m_Memory.x);
61.       SetInteger(OBJPROP_YDISTANCE, m_Memory.y);
62.       SetInteger(OBJPROP_XSIZE, m_Memory.w);
63.       SetInteger(OBJPROP_YSIZE, m_Memory.h);
64.       SetInteger(OBJPROP_SELECTABLE, m_Memory.selectable);
65.      }
66.    //+----------------+
67.    void              StoreMemory(void)
68.      {
69.       m_Memory.x = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_XDISTANCE);
70.       m_Memory.y = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_YDISTANCE);
71.       m_Memory.w = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_XSIZE);
72.       m_Memory.h = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_YSIZE);
73.       m_Memory.selectable = (bool) ObjectGetInteger(0, m_szNameObject, OBJPROP_SELECTABLE);
74.      }
75.    //+----------------+
76.    bool              operator==(const string szArg) { return (szArg == m_szNameObject); }
77.    //+----------------+
78.   };
79. //+------------------------------------------------------------------+

Código 05

Certo, agora temos um código realmente muito doido em nossas mãos. Isto por que, este código 05, tem o mesmo funcionamento que o código 04. No entanto, ele tem um pequeno bloco de memória interna sendo criado para propósito local. E isto faz com que este código 05 consiga reestabelecer as configurações que porventura forem destruídas.Isto caso o usuário remova o objeto do gráfico, e o indicador ainda permaneça ativo no gráfico. Se esta condição específica ocorrer, o indicador irá conseguir, usando dados presentes na memória do objeto, recriar o objeto dentro daquelas possibilidades que foram armazenadas de antemão.

Então, se você ficou interessado nisto que acabei de dizer. Preste atenção para entender como este mecanismo aqui funciona. Pois apesar de ser muito simples, ele exige um certo cuidado, no momento em que você for utilizar ele em algum código.

Como mencionei, precisamos de uma memória a parte. Esta está sendo criada na linha treze deste código 05. Mas você pode estar se perguntando: Bem, esta memória não está a parte. Ela está dentro da classe que cria o objeto. Isto não irá gerar algum tipo de problema depois? Não se você entender como as coisas de fato ocorrem quando o código é executado. Se você, já achar que conhece e entende o que está fazendo, muito provavelmente você fará besteira. Mas se realmente entender o que será explicado, tudo funcionará perfeitamente bem.

Com esta estrutura de memória tendo sido criada, precisaremos inicializar a mesma, e isto é feito entre da linha trinta e cinco até a linha trinta e nove. Note que tais linhas estão dentro do constructor da classe. Fazer isto neste momento é muito importante. Da mesma forma que também é importante que o nome do objeto seja definido neste constructor. Agora perceba que NÃO ESTAMOS CRIANDO O OBJETO NO CONSTRUCTOR. Isto por que, se a criação fosse feita aqui, teríamos dificuldades em saber, quando e onde o objeto realmente foi criado.

Desta forma, uma vez que temos a estrutura de memória inicializada, podemos chamar o procedimento que irá de fato criar o objeto, ou melhor dizendo, irá recolocar o objeto no gráfico. Isto será executado pelo procedimento implementado na linha cinquenta e cinco. Este procedimento, faz exatamente o que o constructor do código 04 fazia. Com uma única exceção, que no caso é a linha cinquenta e sete, onde verificamos se o objeto que queremos recolocar no gráfico se encontra ou não presente na lista de objetos. Mas por que fazer este teste? Bem, o motivo é que, dependendo das circunstâncias, pode ser que algo tenha ocorrido e que algum objeto tenha assumido o posto do que foi removido. Mas por hora, não se preocupe com este fato, já que isto será assunto para outro artigo futuro. Então vamos continuar a entender o que está acontecendo.

Uma vez que os dados tenham sido restaurados, durante a execução do constructor da classe, o objeto já estará funcional e terá todas as configurações vistas anteriormente. Mas antes de passarmos para o código principal, vamos ver um último procedimento que foi adicionado a classe. Este é visto na linha sessenta e cinco. O objetivo deste procedimento é muito simples. Ele irá capturar as informações que desejamos restaurar e as armazenar na memória local, como forma de preservar, mesmo que de forma momentânea as configurações do objeto. Este é o código mais básico e simples que podemos implementar visando restaurar um objeto deletado pelo usuário. Agora vamos ver como o código principal precisou ser modificado, pois de fato, ele não é igual ao código 03. Mas se parece bastante, como você pode observar logo abaixo. Já que o código é visto na íntegra.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #include "include\C_MiniChart_2.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. void Initilize(void)
15.   {
16.    if(gl_MiniChart == NULL)
17.       gl_MiniChart = new C_MiniChart("Mini Chart");
18.    else(*gl_MiniChart).RestoreMemory();
19.    ChartRedraw();
20.   }
21. //+------------------------------------------------------------------+
22. int OnInit(void)
23.   {
24.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
25.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true);
26.    gl_MiniChart = NULL;
27.    Initilize();
28. 
29.    return INIT_SUCCEEDED;
30.   };
31. //+------------------------------------------------------------------+
32. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
33.   {
34.    return rates_total;
35.   };
36. //+------------------------------------------------------------------+
37. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
38.   {
39.    switch(id)
40.      {
41.       case CHARTEVENT_OBJECT_DELETE:
42.          if(gl_MiniChart == sparam)
43.             Initilize();
44.          break;
45.       case CHARTEVENT_OBJECT_DRAG:
46.          if(gl_MiniChart == sparam)
47.             (*gl_MiniChart).StoreMemory();
48.          break;
49.      }
50.   };
51. //+------------------------------------------------------------------+
52. void OnDeinit(const int reason)
53.   {
54.    ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false);
55.    delete gl_MiniChart;
56.   };
57. //+------------------------------------------------------------------+

Código 06

Este código 06, é realmente algo muito belo, mas está faltando um detalhe nele. Porém, antes de corrigir este detalhe que está faltando, quero que você olhe, admire este código, pois ele consegue, trazer algo do mundo dos mortos. Na verdade, a coisa volta meio que deformada, mas isto se deve ao fato de que não estamos memorizando todas as propriedades do objeto, mas apenas alguns deles. Por isto, para entender este código 06, você primeiro precisa entender os demais. Incluindo o que foi explicado em outros artigos nesta mesma sequência.

Principalmente o artigo Do básico ao intermediário: Eventos em Objetos (IV), pois neste artigo, explico como trabalhar com eventos simples em objetos igualmente simples. Claro que talvez você precise ver os artigos anteriores. Mas esta decisão deixo a seu critério.

Ok, agora preste muita, mas muita atenção mesmo, pois se você se distrair, vai se perder no meio da explicação. Lembra que no começo do artigo, menciono o fato de usarmos ponteiros para poder trabalhar de uma determinada maneira aqui no MQL5? E que apesar de ponteiros serem algo, a princípio, bem complicado, eles podem ser muito úteis em diferentes situações? Pois bem, esta é uma destas situações. Para você entender, o que se passa aqui no código 06, quero que você procure comparar ele com o código 03. Da mesma forma que era feito no código 03, aqui no código 06, também declaramos o ponteiro na linha doze. No entanto, antes de chamar o procedimento de inicialização, usamos a linha vinte e cinco para previamente inicializar o ponteiro com um valor conhecido.

Detalhe importante: Ponteiros, NÃO PODEM receber valores discretos diretamente. Eles sempre e para todo o sempre, precisam apontar para algum local na memória.

No entanto, apesar deste detalhe, o valor NULL pode sim ser atribuído a um ponteiro, fazendo com que o identifiquemos como sendo um ponteiro inválido. Esta situação é muito, mas muito importante para nós. Isto por que, diferente do que antes era feito no procedimento Initilize, agora NÃO IREMOS destruir o ponteiro, caso o procedimento receba uma segunda chamada. Que sempre ocorrerá quando o objeto for deletado do gráfico. Novamente, é preciso entender os códigos anteriores para entender como este código 06 funciona.

Assim, na linha dezesseis, efetuamos um teste, cujo objetivo é justamente o de descobrir se o ponteiro é valido ou não. Como na primeira chamada ele está sendo declarado como inválido, o teste passará e uma posição da memória será atribuído ao ponteiro, pelo uso do operador new. Agora vem a parte confusa, para muitos. O que acontece quando a linha quarenta e um chamar novamente este procedimento da linha quatorze? Bem, neste caso, o objeto terá sido removido do gráfico. Porém, o ponteiro onde os dados do objeto se encontravam, não terá sido eliminado ainda da memória. Isto somente acontecerá quando o operador delete for executado. Coisa que somente acontece na linha cinquenta e dois.

Agora perceba, que como a linha cinquenta e um, remove a notificação de evento de um objeto sendo removido do gráfico. O MetaTrader 5, não mais irá disparar, um evento que fará com que a linha quarenta e um seja novamente executada. Este tipo de coisa é muito importante ser feita nesta exata ordem. Caso contrário, quando o teste da linha dezesseis voltasse a ser executado, teríamos um ponteiro invalido, e um novo seria criado. Mas como isto não ocorrerá, durante a execução normal do código. Quando a linha quarenta e um executar, o teste da linha dezesseis fará com que a linha dezessete seja executada. E esta linha faz com que os dados do objeto sejam restaurados. Contudo, como o objeto, já não está mais presente no gráfico, a classe irá forçar que um novo objeto, com o mesmo nome seja recriado, como pode ser visto na linha cinquenta e sete do código 05.

Hum, isto pareceu bem legal. Mas e quanto a questão da memória? Até onde consegui perceber, os dados não estão sendo atualizados. Pelo menos não da forma que eu consiga entender, como isto está acontecendo? Bem, meu amigo leitor, por isto é importante que você estude os artigos anteriores. Pois lá, tratamos do evento CHARTEVENT_OBJECT_DRAG. Este evento vem do MetaTrader 5, e pretende, nos dizer que alguma coisa no objeto foi modificado, sendo basicamente a sua posição. Porém, se você ver o artigo que mencionei a pouco, você verá que não apenas a posição, mas também as dimensões do objeto podem ser modificadas. Mas não vou voltar a repetir o que foi dito antes. Sendo assim, por padrão, apenas a posição será modificada. O que acaba resultado no comportamento observável nas animações logo abaixo.


Animação 04

Nesta animação 04, podemos ver o princípio básico sendo aplicado. Note que no momento em que removemos o objeto do gráfico, um novo objeto é criado. Porém, as características e propriedades salvas são restauradas. As demais são substituídas por valores padrão do objeto.

Bem e se modificarmos os valores na caixa de propriedades do objeto. O que acontece? Neste caso, a resposta mais adequada seria: depende. Mas depende do quê? Se você estiver usando o que é visto no código 06 a resposta é: quando as propriedades forem modificadas, e o objeto, NÃO FOR MOVIMENTADO, fazendo assim disparar um evento que será capturado por CHARTEVENT_OBJECT_DRAG. As propriedades modificadas serão perdidas caso o objeto seja deletado. Isto pode ser visto na animação logo abaixo.


Animação 05

Porém, e isto é importante, se após modificar as propriedades, você mover o objeto, as configurações serão salvas, como mostrado na animação logo na sequência.


Animação 06

Hum, interessante. Mas será que não existem meios de modificarmos as propriedades, e não perdermos os novos valores, sem precisar mover o objeto no gráfico? Sim, meu amigo, existe uma forma de fazer isto. Mas antes de mostrar isto, vamos a uma última animação. Que seria justamente a que mostra o que acontece quando a aplicação que mantém o objeto no gráfico é removida. Esta animação é vista abaixo.


Animação 07

Note que em nenhuma das animações foi constatado nenhum tipo de relato, indicando algum problema no código. O que é um ótimo sinal. Então vamos ver como resolver a questão vista na animação 05, onde após modificar as propriedades, elas foram perdidas, justamente por conta de que não foram salvas em memória. Para resolver esta questão, precisamos adicionar uma simples linha de código. E esta visa fazer a captura justamente do evento que o MetaTrader 5 envia, nos notificando que alguma das propriedades do objeto foram modificadas. Como o código praticamente se mantém igual, podemos focar apenas no fragmento que realmente precisa ser modificado. Este é visto logo abaixo.

                                   .
                                   .
                                   .

35. //+------------------------------------------------------------------+
36. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
37.   {
38.    switch(id)
39.      {
40.       case CHARTEVENT_OBJECT_DELETE:
41.          if(gl_MiniChart == sparam)
42.             Initilize();
43.          break;
44.       case CHARTEVENT_OBJECT_CHANGE:
45.       case CHARTEVENT_OBJECT_DRAG:
46.          if(gl_MiniChart == sparam)
47.             (*gl_MiniChart).StoreMemory();
48.          break;
49.      }
50.   };
51. //+------------------------------------------------------------------+ 
                                   .
                                   .
                                   .

Fragmento 01

E é isto, meu caro leitor. Tudo que precisamos fazer, para corrigir o que é visto na animação 05, é adicionar esta linha quarenta e três que podemos ver no fragmento 01. Com isto, quando o MetaTrader 5, enviar uma notificação informando que as propriedades do objeto foram modificas, nosso código conseguirá interceptar esta notificação e garantir que a memória seja atualizada. Assim, mesmo após modificar as propriedades, o objeto seja deletado, mesmo sem ter sido movido, nossa aplicação conseguirá reviver o objeto, exatamente como ele foi salvo. Lembrando que para restabelecer um objeto que seja idêntico ao que existia antes, precisaremos memorizar um número bem maior de propriedades. Mas como aqui quero apenas e tão somente demonstrar como poderíamos estar fazendo isto. Já me dou por satisfeito com este resultado alcançado.


Considerações finais

Apesar de não termos levado o indicador mostrado neste artigo para ser usado em uma sub janela, via integração com algum outro indicador, como foi feito no artigo anterior. O que foi visto e explicado aqui, é e pode ser considerado, um ponto-chave para entender como faremos para resolver a segunda parte de outra questão. Que é justamente o de garantir que o indicador, presente em uma sub janela, e que esteja integrado a outro indicador, não seja removido, simplesmente por conta de que algum dos objetos criados e mantidos por ele, tenha sido removido do gráfico.

Apesar de que, ligar o que foi visto aqui, como o que havia sido visto no artigo anterior, ser algo aparentemente simples e fácil de ser feito. Existe um pequeno macete, por assim dizer, e isto para não termos de fato um comportamento diferente daquele que seria o esperado. Já que uma coisa é remover e repor um objeto no gráfico. E outra completamente diferente, é o de repor um indicador inteiro, que por ventura seja removido do gráfico. Ambas situações exigem uma abordagem um pouco diferente.

Assim, no próximo artigo veremos como podemos fazer para lidar com este tipo de situações. Que apesar de parecem diferentes, tem muita coisa em comum. Porém, sem o devido conhecimento você não conseguirá lidar com ela.

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




Arquivos anexados |
Anexo.zip (5.99 KB)
Desenvolvimento de um sistema personalizado de detecção do regime de mercado em MQL5 (Parte 2): Expert Advisor Desenvolvimento de um sistema personalizado de detecção do regime de mercado em MQL5 (Parte 2): Expert Advisor
Este artigo descreve em detalhes a criação de um EA adaptativo (MarketRegimeEA) usando o detector de regimes da Parte 1. Ele alterna automaticamente estratégias de negociação e parâmetros de risco para mercados de tendência, mercados laterais ou mercados voláteis. O artigo também inclui otimização prática, tratamento das transições e um indicador para vários timeframes.
Otimização por Comunidade de Cientistas - Community of Scientist Optimization (CoSO): Prática Otimização por Comunidade de Cientistas - Community of Scientist Optimization (CoSO): Prática
Continuação do tema de otimização por comunidade científica. O CoSO não deve ser tratado como uma solução pronta, mas como uma plataforma de pesquisa promissora. Com o refinamento adequado, o CoSO pode encontrar seu nicho em tarefas em que a adaptabilidade e a robustez a mudanças sejam importantes, e quando o tempo de processamento não for crítico.
Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): Estratégia de trading pós-notícia Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): Estratégia de trading pós-notícia
Durante o primeiro minuto após a divulgação de notícias econômicas importantes, o risco de erro de avaliação é extremamente alto. Nesse curto intervalo, o movimento do preço pode ser errático e volátil, frequentemente levando ao acionamento de ordens pendentes dos dois lados do mercado. Pouco depois da publicação, geralmente dentro de um minuto, o mercado tende a se estabilizar, retomando ou corrigindo a tendência predominante em patamares mais normais de volatilidade. Nesta seção, examinaremos uma abordagem alternativa para o trading baseado em notícias, a fim de avaliar sua eficácia como um complemento valioso ao conjunto de ferramentas do trader. Continue lendo para acompanhar mais detalhes desta discussão.
Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): estratégia de ordens pendentes para trading baseado em notícias Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): estratégia de ordens pendentes para trading baseado em notícias
Neste artigo, vamos nos concentrar na integração da lógica de execução de ordens baseada em notícias, permitindo que o EA atue, e não apenas informe. Acompanhe-nos enquanto examinamos como implementar a execução automática de operações em MQL5 e transformar o EA "Manchetes de notícias" em um sistema de trading totalmente adaptativo. Os EAs oferecem vantagens significativas aos desenvolvedores de sistemas algorítmicos graças ao amplo conjunto de funções às quais dão suporte. Até agora, nos concentramos na criação de uma ferramenta para apresentar notícias e eventos do calendário, equipada com faixas analíticas integradas usando IA e indicadores técnicos.