English Русский Español Deutsch 日本語
preview
Desenvolvendo um sistema de Replay (Parte 27): Projeto Expert Advisor — Classe C_Mouse (I)

Desenvolvendo um sistema de Replay (Parte 27): Projeto Expert Advisor — Classe C_Mouse (I)

MetaTrader 5Testador | 20 setembro 2023, 10:15
398 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 26): Projeto Expert Advisor (I), expliquei em detalhes como a primeira classe, começou a ser construída. Mas vamos expandir as coisas, de forma a dar alguma serventia a tudo aquilo. Com isto irá nascer a classe C_Mouse. Esta foi pensada de maneira que a programação, seja feita no mais alto nível quanto for possível ser feita. Mas dizer que trabalharemos em alto, ou baixo nível, nada tem haver com questões de colocarmos palavrões ou chavões no meio do código. Longe disto. Trabalhar em alto nível ou de baixo nível, quando se fala em programação, diz o quanto o programa pode ser mais simples ou mais difícil de ser lido por outro programador. Ou seja, quando o código se assemelha com a linguagem natural, dizemos que o código é de alto nível, da mesma forma, quanto menos o código se parece com uma linguagem natural, mas baixo é o nível da programação. Já que nos afastamos da forma como escrevemos naturalmente e vamos nos aproximando da maneira como o processador, ou unidade de processamento irá conseguir entender as instruções.

Desta forma, tentarei sempre fazer com que o código das classes, tenham a maior nível possível, mas ao mesmo tempo evitarei usar ao máximo certo tipos de modelagem. Isto para não dificultar a compreensão do código por parte de pessoas com menos experiência. Pelo menos este será o meu intuito, não sei se irei de fato conseguir.


A Classe C_Mouse. Começando a interação com o usuário

Bem, muito provavelmente o mouse, assim como o teclado, são as duas forma mais comuns de interação entre o usuário e a plataforma. Assim precisamos que esta interação seja ao mesmo tempo, simples e eficiente. Sem que o usuário precise reaprender a fazer as coisas. O código começa com as seguintes linhas:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Terminal.mqh"
//+------------------------------------------------------------------+
#define def_MousePrefixName "MOUSE_"
#define def_NameObjectLineH def_MousePrefixName + "H"
#define def_NameObjectLineV def_MousePrefixName + "TV"
#define def_NameObjectLineT def_MousePrefixName + "TT"
#define def_NameObjectBitMp def_MousePrefixName + "TB"
#define def_NameObjectText  def_MousePrefixName + "TI"
//+------------------------------------------------------------------+
#define def_Fillet      "Resource\\Fillet.bmp"
#resource def_Fillet
//+------------------------------------------------------------------+

Aqui incluímos o arquivo de cabeçalho que contem a classe C_Terminal. Como foi visto no artigo anterior, este arquivo com a classe C_Mouse, estará no mesmo diretório que o arquivo da classe C_Terminal. Assim esta sintaxe pode ser utilizada sem problemas, Aqui definimos um nome para um recurso que será incluído no arquivo executável, de maneira que você poderá transportar o executável, sem de fato precisar transportar o recurso como sendo um arquivo separado. De fato isto é algo bastante interessante em muitos casos. Principalmente quando o recurso, é algo critico e que precisamos garantir que ele esteja presente no momento de sua utilização. Normalmente colocamos o recurso em uma diretório especifico, de maneira que seja fácil ter acesso a ele. Assim ele sempre será compilado junto com o arquivo de cabeçalho. No caso, adicionamos um diretório que é chamado de Resource a pasta onde se encontra o arquivo C_Mouse.mqh. Dentro deste diretório Resource haverá um arquivo chamado Fillet.bmp. Assim se mudarmos a estrutura dos diretórios, mas mantendo esta mesma modelagem, o compilador irá saber exatamente onde encontrar o arquivo Fillet.bmp. Depois que o código estiver compilado, você pode simplesmente carregar o arquivo executável, sem medo de que o recurso não será encontrado. Pois como foi dito acima, ele estará embutido no próprio executável.

Neste ponto, temos primeiramente a definição de um nome. Na verdade um prefixo para outros nomes que iremos definir logo em seguida. O uso de definições é bastante comum, e torna o trabalho de desenvolvimento e manutenção bastante mais simples e prático. Sendo algo muito comum em códigos profissionais. Onde o programador define diversos nomes e coisas a serem utilizados durante todo o código. Normalmente este tipo de coisa costuma ficar em um arquivo, que muitos nomeiam como Defines.mqh, ou algo do tipo. Isto facilita a mudança de alguma definição. Mas aqui, como estas definições somente existirão dentro deste arquivo, não faz sentido as declarar em outro local.

#undef def_MousePrefixName
#undef def_NameObjectLineV
#undef def_NameObjectBitMp
#undef def_NameObjectLineH
#undef def_NameObjectLineT
#undef def_NameObjectText
#undef def_Fillet

Este fragmento mostrado acima, informa o compilador que todos os símbolos e nomes definidos e visíveis por este arquivo C_Mouse.mqh, deverão deixar de serem visíveis a partir deste momento. Naturalmente não devemos de fato, ou não é muito comum, removermos ou modificar, definições feitas em outros arquivos. Por conta disto fazemos a declarações dos nomes no ponto onde eles de fato irão surgir e passarão a ser utilizados. E no final os removemos. Fora isto, não é de boa pratica mudar ou remover definições sem critério algum. Quando precisamos usar uma definição em diversos arquivos, criamos um arquivo apenas para isto.

Vamos começar a ver as primeiras linhas do código da classe. Deste momento em diante é que a coisa realmente começa a ficar interessante. Tudo começa com o seguinte fragmento:

class C_Mouse : public C_Terminal
{

   protected:
      enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
      enum eBtnMouse {eKeyNull = 0x01, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
      struct st_Mouse
      {
         struct st00
         {
            int      X,
                     Y;
            double   Price;
            datetime dt;
         }Position;
         uint    ButtonStatus;
      };

Neste podemos observar que a classe C_Terminal, será herdada de uma forma publica. Ou seja usando a classe C_Mouse, poderemos acessar todos os procedimentos públicos da classe C_Terminal. Assim seria como se a classe C_Mouse, contivesse muito mais capacidade do que de fato teria se apenas o código do arquivo C_Mouse.mqh fosse ser utilizado.  Mas isto não nos traz apenas e somente esta vantagem. Existem outras vantagens em se usar a herança de maneira a tornar as classes bem mais poderosas. Mas isto será visto ao logo dos próximos artigos. Seguindo o fragmento, temos dentro da clausula protegida, duas declarações de enumeração. Estas nos darão a capacidade de poder programar um uma linguagem de nível um pouco maior do que normalmente poderíamos fazer sem o uso delas. A primeira das enumerações é bem simples, e segue a mesma regra e conceito visto no artigo anterior. Mas a segunda enumeração parece um pouco confusa e complicada demais. No entanto vamos entender o por que de ela ser feita desta forma e principalmente o por que de ela existir.

Esta enumeração esta nos dado uma capacidade que de outra forma seria muito menos fácil de ser mantida. ou melhor dizendo, iria nos dar muito mais trabalho. O que esta enumeração especifica está fazendo, é criando definições de nomes. Isto é  equivalente ao comando de pré-processamento #define. Mas diferente utilizar definições, usarmos a enumeração. Assim podemos fazer uso de uma técnica um pouco diferente, mas ao mesmo tempo muito mais simples de ser compreendida no código. Quanto esta enumeração for utilizada você irá de fato ver, como o código se torna muito mais legível. Isto em um código bem complexo, se torna vital isto para não dizer outra coisa. Mas achar que esta enumeração tem uma declaração, a primeira vista muito confusa e complicada, se deve a questão de que você não entende realmente como as enumerações funcionam. Isto pelo ponto de vista do compilador, pois para o compilador uma enumeração nada mais é do que uma sequencia de definições. Onde por padrão o primeiro elemento irá começar no index zero. Mas nada impede que você possa definir qual será o index da enumeração e a partir do momento que isto for feito, o compilador irá começar a incrementar os próximos valores, dos index subsequentes. Isto é muito útil em muitos cenários, onde o valor daquele index que você acabou serve como valor inicial de uma sequência. Não é raro ver grandes listas de enumerações, onde valores de erro serão definidos com base em algum tipo de critério. Você define um nome e aplica ali um determinado valor, e todos os nomes que sucederem aquele terão um valor automaticamente incrementado pelo compilador. Isto torna bem mais simples criar listas de definições, muito grandes sem correr o risco de duplicar em algum momento um determinado valor.

É bastante curioso de fato, não ver grande parte dos programadores usando isto. Já que tal coisa nos ajuda e muito e evitar erros grosseiros durante a programação de certos tipos de projetos. Mas agora que você viu entendeu isto, poderá experimentar e notará que será algo consideravelmente mais simples usar enumeração. Isto quando tiver que criar uma lista grande de coisa correlacionadas e de forma sequencial. No caso como iremos ver, pode ser de uma maneira não sequencial. Mas desta forma que irei mostrar, a intenção de fato é elevar o nível da programação, tornando assim o código muito mais legível e de compreensão muito mais fácil.

A próxima coisa que temos no código é uma estrutura. Esta será responsável por repassar ao restante do código, o que estará ocorrendo com o mouse. É verdade que muitos esperariam ver neste ponto, algum tipo de declaração de variáveis. Mas não é de fato uma boa prática de programação permitir ou declarar qualquer tipo de variável dentro de uma classe, que não esteja sendo feita de fato, dentro da clausula privativa. Da mesma forma, alguns podem pensar que seria mais adequado colocar estas mesmas declarações feitas aqui, em uma clausula pública. No entanto, não gosto de começar assim. Prefiro definir as coisas com acesso um pouco mais restrito, e somente no último caso, permitir que o acesso seja público. Sempre devemos permitir que apenas funções ou procedimentos tenham acesso publico. Salvo as aquelas que são de puro interesse da classe. No entanto fora isto, sempre devemos começar dando o mínimo de privilégios a tudo que criamos.

Seguindo esta ideia. Podemos ver no fragmento abaixo, as variáveis presentes na nossa classe C_Mouse:


   private :
      enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
      struct st01
      {
         st_Mouse Data;
         color    corLineH,
                  corTrendP,
                  corTrendN;
         eStudy   Study;
      }m_Info;
      struct st_Mem
      {
         bool    CrossHair;
      }m_Mem;

Notem como é bastante curioso, o fato de que aqui já temos uma enumeração, que não será de forma alguma visível a nenhum outra parte do código fora da classe. Isto por conta que esta enumeração somente tem alguma utilidade dentro desta classe. Assim não faz sentido que outras partes do código venham a saber da existência desta enumeração. Isto se chama encapsulamento, onde nos reservamos o direito, de que nenhuma outra parte do código, venha a saber como o código responsável por fazer determinado tipo de tarefa de fato faz ou utiliza. Este tipo de ideia é muito apreciada por quem desenvolve bibliotecas, onde você simplesmente dá os meios de outros programadores acessarem os procedimentos. Mas não mostra, de fato como o código da biblioteca funciona.

Seguindo, temos uma estrutura. Esta estará fazendo uso da estrutura que poderá ser acessada fora do corpo da classe. Mas isto será visto durante a explicação dos procedimentos e funções de acesso. No demais, tudo que você precisa saber neste momento, é que esta variável daqui, estará se referindo a uma estrutura privativa da classe. Temos uma outra estrutura, neste caso, prefiro fazer desta maneira. Pois fica claro de que o conteúdo será especial e somente será acessado em pontos bastante específicos no código. Mas nada impediria que estes mesmo dados estivessem declarados dentro estrutura anterior. Você apenas teria que tomar alguns cuidados durante a programação, a fim de evitar modificar tais dados. Já que os presentes na primeira estrutura são de uso geral, e podem sofrer mudanças a qualquer momento.

Agora que já foram vistas as partes referentes as variáveis. Podemos passar a analisar os procedimentos e funções. Começando com o primeiros dos códigos que pode ser apreciado logo abaixo:

inline void CreateObjectBase(const string szName, const ENUM_OBJECT obj, const color cor)
   {
      ObjectCreate(GetInfoTerminal().ID, szName, obj, 0, 0, 0);
      ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_TOOLTIP, "\n");
      ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BACK, false);
      ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, cor);
   }

Este código nos permite fazer uso da reutilização de software. Já que precisaremos criar diversos elementos durante todo o trabalho da classe C_Mouse. E tais elementos terão sempre que seguir uma certa padronização, fica mais simples fazer isto em um único procedimento. Uma coisa que você irá ver se repetir muito nas declarações quando performance for desejável, é o uso desta palavra reservada. Isto se deve ao meu desejo de que o código seja incluído pelo compilador, no ponto onde ele estará sendo declarado. Fazendo com que o código trabalhe como se fosse uma macro. É verdade que isto cria um aumento no tamanho do executável, mas também faz com que durante a execução tenhamos uma ligeira melhoria na performance geral. As vezes este aumento é pequeno devido a vários fatores, não justificando o incremento no tamanho do executável.

Aqui temos um fato, que para muitos não faz muito sentido. Mas que passará a fazer parte de todo o código. Esta função esta se referindo a uma estrutura declarada na classe C_Terminal. Mas o compilador não estará vendo isto como sendo uma função e sim como se fosse uma variável constante. Mas como assim ?!?! Como o compilador estará vendo uma declaração, que para meus olhos parece uma função, como sendo uma variável constante ?!?! Isto não faz nenhum sentido. De fato não faz sentido. Mas ao observar o código, desta chamada e isto na classe C_Terminal. Vemos o seguinte:

inline const st_Terminal GetInfoTerminal(void) const
   {
      return m_Infos;
   }

Este código mostrado está retornando uma referencia a uma estrutura que pertence a classe C_Terminal. No entanto a variável no qual estaremos retornando a referencia é uma variável privativa da classe. Ela de maneira alguma, deve ter seus valores modificados por nenhum outro código, se não aquele presente na classe C_Terminal. Para garantir que ao referenciar esta variável privativa da classe, o código não faça nenhuma mudança na mesma, adicionamos esta declaração daqui. Desta forma o compilador garantirá que o código que receba uma referencia constante. Ou seja seu valor não poderá ser modificado. Ao mesmo tempo usamos esta declaração a fim de evitar que qualquer valor seja modificado. Seja por algum esquecimento ou mesmo falha durante a programação. Assim caso você, mesmo dentro da classe C_Terminal, tente modificar indevidamente algum valor presente dentro da função, isto será visto pelo compilador como sendo um erro. Já que para o compilador nenhuma informação ali poderia ser modificada. Isto por conta de que aquele não é o local correto ou adequado para se fazer tal modificação.

Este tipo de programação com certeza dá mais trabalho. No entanto traz um nível de robustez e confiabilidade ao código, que de nenhuma outra forma, seria possível de ser feita, ou conseguida. Mas existe uma falha aqui. Pelo menos no momento que escreve este artigo. Mas esta falha será explicada futuramente, já que neste momento seria complicado de deixar claro do que se trata. Seguindo poderemos ver o seguinte procedimento, na classe C_Mouse.

inline void CreateLineH(void)
   {
      CreateObjectBase(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
   }

Tal procedimento irá criar a linha horizontal que representa a linha do preço. Atenção ao fato de que por colocarmos toda a complicação em um outro procedimento, tudo que precisamos codificar é apenas uma única linha. Este tipo de código costuma muito comumente ser substituído por algum tipo de macro. Mas já quero tentar fazer sem usar tal recurso. Uma outra forma de também fazer isto, seria colocar o mesmo conteúdo, já que se trata de apenas e somente uma única linha, nos pontos em que ocorreria tal chamada. Pessoalmente não aconselho tal prática. Não que seja errada, mas por conta do fato de que sempre e sempre você terá que cuidar em modificar todas as linhas quando uma e apenas uma delas for modificada. O que sinceramente é uma tarefa muito chata e sujeita a muitos erros. Então mesmo que pareça melhor colocar o código nos pontos que ele é referenciado, faça isto usando uma macro ou um código com a palavra inline em sua declaração.

Os próximos procedimentos não irão fazer muito sentido agora. Mas é preciso os ver antes de dar prosseguimento ao restante da explicação. O primeiro procedimento é visto a seguir:

void CreateStudy(void)
{
   CreateObjectBase(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
   CreateObjectBase(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
   CreateObjectBase(def_NameObjectBitMp, OBJ_BITMAP, clrNONE);
   CreateObjectBase(def_NameObjectText, OBJ_TEXT, clrNONE);                                
   ObjectSetString(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_FONT, "Lucida Console");
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_FONTSIZE, 10);
   ObjectSetString(GetInfoTerminal().ID, def_NameObjectBitMp, OBJPROP_BMPFILE, "::" + def_Fillet);
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
   m_Info.Study = eStudyCreate;
}

Este irá criar os objetos necessários para que façamos estudos no gráfico. Já que podemos desejar criar um estilo próprio. Onde algumas informações que consideramos mais pertinentes e necessárias, possam de fato serem analisadas. Aqui o modelo de estudo é bastante simples. Mas podemos criar uma metodologia bem diversificada, que seja feita de forma rápida e que no final não fique ali poluindo o gráfico. Na metodologia que estou mostrando, e esta é a mais simples. Iremos criar um estudo de forma a verificar a quantidade de pontos entre um dado preço e outro. Nos mostrando se o valor é negativo ou positivo, isto de forma visual. Não é um sistema muito sofisticado. Isto daqui é apenas uma base para que você possa entender como fazer para criar um modelo mais elaborado.

Muitos estudos contam com uma diversidade bastante grande de objetos, e combinações dos mesmos. Sendo em alguns casos necessário efetuar algum tipo de calculo ou busca uma posição em uma faixa de preço ( estudos baseados em máxima e mínimas ). Fazer isto de forma manual, além de ser muito lento, é muito cansativo, dado o fato de ter que colocar e retirar a todo o momento objetos do gráfico. Caso contrário ele acaba virando uma zona total. Onde a informação que precisamos fica ali, no meio de toda aquela bagunça. Então utilize este método como uma base para criar algo ainda melhor e direcionado a suas necessidades. Mas não tenha pressa, iremos melhorar isto depois.

O único cuidado que você precisará de fato ter, é que caso venha a querer colocar, e muito provavelmente isto irá acontecer, algum texto este seja o último objeto na fila de criação. Observem isto no fragmento acima, onde o texto que irá mostrar a informação é o último objeto a ser criado. Isto para que ele não fique de maneira alguma oculto por outros objetos. É bastante comum no começo, você criar diversos objetos e no final um deles, acabar ocultado o objeto que você realmente gostaria de ver. Então fica a dica: O objeto que você de fato tem interesse, deverá sempre ser o último na fila de criação.

O fato das cores dos objetos estarem como declaradas sendo clrNONE, não deverá ser uma preocupação, visto que depois tais cores serão modificadas conforme o estudo irá se dar. Muito bem, se o procedimento acima cria o estudo. O procedimento abaixo executa de fato o estudo.

void ExecuteStudy(const double memPrice)
{
   if (CheckClick(eClickLeft))
   {
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectBitMp, 0, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectText, 0, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > m_Info.Data.Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_COLOR, (memPrice > m_Info.Data.Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectBitMp, OBJPROP_ANCHOR, (memPrice > m_Info.Data.Position.Price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_ANCHOR, (memPrice > m_Info.Data.Position.Price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
      ObjectSetString(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_TEXT, StringFormat("%." + (string)GetInfoTerminal().nDigits + "f ", m_Info.Data.Position.Price - memPrice));
   } else {
      m_Info.Study equal eStudyNull;
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
      ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
   }
   m_Info.Data.ButtonStatus equal eKeyNull;
}

Aqui executamos o estudo no gráfico. Apesar de aparentar se uma coisa muito sem sentido, observando apenas e somente este código acima, no momento em que o código de interação for visto, este código acima fará bem mais sentido, ficando bem mais claro. Mesmo assim vamos entender o que está acontecendo aqui. Por que diferente do código onde criamos os objetos, que serão usados no estudo, este código acima contém algumas coisas que pode ser interessante você compreender. Caso queira adicionar ainda mais detalhes. Basicamente podemos notar aqui a presença de dois segmentos de código: Um onde testamos uma condição, e que apenas observando o código mesmo sem saber programar, você conseguirá entender o que exatamente estamos testando. Quanto mais o código se parecer com a linguagem natural, maior será o nível do código programado. Novamente, não confunda isto com nível de conhecimento do programador, ambas as coisas são diferentes.Temos um outro segmento é onde finalizamos o estudo. Ambos segmentos são simples, e podemos passar por eles rapidamente. Neste ponto movemos os objetos na tela de maneira que eles indiquem, de onde até onde, estamos fazendo o estudo. Isto é bastante clássico. Aqui nestas linhas trocamos as cores dos objetos a fim de mostrar se estamos tendo um movimento de baixa ou de alta. Se bem que muitas das vezes isto é bastante evidente. Mas pode acontecer de você estar usando algum tipo de curva, que deixaria a coisa bem complicada de se ver apenas observando se os valores são positivos ou negativos. Lembre-se: Você poderá gerar estudos de diversas formas, baseadas em diversas coisas. Talvez a parte mais importante, seja esta linha, onde apresentamos os valores com base em algum tipo de calculo ou analise. Aqui você poderá apresentar diversas informações diferentes e de maneiras distintas. Mas isto vai de caso a caso.

De qualquer maneira precisamos de alguma forma de encerrar a apresentação do estudo. Isto é conseguido neste segundo segmento do código. No geral este segundo segmento não chama muito a atenção. Mas existe sim um ponto que merece uma atenção mais especial, e este é o momento em que usamos a função ObjectsDeleteAll. E por que este ponto é especial e merece alguma atenção ?!?! O motivo está na classe C_Terminal. Se você observar o constructor da classe C_Terminal, irá notar a seguinte linha, mostrada abaixo:

ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);

Esta linha informa a plataforma que sempre que um objeto for removido do gráfico, a plataforma deverá emitir um evento avisando ao código qual foi o objeto removido. Ao usarmos a função ObjectsDeleteAll, iremos remover todos os elementos, ou objetos, usados no estudo que estaremos fazendo. Isto fará com que o MetaTrader 5 gere um evento para cada um dos objetos removidos do gráfico. Isto é tudo que a plataforma de fato fará, depende do nosso código recriar ou não tais objetos. A questão aqui, é que pode acontecer de ocorrer algum tipo de falha, onde os objetos simplesmente não serão removidos ( já que o código estará os recriando ) ou eles serem removidos, mas o código não ser informado disto. Neste caso a propriedade CHART_EVENT_OBJECT_DELETE estaria com o valor false. Apesar de no primeiro momento, isto não acontecer. Conforme o código vai se tornando maior, existirá momentos em que esta propriedade será modificada, e você por esquecimento, pode não reativar a mesma. Assim a plataforma não iria gerar o evento, avisando ao nosso código sobre o fato de objetos terem sido removidos do gráfico.

Vamos agora dar uma olhada no constructor da classe C_Mouse.

C_Mouse(color corH, color corP, color corN)
   :C_Terminal()
{
   m_Info.corLineH  = corH;
   m_Info.corTrendP = corP;
   m_Info.corTrendN = corN;
   m_Info.Study = eStudyNull;
   m_Mem.CrossHair  = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
   ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
   ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
   CreateLineH();
}

Aqui temos uma chamada explicita ao constructor da classe C_Terminal. É importante que este constructor seja chamado antes de qualquer linha na classe C_Mouse, realmente venha a ser executada. Isto por conta que precisamos de alguns valores da classe C_Terminal, tenham sido de fato inicializados, da forma correta. Depois disto iniciamos algumas coisas, e armazenamos o estado de outras. Mas o que nos interessa aqui, são estas 2 linhas. Onde informamos a plataforma que desejamos receber eventos relacionados ao mouse e que não queremos usar a linha de estudos padrão da plataforma. Uma vez que isto tenha sido feito, a plataforma irá obedecer o nosso pedido e irá reportar os eventos relacionados ao mouse, da mesma forma, agora ao clicarmos tentando usar a linha de estudos da plataforma, deveremos nos mesmo, por meio de nosso código oferecer os meios de fazer tais estudos.

O próximo código na sequencia é destructor da classe. Já que precisamos devolver a plataforma o seu funcionamento original.

~C_Mouse()
{
   ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false);
   ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
   ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
   ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
}

Aqui temos uma questão que pode não parecer lógica, mas se faz necessária. Por que temos esta linha de código aqui ?!?! O motivo é que se você tentar remover os objetos criados pela classe, e um deles especificamente a linha de preço, irá fazer com que a plataforma gere um evento informando que o objeto foi removido do gráfico. Nosso código irá tentar recolocar tal objeto no gráfico. No entanto o código esta sendo removido do gráfico. Para evitar que a plataforma gere o evento, precisamos dizer a ela para não fazer isto. Por isto precisamos desta linha de código. Mas você pode estar pensando: Mas a classe C_Terminal também não faria tal coisa ?!?! Informando que não desejamos mais receber eventos relacionados a remoção de objetos do gráfico ?!?! Sim ela fará isto. Mas por conta que ainda precisamos de alguns dados presentes na classe C_Terminal, deixamos que o compilador gere implicitamente a chamada ao destructor da classe C_Terminal. Esta chamada somente ocorrerá depois que a última linha do destructor da classe C_Mouse for executada. Assim sendo, sem adicionar a linha que esta destacada no código, a plataforma ainda irá gerar o evento, e ao  faze-lo mesmo que a linha seja removida no primeiro momento, pode acontecer de ela ser recolocada antes do código sair completamente. As demais linhas, acredito não serem complicadas de compreender. Já que tudo que estaremos fazendo, é repor o gráfico ao seu estado original.

Com isto estamos chegando as última funções deste artigo.

inline bool CheckClick(const eBtnMouse value) { return (m_Info.Data.ButtonStatus & value) == value; }

Esta linha de código permite que usemos algumas coisas bem interessantes. Mas no momento você só precisa entender o seguinte: Usando a enumeração que definimos os eventos recebidos do mouse. Verificamos se o valor informado pela plataforma é o mesmo que um dado ponto do código espera receber. Se este for o caso a função retornará verdadeiro, caso contrário irá retornar falso. No momento isto parece banal. Mas quando começamos a interagir com as coisas, isto irá ser de grande ajuda para nos. Mas existe um pequeno macete na declaração a fim de podermos usar esta função. Mas por ele não é de fato necessário no momento, não irei entrar em detalhes ainda sobre isto.

A próxima função é igualmente simples, e segue o mesmo principio da função GetInfoTerminal presente na classe C_Terminal,

inline const st_Mouse GetInfoMouse(void) const { return m_Info.Data; }

A ideia é retornar o conteúdo presente na variável que contem a estrutura de informações sobre o mouse. Isto de forma que este mesmo conteúdo não possa ser de maneira alguma modificado, sem o devido conhecimento da classe C_Mouse. Por conta que este método já foi comentando anteriormente, não vejo necessidade de recapitular ele aqui. Em essência ambos funcionam da mesma forma.

Finalmente chegamos a ápice do código da classe C_Mouse. Mas estou pensando uma coisa. Então irá ficar uma última função presente na classe C_Mouse para o próximo artigo. O motivo será explicado lá.


Conclusão

Apesar de estamos montando algo bem sugestivo. Isto está longe de ser definitivo. Nos vemos no próximo artigo.


Como criar um indicador personalizado True Strength Index usando MQL5 Como criar um indicador personalizado True Strength Index usando MQL5
Apresento um novo artigo sobre como criar um indicador personalizado. Desta vez, trabalharemos com o True Strength Index (TSI) e criaremos um Expert Advisor com base nele.
Integrando modelos de ML ao Testador de Estratégias (Conclusão): Implementação de um Modelo de Regressão para Previsão de Preço Integrando modelos de ML ao Testador de Estratégias (Conclusão): Implementação de um Modelo de Regressão para Previsão de Preço
Este artigo descreve a implementação de um modelo de regressão de árvores de decisão para prever preços de ativos financeiros. Foram realizadas etapas de preparação dos dados, treinamento e avaliação do modelo, com ajustes e otimizações. No entanto, é importante destacar que o modelo é apenas um estudo e não deve ser usado em negociações reais.
Desenvolvendo um sistema de Replay (Parte 28): Projeto Expert Advisor — Classe C_Mouse (II) Desenvolvendo um sistema de Replay (Parte 28): Projeto Expert Advisor — Classe C_Mouse (II)
Quanto de fato os primeiros sistema capazes de fatorar alguma coisa, começaram a ser produzidos. Tudo tinha que ser feito por engenheiros com grande conhecimento, no que estava sendo projetado. Isto nos primórdios da computação, onde se quer existia algum tipo de terminal, para que fosse possível programar algo. Conforme ia se desenvolvendo, e o interesse de que mais pessoas também conseguisse criar algo, começou surgir novas ideias e meios, de programar aquelas máquinas, que antes era feito mudando a posição dos conectores. Assim começamos a ter os primeiros terminais.
Previsão usando modelos ARIMA em MQL5 Previsão usando modelos ARIMA em MQL5
Neste artigo, continuamos a desenvolver a classe CArima para construir modelos ARIMA adicionando métodos de previsão intuitivos.