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

Desenvolvendo um sistema de Replay (Parte 30): Projeto Expert Advisor - Classe C_Mouse (IV)

MetaTrader 5Testador | 10 outubro 2023, 16:36
287 1
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 29): Projeto EA - Classe C_Mouse (III) desenvolvemos a classe C_Mouse de forma que seria possível expandir a funcionalidade dos estudos, sem que isto viesse de alguma maneira, comprometer alguma parte do nosso código. Já que você passa a contar e a utilizar uma técnica de programação, que permitirá criar códigos em paralelo, fazendo que tenhamos um projeto principal que crescerá de maneira organizada. Ao mesmo tempo, caso desejemos, podemos adicionar funcionalidades extras ao sistema. De forma que tais funcionalidades ao serem implementadas, não faria com que nosso código principal viesse a ficar comprometido de alguma forma, ou viesse a ter pendencias durante o uso indiscriminado de herança.

Apesar do sistema de programação orientada em objetos ( OOP ) ser uma excelente forma de programação. Sendo muito mais adequada para produção de projetos em que queremos ter um controle rígido sobre o que esta acontecendo de maneira a não gerar erros estranhos conforme o sistema vai crescendo. As vezes precisamos fazer com que parte do projeto seja desenvolvido de uma maneira meio que paralela. Parece estranho dizer isto, mas quando estamos adicionando algum tipo de recurso ou método em um programa. Não é muito adequado que você já venha a colocar logo no inicio ( fase de testes ) o recurso em meio ao código já em estágio avançado. Ou melhor dizendo, não é adequado você adicionar um recurso não testado em um código já testado e que se encontra em funcionamento, pois isto pode gerar falhas que não foram totalmente prevista.

Tais falhas são responsáveis muitas vezes por fazer um projeto inteiro, voltar para o estágio inicial de desenvolvimento, por conta que não conseguimos remover facilmente o recurso adicionado recentemente. Já que ele pode estar incrustrado de tal maneira no código, que remover o mesmo é muito mais complexo do que reiniciar o projeto em um estágio anterior. Apesar de muitas pessoas, mas principalmente programadores que estão começando, não fazem de fato, uso de uma estrutura de diretórios, de forma a ter um ponto a ponto de retorno a estágios anteriores de desenvolvimento. Podemos mesmo sem usar tais estruturas de diretórios, fazer uso de algum tipo de técnica que nos permite retornar o sistema em um ponto que o recurso adicionado, não irá fazer de fato parte do projeto terminado.

Podendo desta forma, ser trabalhado em paralelo, enquanto o projeto final pode seguir o seu desenvolvimento sem grandes sobressaltos. Para fazer uso de tal coisa, mostrei no artigo anterior como você faz para criar tal abordagem, usando para isto ponteiros. Agora vamos elevar um pouco mais a régua, a ponto de conseguir gerar um estudo mais elaborado a partir de um modelo básico. Caso este estudo, ou recurso venha a ser adequado ao projeto final, podemos quando este estiver em um estágio mais avançado em termos de testes, assim sendo considerado estável e robusto o suficiente, vim a ser englobado ao sistema de classes principal. Desta forma, o que era anteriormente considerado um projeto paralelo, passa a ser parte do projeto final. Herdando e sendo herdado dentro do sistema de classes.

Para demonstração, iremos criar uma modificação da classe C_Mouse. Mas sem usar para isto herança ou polimorfismo. Ao mesmo tempo iremos ter um modelo de estudos completamente diferente, do que é o sistema original que está presente na classe C_Mouse. Para fazer isto, iremos criar uma nova classe que poderá herdar ou não a classe C_Studys, que foi vista no artigo anterior. O fato de fazer a herança ou não da classe C_Studys, é mais uma questão pessoal do que uma questão pratica. Para dizer a verdade, de uma forma ou de outra, um projeto nada terá haver com o outro projeto. Podendo ser trabalhados em paralelo. No entanto, independente disto, qualquer código pertencente ao sistema principal, herdará a classe C_Mouse até que os códigos que expandem a classe, possam ser considerados estáveis e interessantes o suficiente, para serem usados no projeto final.

Agora antes de realmente entrar na parte sobre a programação, é importante que você compreenda que o sistema basicamente pode seguir por dois caminhos diferentes. A escolha de seguir um caminho ou outro, depende do que você pretende fazer e até onde deseja ir. Já que temos dois caminhos e a diferença entre eles, é bem pequena, irei mostrar ambos. No código em anexo, você terá acesso a um dos dois caminhos. Mas se desejar, poderá fazer as devidas modificações a fim de seguir pelo outro caminho, nada lhe impede de fazer a sua própria escolha de qual tipo de caminho seguir.

A ideia aqui, sempre será demonstrar o que se pode fazer dentro da plataforma, e não como se deve fazer.


Adições a classe C_Terminal.

Apesar do sistema que esta sendo programado para demonstração, não exigir ou precisar de nenhuma outra adição no código principal. Por motivos práticos e para já começar a testar a criação de objetos, que de fato iremos usar e bastante no decorrer da criação do código. Vamos adicionar um pequeno código de criação genérica de objetos no gráfico do ativo. Assim começaremos testando e aprimorando o mesmo, já logo de inicio. O código para fazer isto, já de certa forma vem sendo visto a algum tempo. De certa maneira já estava sendo cogitado colocar o mesmo em um ponto mais adequado. Até que um outro ponto, mais adequado, venha a de fato surgir, a rotina de criação irá ficar na classe C_Terminal, e pode ser vista logo abaixo:

inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor, const int zOrder = -1)
   {
      ObjectCreate(m_Infos.ID, szName, obj, 0, 0, 0);
      ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
   }

Esta rotina, será a rotina de criação genérica dos objetos a serem apresentados no gráfico. Em vários momentos você verá que ela irá aparecer no código tendo apenas 2 elementos declarados. Isto por conta que este elemento daqui, esta sendo declarado com um valor default, assim ele não precisa ser de fato declarado durante a chamada, salvo o fato de que o seu valor venha a ser diferente por um motivo ou outro. Fora isto, sempre veremos a rotina sendo chamada com dois elementos em sua declaração. O detalhe é que esta propriedade dos objetos a OBJPROP_ZORDER, serve para solucionar um problema que depois iremos ver acontecer em alguns pontos. Sem que esta propriedade seja definida de uma maneira correta, iremos ter sérios problemas com objetos colocados no gráfico do ativo, durante as atividades de uso do programa, seja usando o replay / simulador, seja operando em conta demo ou real. Agora que já vimos a mudança, na qual o código principal sofreu, podemos começar a compreender como usar o sistema de maneira diferente, do que estará sendo observado, ao usar apenas e somente o código original. Mas para separar melhor as coisas vamos ver isto em tópicos.


Primeiro caminho: Utilizando herança

Para seguir pelo primeiro caminho, onde usaremos a herança, mas não herdando a classe C_Mouse, e sim a classe C_Study que foi vista no artigo anterior. Nosso arquivo de cabeçalho C_StudyS2.mqh, irá ter o seguinte inicio de código:

//+------------------------------------------------------------------+
#include "C_StudyS1.mqh"
//+------------------------------------------------------------------+

// ... Definições locais ....

//+------------------------------------------------------------------+

// ... Alias Locais ...

//+------------------------------------------------------------------+
class C_StudyS2 : public C_StudyS1
{
   protected:
   private :

// ... Código e procedimentos internos ...

//+------------------------------------------------------------------+
   public  :
//+------------------------------------------------------------------+
      C_StudyS2(C_Mouse *arg, color corP, color corN)
         :C_StudyS1(arg, corP, corN)
      {                               
// ... Código interno ....

      }
//+------------------------------------------------------------------+
virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
      {
         double v1, v2;
         int w, h;
         string sz1;
                                
         C_StudyS1::DispatchMessage(id, lparam, dparam, sparam);

// ... Código interno ...

      }
//+------------------------------------------------------------------+
};

Aqui vemos que a classe do artigo anterior, esta sendo usada seguindo os princípios de herança, onde herdamos a classe e vamos somando conhecimento e propriedades. Em diversos casos, isto será de fato o melhor caminho a ser tomado, em outros este caminho não será definitivamente a melhor escolha. No entanto, é importante você ter o conhecimento das partes que são diferentes e complementares entre os caminhos. Isto para que no momento que for usar tal técnica, consiga de fato tirar proveito no seu uso. Preste atenção a todos os pontos que estão sendo destacados no fragmento de código acima. Não se preocupem pois irei explicar como se trabalha com isto, a fim de conseguir gerar algo interessante.

Logicamente, por estar usando herança, o código do Expert Advisor para este caso, e não necessariamente precisa ser um Expert Advisor, pode ser um indicador ou script, será ligeiramente diferente, do caso em que a herança não estará sendo usada. Então para entender estas diferenças, vamos primeiramente ver o código do Expert Advisro para este primeiro caso. Este pode ser visto na integra logo abaixo:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS2 *extra = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if (mouse.GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

Notem que as únicas diferenças para o código do artigo anterior, é justamente as partes que se encontram destacadas. Isto se deve ao fato de estarmos usando o sistema de herança. Mas como é sabido o sistema de herança serve muito bem quando queremos um sistema que vá crescendo, de uma maneira coesa e sem muitos imprevistos. Porém aqui podemos acabar tendo outras questões relacionadas, que nos impede ou no mínimo dificulta a nossa vida. As vezes precisaremos usar uma forma um pouco diferente, que é justamente a que estará disponível no anexo. Se você desejar usar o sistema como sendo um modelo baseado em herança, tudo bem. Apenas lembre-se de efetuar as mudanças que foram marcadas em destaque e tudo se sairá da maneira correta e sem problemas.


Segundo caminho : Usando Ponteiros.

Neste segundo caminho, iremos ver a explicação em detalhes do código da classe. Mas antes vamos ver como fica o código de Expert Advisor. Mas neste ponto teremos grandes diferenças frente ao código da classe, que praticamente irá sofrer apenas os acréscimos vistos no tópico anterior. Pois bem o código do Expert Advisor, para se seguir este segundo caminho pode ser visto logo abaixo na integra:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS1.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS1 *extra1 = NULL;
C_StudyS2 *extra2 = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra1 = new C_StudyS1(mouse, user01, user02);
   extra2 = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra1;
   delete extra2;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra1).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if ((*mouse).GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra1).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra1).DispatchMessage(id, lparam, dparam, sparam);
   (*extra2).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

Aqui estou mostrando os pontos onde estamos usando o sistema de classe principal, que esta todo marcado em amarelo O sistema de classe de expansão que foi visto no artigo anterior, esta sendo marcado todo em verde. Já o sistema que irá produzir um outro tipo de estudo, mas poderia ser outra coisa também, esta sendo mostrado em laranja. Notem o fato de que não estarmos usando herança, nos força a ter que declarar mais código no Expert Advisor. Mas ao mesmo tempo, nos permite lançar mais código paralelo, de maneira a testar qual o tipo de coisa que iremos ter em uma versão finalizada. Contudo o maior dos detalhes é que, se este código paralelo, começar a apresentar algum defeito, ou falha, podemos simplesmente o retirar do código, sem muitas dificuldades. No entanto existe uma outra questão aqui, tanto o código em laranja quanto o código em verde, podem ser dirigidos de uma forma polimórfica. Isto nos permitindo testar mais aspectos do sistema, que esta sendo desenvolvido de uma maneira paralela. Mas esta questão sobre polimorfismo irá ficar para um outro momento. Já que se formos falar disto neste momento, irá complicar muito a explicação, de maneira que aqueles entusiastas que estão seguindo a sequencia, não conseguirão de fato acompanhar todo o raciocínio envolvido a fim de usar o polimorfismo.

Feito estas considerações, podemos passar para o código da classe. Lembrando que tanto o código do primeiro caminho, quando o código do segundo caminho, são praticamente idênticos. Com exceção é claro, dos pontos mostrados no tópico do primeiro caminho.


Dissecando código da classe C_StudyS2

De certa forma, qualquer código relacionado ao sistema de estudos, irá ser muito similar entre si, com pequenas exceções. Mas mesmo assim, existem diversos pontos que tornam o código de geração de estudos algo interessante de ser analisado e compreendido. Mas vamos ver isto de uma maneira mais clara. Lembrando que o código aqui é apenas para demonstração, não é de maneira alguma um método finalizado. O arquivo C_StudyS2.mqh começa da seguinte maneira:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "..\C_Mouse.mqh"
#include "..\..\..\Service Graphics\Support\Interprocess.mqh"
//+------------------------------------------------------------------+
#define def_ExpansionPrefix "Expansion2_"
#define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
#define def_ExpansionFibo def_ExpansionPrefix + "FB"
//+------------------------------------------------------------------+
#define def_InfoTerminal (*mouse).GetInfoTerminal()
#define def_InfoMousePos (*mouse).GetInfoMouse().Position
//+------------------------------------------------------------------+
class C_StudyS2
{
   protected:
   private :
//+------------------------------------------------------------------+
      C_Mouse *mouse;
//+------------------------------------------------------------------+
      struct st00
      {
         bool            ExecStudy,
                         ClearStudy;
         double          MemPrice;
         datetime        MemDT;
         color           corP,
                         corN;
       }m_Info;
//+------------------------------------------------------------------+

Aqui temos a declaração dos arquivos a serem incluídos no sistema. Observem que os caminhos são relativos ao caminho onde se encontra este arquivo C_StudyS2.mqh. Sendo assim poderemos transportar mais facilmente o projeto para outros diretórios, desde é claro a estrutura se mantenha. Seguindo temos a definição de alguns nomes para serem usados nos objetos, que fazemos uso durante o processo de estudo. Logo depois temos as declarações de alias, para facilitar o processo de programação, já que estes serão usados em muitos pontos durante a codificação. E como último ponto a ser visto neste fragmento, temos uma estrutura que será acessada por meio de uma variável global privativa.

A próxima coisa que iremos ver, nesta classe é o fragmento logo abaixo:

#define def_FontName "Lucida Console"
#define def_FontSize 10
       void GetDimensionText(const string szArg, int &w, int &h)
          {
             TextSetFont(def_FontName, -10 * def_FontSize, FW_NORMAL);
             TextGetSize(szArg, w, h);
             h += 5;
             w += 5;
          }
//+------------------------------------------------------------------+
       void CreateBTNInfo(int x, int w, int h, string szName, color backColor)
          {
             (*mouse).CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor);
             ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, def_FontName);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, def_FontSize);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x);
          }
#undef def_FontSize
#undef def_FontName

Aqui temos duas declarações que serão localizadas e utilizadas apenas neste local, por conta disto definimos e removemos a definição, assim que ela se torna desnecessária para o restante do código. Neste ponto, já estamos de fato fazendo a chamada a rotina que cria de uma maneira genérica os objetos que serão usados no gráfico. A questão aqui é criar o objeto e logo depois ajustar conforme necessário algumas de suas propriedades, a fim de que ele seja plotado da maneira esperada. No entanto se você observar, estamos usando um botão como se fosse uma janela, onde teremos um texto somente de leitura. Talvez fosse mais adequado utilizar um objeto OBJ_LABEL ou OBJ_EDIT aqui. Mas já que a ideia é apenas demonstrar uma forma de se obter o resultado mais adequado. Assim podemos nos dar ao luxo de usar um outro objeto que lance os dados no gráfico para nos.

A grande verdete desta classe, são dois procedimentos que ela contem. O primeiro procedimento pode ser visto logo abaixo. O outro será visto mas para o final do artigo. Então vamos ver como esta classe cria o estudo que é apresentado do vídeo 01, fazendo uso do objeto Fibonacci, o código para criação do objeto pode ser visto a seguir:

void CreateStudy(void)
   {
      const double FiboLevels[] = {0, 0.236, 0.382, 0.50, 0.618, 1, 1.618, 2};
      ENUM_LINE_STYLE ls;
      color cor;
                                
      ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TB");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TI");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TT");
      (*mouse).CreateObjectGraphics(def_ExpansionFibo, OBJ_FIBO, clrNONE);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_HIDDEN, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_RAY_LEFT, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELS, ArraySize(FiboLevels));
      for (int c0 = 0, c1 = ArraySize(FiboLevels); c0 < c1; c0++)
      {
         ls = ((FiboLevels[c0] == 0) || (FiboLevels[c0] == 1) || (FiboLevels[c0] == 2)  ? STYLE_SOLID : STYLE_DASHDOT);
         ls = (FiboLevels[c0] == 0.5 ? STYLE_DOT : ls);
         switch (ls)
         {
            case STYLE_DOT    : cor = clrBlueViolet;  break;
            case STYLE_DASHDOT: cor = clrViolet;      break;
            default           : cor = clrIndigo;
         }
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELSTYLE, c0, ls);
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELCOLOR, c0, cor);                                  
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELWIDTH, c0, 1);
         ObjectSetString(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELTEXT, c0, (string)NormalizeDouble(FiboLevels[c0] * 100, 2));
      }
      ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 1, m_Info.MemPrice = def_InfoMousePos.Price);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 1, m_Info.MemDT = def_InfoMousePos.dt);
      CreateBTNInfo(def_InfoMousePos.X, 50, 18, def_ExpansionBtn1, clrNONE);
      m_Info.ExecStudy = true;
      m_Info.ClearStudy = false;
   }

Apesar deste código, a primeira vista parecer complicado e muito pouco claro, ele na verdade é composto de 3 partes. Cada uma delas fazemos algo especifico a fim de poder criar o estudo fazendo uso do objeto OBJ_FIBO.

  1. Na primeira parte eliminamos os objetos "indesejáveis" que foram criados pela classe C_Mouse, quando esta recebeu da plataforma um evento, que indica o fato do usuário estar iniciando um estudo no gráfico do ativo. Ao fazer a remoção destes objetos, temos que tomar o cuidado para não remover algo que não seria de fato indispensável. Desta forma podemos configurar um estudo bastante particular, dispensando qualquer coisa que não precisa ser visto no estudo que estaremos criando aqui. Observem também que eliminamos o objeto do estudo antigo, isto serve para os permitir fazer um estudo com base em algum critério especifico. Porém o motivo disto também pode ser, que você deseje usar uma combinação de teclas a fim de criar um estudo em cima de uma variação do objeto OBJ_FIBO. Estas variações podem ser OBJ_FIBOTIMES, OBJ_FIBOFAN, OBJ_FIBOARC, OBJ_FIBOCHANNEL e OBJ_EXPANSION. Todos estes seguem os mesmos princípios mostrados aqui.
  2. A segunda parte é onde estaremos de fato criando e definindo as propriedades do objeto. Aqui temos alguns pontos interessantes: Neste em que dizemos a plataforma que o objeto será visível na lista de objetos. Já neste ponto dizemos quais serão os níveis que o objeto irá conter. É verdade que aqui usei níveis estáticos, mas nada impede de você usar níveis dinâmicos em um sistema próprio, ou que use níveis diferentes. Já nesta região, dizemos como cada um dos níveis irá se parecer, tanto em questão de cor, quanto no tipo de linha que será usado para construir o mesmo. Você pode modificar isto a vontade, de maneira a ter um nível adequado de visualização, já que ao fazer um estudo queremos de fato que ele seja rapidamente compreendido a fim de tirar algum tipo de proveito.
  3. E na terceira e última parte começamos a construir de fato o objeto diretamente no gráfico, ou seja, aqui ele irá começar a ser plotado. Assim como também estamos informando por meio de variáveis o que está acontecendo. Isto para que durante o procedimento que será visto depois, possamos de fato fazer a coisa da maneira correta.

Basicamente é assim que iremos criar um estudo nos baseando em um sistema já produzido e testado, que se encontra na classe C_Mouse. Ou seja não iremos realmente construir algo do nada, iremos reutilizar a adequar o que já temos de maneira a obter algo diferente. Mas a coisa toda, de fato será melhor compreendida quando o segundo procedimento for de fato visto. Mas vamos continuar, vendo agora o constructor e o destructor da classe. Estes podem ser vistos no fragmento abaixo:

C_StudyS2(C_Mouse *arg, color corP, color corN)
   {                               
      mouse = arg;
      ZeroMemory(m_Info);
      m_Info.corP = corP;
      m_Info.corN = corN;
   }
//+------------------------------------------------------------------+
~C_StudyS2()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_ExpansionPrefix);
   }

Observem com atenção, estes dois procedimentos. Estes são voltados ao uso do sistema baseado no segundo caminho. Para se utilizar o modelo de herança, você deve adicionar as linhas que estão sendo usadas no tópico sobre o primeiro caminho. A mesma coisa, deverá ser feita neste último procedimento visto na classe. Este é é justamente o que permite a interação com a plataforma. Observem o código na integra deste procedimento, que de fato permite criar o estudo no fragmento abaixo:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      double v1, v2;
      int w, h;
      string sz1;
                                
      switch (id)
      {
         case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) && (m_Info.ExecStudy)) m_Info.ClearStudy = true;
            break;
         case CHARTEVENT_MOUSE_MOVE:
            if (mouse.GetInfoMouse().ExecStudy)
            {
               if (!m_Info.ExecStudy) CreateStudy();
               v1 = def_InfoMousePos.Price - m_Info.MemPrice;
               v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
               sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);
               GetDimensionText(sz1, w, h);
               ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 0, def_InfoMousePos.Price);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 0, def_InfoMousePos.dt);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetString(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_TEXT, sz1);                                                                                                                             
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XSIZE, w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YSIZE, h);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XDISTANCE, def_InfoMousePos.X - w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, def_InfoMousePos.Y - (v1 < 0 ? 1 : h));
            }else if (m_Info.ExecStudy)
            {
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, clrNONE);
               ObjectDelete(def_InfoTerminal.ID, def_ExpansionBtn1);
               if (m_Info.ClearStudy) ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
               m_Info.ExecStudy = false;
            }
            break;
         }
      }

Código curioso estes que está logo acima, não é mesmo ?!?! Pois bem, notem que não estamos em nenhum momento, olhando o que esta sendo feito pelo mouse. Estamos observando o que esta sendo feito na classe C_Mouse. Isto é feito no momento em que fazemos a verificação destes teste daqui. Enquanto a classe C_Mouse estiver indicando que estamos fazendo algum estudo, esta classe daqui irá seguir esta orientação, fazendo assim o estudo seguindo o que a classe C_Mouse esta ordenando. Assim que a classe C_Mouse, já não esta sendo usada em algum estudo. Iremos remover o objeto usado para colocar o texto de informação. Mas se durante o estudo a tecla ESC for pressionada, teremos também a remoção do objeto de estudo. Uma coisa que você irá notar, é que o objeto usando para nos informar o texto, tem suas dimensões calculadas de maneira dinâmica, ou seja, pode ser que ele seja maior ou menor dependendo do caso, tudo isto é controlado nesta região daqui. Assim como a questão referente as cores, e posicionamento dos objetos.

Existe uma parte, que é de fato bem interessante neste código. Merecendo uma explicação melhor. Desta forma, vamos retirar ela e coloca-la em destaque no fragmento abaixo. Assim poderei explicar em detalhes o que estaremos apresentando, e por que os valores são visto daquela forma especifica.

v1 = def_InfoMousePos.Price - m_Info.MemPrice;
v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);

Para entender estas 3 linhas de fatoração e formatação das informações, que serão apresentadas, é preciso que você consiga perceber que o sistema, esta de fato se adequando de maneira dinâmica ao ativo, no qual o código está sendo executado. Alguns ativos podem precisar de 4 dígitos para representar seus valores, enquanto outros precisam de 5 dígitos. Em diversos ativos de bolsa, teremos ativos que precisam de 2 dígitos. Para que o sistema se adapte de maneira simples, usamos este tipo de modelagem vista acima. Parece confuso, mas na verdade é apenas pouco usual.

Para começar, fazemos uma fatoração, a fim de definir a diferença entre o preço onde o estudo se iniciou e o ponto onde a linha de preço se encontra. Desta forma teremos um valor, seja em pontos, pips ou valor financeiro, de deslocamento entre a posição que começamos o estudo com a posição atual onde a o mouse se encontra. Mas para apresentar isto de maneira correta, precisamos saber qual a quantidade de dígitos exigidos. Para fazer isto, usamos a seguinte técnica: Sabemos que ao usar o carácter de porcentagem ( % ) poderemos definir o tipo de informação a ser convertida em string. Pois bem, usando a seguinte formatação < %.2f > teremos um valor que conterá duas casas decimais, < %.4f > teremos um valor que conterá 4 casas decimais e assim por diante. Mas precisamos que isto seja definido em RUN-TIME. 

Assim usamos este sistema daqui. desta maneira a própria função StringFormat irá criar uma formatação adequada. Sei que parece confuso fazer isto, mas assim que o valor que calculamos com diferença for ser lançado, este será lançado seguindo exatamente a formatação criada. Teremos assim um numero de casas decimais adequadas ao valor que deverá ser apresentado. Para de fato compreender com isto funciona na prática, você precisará usar este mesmo código em ativos com um numero diferente de dígitos, assim ficará mais claro. Uma outra questão, que pode ser vista aqui e esta de fato é bem interessante para quem deseja operar certos tipos de movimentos, é saber de maneira simples, a quantidade de barras a partir de um determinado ponto.

Algumas plataformas contam com um indicador que faz a contagem das barras e deixa isto visível no gráfico. Você também pode criar tal indicador facilmente. Mas isto irá colocar mais e mais informações no gráfico, dificultando muitas vezes a leitura. Já que ele acaba ficando extremamente poluído com uma quantidade enorme de informações, e muitas delas, as vezes não são necessárias em grande parte do tempo. Mas usando a própria linguagem MQL5, de maneira um pouco mais exótica, podemos contar quantas barras estão dentro da área, onde o estudo esta sendo executado, e apresentar este valor em tempo real diretamente no gráfico. Uma vez que o estudo tenha terminado, o gráfico continuará com apenas e somente as informações que precisamos.

Para conseguir fazer este tipo de analise, usamos esta função com estes parâmetros. Mas atenção: Se o estudo estiver sendo executado em uma região, onde não se encontra nenhuma barra, o valor indicado será de -1 e caso o estudo esteja se dando, sobre uma única barra teremos o valor de zero. Se você desejar mudar isto, já que a quantidade de barras será referente a quantidade presente na região do estudo, bastará remover este valor de -1 da fatoração. Assim o valor será sempre o numero real de barras, contando inclusive com a onde o estudo se iniciou. Pode ser um detalhe para alguns, mas para outros pode ser de fato interessante saber por que as vezes é obtido o valor de -1 no estudo.

Já que desejamos também informar um percentual de desvio. Usamos este cálculo daqui a fim de gerar tal percentual. Para que a visualização seja facilmente compreendida, usamos esta formatação, assim conseguimos imprimir o símbolo de percentual junto com o restante das informações.


Conclusão

Aqui demonstrei uma técnica que pode lhe ajudar muito, em vários momentos durante a sua vida como programador. Demonstrei que diferente do que muitos dizem, não é a plataforma que é limitada, mas sim o conhecimento do individuo que diz que a plataforma ou linguagem não nos permite criar as coisas. O que foi explicado aqui, mostra que com um pouco de bom senso e criatividade, você pode tornar a plataforma MetaTrader 5 muito mais interessante e versátil. E sem precisar de fato criar programas malucos ou coisas do tipo. Você pode criar um código simples, porém seguro e confiável. Usando de perspicácia, domar o código a fim de modificar algo já existente, sem se quer remover ou adicionar uma única linha se quer, no código original. Assim se em algum momento aquele código que você criou e durante um tempo utilizou, se tornou de fato útil para você. Você pode adiciona-lo de forma definitiva e com muito pouco trabalho ao código que você tem como seguro. Isto usando o conceito de classes. Onde você simplesmente cria uma hierarquia de herança de códigos.

Não existe trabalho que não possa ser feito. Existe sim trabalho que alguns não conseguem fazer. Mas isto não quer dizer que a tarefa não possa ser executada.

No anexo, você terá o código completo do que foi mostrado nestes artigos. Mas no próximo artigo iremos continuar a desenvolver o sistema. Mas sem o código referente aos estudos. Talvez com parte dele sendo integrado a classe C_Mouse. Mas se isto acontecer, não entrarei em detalhes. Pois todo o código foi explicado nestes últimos artigos. Então até.


Arquivos anexados |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
Últimos Comentários | Ir para discussão (1)
ChatGPT da OpenAI dentro do framework de desenvolvimento MQL4 e MQL5 ChatGPT da OpenAI dentro do framework de desenvolvimento MQL4 e MQL5
Neste artigo, vamos experimentar e explorar a inteligência artificial ChatGPT da OpenAI, a fim de entender suas capacidades com o objetivo de reduzir o tempo e o esforço de desenvolvimento de seus Expert Advisors, indicadores e scripts. Vou rapidamente abordar essa tecnologia e tentar mostrar como usá-la corretamente para programar nas linguagens MQL4 e MQL5.
Estratégia de negociação simples: Reversão à média Estratégia de negociação simples: Reversão à média
A reversão à média é um tipo de negociação contra-tendência em que o trader espera que o preço retorne a algum tipo de equilíbrio, geralmente medido por uma média ou outra estatística de tendência central.
Entendendo a programação orientada a objetos (POO) em MQL5 Entendendo a programação orientada a objetos (POO) em MQL5
Como desenvolvedores, precisamos aprender a criar e desenvolver software que possa ser usado de forma repetida e flexível, sem duplicação de código, especialmente quando lidamos com diferentes objetos que têm comportamentos distintos. Isso pode ser facilmente alcançado usando métodos e princípios de programação orientada a objetos. Neste artigo, apresentaremos os fundamentos da programação orientada a objetos em MQL5.
Desenvolvimento de um indicador Heiken Ashi personalizado usando MQL5 Desenvolvimento de um indicador Heiken Ashi personalizado usando MQL5
Neste artigo, aprenderemos a criar nosso próprio indicador usando MQL5 com base em nossas preferências, que será usado no MetaTrader 5 para interpretar gráficos ou como parte de Expert Advisors.