Indicadores múltiplos em um gráfico (Parte 04): Iniciando pelo EA
Daniel Jose | 24 fevereiro, 2022
Introdução
Em artigos anteriores, eu expliquei como criar um indicador com múltiplas sub janela, mas apesar de ser interessante de se fazer, quando usamos um indicador personalizado, a coisa é bem simples de ser feita, mas quando tentamos fazer isto em um programa EA, a coisa toda começa a se tornar um pouco mais complexa, já que não temos os mesmos meios que temos em um indicador personalizado, neste ponto a programação se torna necessária, e saber criar o código adequado para se ter uma sub janela é primordial, apesar de não ser uma tarefa tão trivial, já que saber colocar uma sub janela em um EA não envolve muita codificação, mas apenas um certo conhecimento de como o MQL5 funciona.
Planejamento
Já temos nosso indicador personalizado funcionado, ou seja nossa classe objeto já é funcional, e sendo ele uma classe objeto podemos transportá-lo facilmente para outros modelos, mas simplesmente declarar e tentar usar a classe em nosso EA não fará as coisas funcionarem da mesma forma que fizemos em nosso indicador personalizado, e o motivo é que não contamos com a possibilidade de uma sub janela em nosso EA. Mas ai você pensa e se eu utilizar o indicador personalizado que já se encontra compilado e funcionando e chamá-lo de dentro da EA via comando iCustom ?!?! Poderia dar certo ... Bem na verdade até daria certo, desde que não fosse necessário uma sub janela, e o comando ficaria assim:
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ input string user01 = ""; //Indicadores a usar input string user02 = ""; //Ativos a acompanhar //+------------------------------------------------------------------+ int OnInit() { int m_handleSub; //... Código do EA ... if ((m_handleSub = iCustom(NULL, 0, "Chart In SubWindows\\Chart In SubWindow.ex5", user01, user02)) == INVALID_HANDLE) return INIT_FAILED; if (!ChartIndicatorAdd(ChartID(), 0, m_handleSub)) return INIT_FAILED; //... Código do EA ... ChartRedraw(); return(INIT_SUCCEEDED); } //...Resto do codigo do EA ...
Este fragmento singelo de código é sim capaz de carregar nosso indicador personalizado, mas no entanto ele não irá funcionar corretamente, pois não temos a presença de uma sub janela, neste caso quando o código for executado no EA, ele irá aplicar o nosso indicador diretamente na janela principal, ou seja nosso gráfico será ocultado pelos templates carregados pelo indicador, e definitivamente não é isto que estamos querendo.
Então nosso real e principal problema é criar uma sub janela que possa ser utilizada para assim podermos usar o nosso indicador já funcional. Mas por que criar uma sub janela para logo depois executar o nosso indicador ?!?! Isto não faz sentido, é melhor adicionarmos as funcionalidades diretamente no nosso EA e assim superar qualquer limitação que possa vim a acontecer.
Pensando nisto temos então algumas tarefas para serem feitas:
Tarefa | Objetivo |
---|---|
1 => Criar um indicador de uso geral, ou seja genérico | Permitir criar e usar o comando iCustom sem poluir em nada o gráfico |
2 => Incluir de alguma forma este indicador ao EA | Isto irá permitir transportar o EA com todas as funcionalidades sem problemas |
3 => Criar uma classe objeto generalista para sub janelas | Permitir adicionar sub janelas via nosso EA |
4 => Fazer nossa classe C_TemplateChart se ligar a classe de janelas | Isto irá permitir controlar o conteúdo da sub janelas sem mudar em nada o código já funcional. |
Apesar de parecer trabalhoso, as questões são bem simples de serem resolvidas. Então vamos resolver cada um dos pontos.
Implementação: Criar um indicador de uso geral
Esta parte pode ser resolvida criando um código totalmente limpo, porém funcional de um indicador personalizado. No caso o código ficaria assim:
#property copyright "Daniel Jose" #property version "1.00" #property description "Este arquivo serve apenas como Suporte ao Indicador em SubWin" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ int OnInit() { return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
Somente isto e mais nada. Vamos salvar este arquivo como SubSupport.mq5, só que ele não ficará junto com outros indicadores, transfira ele para o diretório RESOURCE do nosso EA, então a estrutura de arquivos ficaria conforme a imagem abaixo:
Implementação: Incluir o indicador genérico no EA
Para se conseguir fazer isto teremos que adicionar o seguinte código no inicio do nosso EA.
//+------------------------------------------------------------------+ #define def_Resource "Resources\\SubSupport.ex5" //+------------------------------------------------------------------+ #resource def_Resource //+------------------------------------------------------------------+
Isto irá incorporar o código compilado do indicador genérico ao nosso EA, uma vez feito isto o indicador genérico poderá ter o arquivo .ex5 deletado, já que ele não será mais necessário. Agora atenção a um fato, caso no momento de compilar o código do EA o executável SubSupport.ex5 não seja encontrado, o compilador irá automaticamente compilar o código do nosso indicador genérico SubSupport.mq5 e adicionar este executável recém compilado ao nosso EA, ou seja se você modificar por qualquer razão o arquivo SubSupport.mq5 e desejar adicionar as mudanças ao EA, delete o arquivo SubSupport.ex5, caso contrário as mudanças não serão adicionadas.
É muito importante se ter este detalhe em mente, pois em alguns casos é necessário saber como adicionar as novas mudanças a um recurso.
Bem, o indicador genérico já esta fazendo parte do nosso EA, passemos então a próxima tarefa.
Implementação: Criar uma classe objeto de sub janelas
Esta parte é igualmente simples, mas no entanto temos que decidir algumas coisas antes de sair codificando, e estas são o seguinte: Quais funções realmente precisaremos ter nesta classe ?!?! Eu decidi usar as seguintes inicialmente:
Função | Descrição |
---|---|
Init | Criar uma sub janela no gráfico via código do EA |
Close | Finaliza uma sub janela no gráfico via código do EA |
Esta funções não fará testes, estou desta forma imaginando que elas serão chamadas apenas uma única vez durante todo o tempo de vida do EA. Mas já que nosso EA esta crescendo é bom já pensar em torná-lo ainda mais prático para o futuro, então vamos acionar uma nova classe objeto a ele será chamada C_Terminal, esta classe irá dar suporte a diversas coisas ligadas ao terminal gráfico, mas não se preocupem com isto por enquanto, vamos então ver a ultima tarefa, já que não tem como implementar uma solução de forma parcial.
Implementação: Herdando a classe C_TemplateChart
Quanto eu decidi criar as coisas usando OOP ( Programação Orientada a Objetos ) fiz isto por já saber que existem vantagens muito grandes em se usar tal abordagem, entre elas está a segurança e a herança, apesar de termos também o polimorfismo, mas este iremos usar mais para frente quando formos criar um sistema de ordens cruzadas, mas aqui vamos usar uma das boas coisas que a OOP nos traz, a herança. Pois bem, a nossa classe C_TemplateChart já se encontra totalmente funcional, e vendo isto, não queremos ter o trabalho de reprogramar tudo de novo, ou correr o risco de adicionar códigos na classe, e estes códigos impedirem a classe de ser usada em outros locais. Solução usamos a herança e assim adicionamos novos códigos ou funcionalidades sem mudar em nada o código original.
Usar a herança nos traz diversos benefícios, entre eles temos: O código já testado permanece testado; a complexidade cresce sem um igual crescimento na quantidade de código; apenas novas funcionalidades de fato precisam se testadas; o que não muda simplesmente é herdado garantindo a estabilidade, enfim, a coisa vai melhorando com o mínimo de esforço, mas com o máximo de segurança, para entender veja o esquema abaixo.
A classe avô é a classe mais básica, onde temos um nível de manipulação dos dados em menor escala, mas quando a classe pai herda as coisas da classe avô, todas as coisas declaradas como publicas na classe avô poderão ser vistas e usadas pela classe pai, mas também poderemos fazer adições de novas coisas a classe pai, mas isto em nada irá afetar o que foi herdado e mantido durante a herança, mas se a classe pai já esta concluída e funcionado e desejamos expandir as coisas ainda mais sem mudar em nada as classes mais abaixo, então criamos uma classe filho e esta agora terá todas as funcionalidades das classes anteriores, mas podemos mudar a forma como as coisas funcionam, e isto é a parte interessante da herança, e ao fazer estas mudanças as demais classes não serão afetadas. No entanto existe uma limitação aqui, diferente do C++ que permite heranças múltipla, ou seja, o filho irá herdar características tanto do lado paterno quanto do materno, no MQL5 isto não é possível, então a estrutura fica sempre um pouco defasada, mas ainda assim você consegue tirar algum proveito da herança. Um exemplo de herança múltipla pode ser visto abaixo:
Bem então como fazemos isto em MQL5 ? Como declaramos a herança e assim possamos tirar proveito disto ?!?! A forma mais clara de entender é lendo este conteúdo Programação Orientada a Objetos ( OOP ) mas aqui iremos direto ao ponto. A herança se dará usando as seguintes linhas:
#include "C_TemplateChart.mqh" //+------------------------------------------------------------------+ class C_SubWindow : public C_TemplateChart { // ... Código da classe };
Vejam que a classe C_SubWindow irá herdar de forma publica a classe C_TemplateChart, então a partir deste momento poderemos usar a classe C_SubWindow para acessar as funcionalidades da classe C_TemplateChart, ou seja herdamos a classe C_TemplateChart e podemos modificá-la ou incrementá-la sem corrermos riscos de estragar o código já testado.
No fragmento de código acima, eu destaquei uma coisa, notem que esta entre aspas ( " ) e não como é de costume usar ( < > ) e por que eu fiz isto ?!?! Como a linguagem C++, o MQL5 tem algumas coisas bem interessantes, porém que confundem quem esta começando a aprender a arte da programação, quando colocamos um arquivo de cabeçario entre os sinais de maior e menor ( < > ) estamos nos referindo a um caminho absoluto, ou seja o compilador irá seguir exatamente o caminho que indicarmos a ele, mas quando usamos aspas como foi feito, o compilador irá usar um caminho relativo, ou para você entender melhor, ele irá primeiro começar no diretório atual onde o arquivo de trabalho se encontra. Isto talvez soe estranho, mas existem casos em que teremos o mesmo nome para arquivos cujo conteúdo é diferente, e eles estarão em diretórios diferentes, mas no entanto queremos nos referir ao diretório atual, então usamos as aspas para fazer isto.
As duas funções que planejamos usar anteriormente, INIT e CLOSE podem ser vista abaixo:
//+------------------------------------------------------------------+ bool Init(void) { if (m_handleSub != INVALID_HANDLE) return true; if ((m_handleSub = iCustom(NULL, 0, "::" + def_Resource)) == INVALID_HANDLE) return false; m_IdSub = (int) ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL); if (!ChartIndicatorAdd(Terminal.Get_ID(), m_IdSub, m_handleSub)) return false; return true; } //+------------------------------------------------------------------+ void Close(void) { ClearTemplateChart(); if (m_handleSub == INVALID_HANDLE) return; IndicatorRelease(m_IdSub); ChartIndicatorDelete(Terminal.Get_ID(), m_IdSub, ChartIndicatorName(Terminal.Get_ID(), m_IdSub, 0)); ChartRedraw(); m_handleSub = INVALID_HANDLE; } //+------------------------------------------------------------------+
Vejam que o código é super simples e curto, mas tem algo que precisamos tomar cuidado, observem a parte destacada do mesmo. É preciso tomar cuidado para que não se cometa o erro no momento de adicionar esta parte, pois se você não deixar exatamente assim, o executável SubSupport.ex5 que pedimos para adicionar ao EA não será visto dentro do EA, e sim de fora do EA. Para entender leia sobre Resources, mas basicamente é o seguinte se você usar ( :: ) isto irá indicar que o EA deverá usar o recurso interno presente dentro dele, mas se apenas indicarmos o nome do recurso o EA irá procurar ele dentro do diretório MQL5, desta forma se o arquivo não existir no local indicado a função irá falhar, mesmo que o arquivo tenha sido adicionado como um recurso do EA.
Então uma vez carregado o recurso, verificamos o numero de sub janelas presentes, e adicionamos um indicador nesta sub janela.
O que este código de fato esta fazendo pode ser visto abaixo:
input string user01 = ""; //Indicadores a usar input string user02 = ""; //Ativos a acompanhar //+------------------------------------------------------------------+ int OnInit() { int m_handleSub; //... if ((m_handleSub = iCustom(NULL, 0, "Chart In SubWindows\\Chart In SubWindow.ex5", user01, user02)) == INVALID_HANDLE) return INIT_FAILED; if (!ChartIndicatorAdd(ChartID(), (int) ChartGetInteger(ChartID(), CHART_WINDOWS_TOTAL), m_handleSub)) return INIT_FAILED; //... ChartRedraw(); return(INIT_SUCCEEDED); } //...Resto do código do EA ...
Ambos os códigos irão funcionar de forma idêntica, mas a versão da classe objeto nos permitirá agregar mais coisas no decorrer do tempo, já a versão mostrada acima, é uma versão já consolidada e não irá mudar, mas ambos executaram a mesma coisa, criar uma sub janela a partir do EA e colocar nesta sub janela todo o indicador personalizado criado anteriormente. Notem que o código sofreu apenas e tão somente uma única modificação do código visto lá no inicio do artigo e que ela esta em destaque.
Conclusão
É muito interessante e curioso a forma como decidimos percorrer um caminho para alcançar nossos objetivos, várias vezes somos confrontados e imaginamos ser difícil conseguir atingir os objetivos, mas com um pouco de paciência e dedicação conseguimos superar obstáculos que pareciam inicialmente intransponíveis. Neste artigo demostrei como você pode ampliar as funcionalidade de uma classe sem de fato mexer nela usando para isto a herança, e ao mesmo tempo mostrei como você pode adicionar indicadores ao seus gráficos, de forma que os mesmo funcionem conforme já foi testado. Adicionamos um programa ex5 dentro de nosso EA e usamos ele sem precisar transportar o ex5 original, bastando carregar o EA.
O arquivo anexado contém todas as melhorias desenvolvidas até o presente momento, mas em breve teremos ainda mais coisas interessantes neste código. 😁👍