English Русский 中文 Español Deutsch 日本語
Modelagem 3D em MQL5

Modelagem 3D em MQL5

MetaTrader 5Exemplos | 22 fevereiro 2017, 08:50
1 475 0
Sergey Pavlov
Sergey Pavlov

A modelagem 3D permite explorar os complexos processos e fenômenos de interesse, a fim de prever seus resultados.

No que diz respeito aos mercados financeiros, a modelagem 3D é usada, por exemplo, para a representação tridimensional de séries temporais. As séries temporais são um sistema dinâmico em que os valores de uma variável aleatória chegam de forma consistente, isto é, de forma contínua ou em intervalos (ticks, barras, fractais, etc.). Neste artigo vamos considerar a visualização da representação tridimensional das séries temporais, tais como TimeSeries e indicadores (ver fig. 1).

Fig. 1. Exemplos de representação tridimensional de séries temporais.

Fig. 1. Exemplos de representação tridimensional de séries temporais.

Na tela do monitor, a imagem tridimensional é diferente da bidimensional na medida em que permite a plotagem de uma projeção geométrica do modelo 3D, sobre o plano, usando funções especiais. Para obter imagens tridimensionais, sobre o plano, são necessárias as seguintes etapas:

  • modelagem: criação do modelo matemático tridimensional da série temporal;
  • rendering ou visualização: construção da projeção de acordo com o modelo selecionado;
  • exibição da imagem obtida na tela.

A finalidade do artigo consiste em mostrar os gráficos usuais num espaço tridimensional. Como ainda não existem soluções prontas para a modelagem 3D, em linguagem MQL5, então começamos com os princípios e métodos básicos, a saber, com objetos 3D e sistemas de coordenadas. Talvez muitos leitores serão céticos a esse problema e só darão uma vista de olhos, mas, ainda assim, alguns dos seus algoritmos e métodos podem ser úteis em outras tarefas não relacionadas com a visualização tridimensional.


Objeto gráfico interativo

Em primeiro lugar, vamos começar com os objetos 3D. A rica funcionalidade da linguagem MQL5 permite operar com objetos bidimensionais e criar representações gráficas complexas. Basta adicionar algumas funções e, no terminal MT5, estarão disponíveis os gráficos tridimensionais.

Para começar, listamos os requisitos que devem ser cumpridos ao projetar as classes base dos objetos 3D.

  1. Fácil de usar.
  2. Alta capacidade de sobrevivência.
  3. Autonomia.
  4. Interatividade.

Fácil de usar.

Para os desenvolvedores e usuários, deve ser criado um conjunto mínimo de funções, mas suficiente para a maioria das tarefas dos gráficos tridimensionais.

Alta capacidade de sobrevivência.

O objeto 3D deve ser "imortal" durante todo o tempo de vida do programa da instância criada de classe. Ele deve ser protegido quer contra o apagamento acidental ou deliberado quer contra a exclusão acidental das propriedades básicas.

Autonomia.

O objeto deve ser dotado de "inteligência" e auto-ajustar às condições de mudança (rotação do sistema de coordenadas, alteração dos pontos base de ancoragem, etc.). A partir do fluxo de entrada de eventos, ele deve filtrar as informações necessárias e responder adequadamente.

Interatividade.

A visualização 3D envolve a possibilidade de alterar o ponto-de-vista do modelo tridimensional (a rotação do sistema de coordenadas). Por isso, é necessário criar esta funcionalidade, com ajuda da qual não será exigida a utilização de painéis de controle adicionais ou algo parecido. Estritamente falando, os objetos gráficos na linguagem MQL5 já têm a propriedade de interatividade, quer dizer, eles podem selecionados, movidos, alterados quanto às suas propriedades, etc. Basta melhorar um pouco esta capacidade ao nível da gestão colectiva e interação. Por exemplo, nós mudamos o centro de coordenadas e, assim, todos os objetos relacionados foram reconstruídos por conta própria e, além disso, corretamente.

Se você atender a todos esses requisitos, o objeto 3D virará objeto gráfico interativo (IGO). O objeto gráfico interativo é necessariamente vinculado ao objeto gráfico a partir da linguagem MQL5. Passamos agora à consideração da classe base CIGO dos objetos gráficos interativos.

class CIGO
  {
protected:
   bool              on_event;      // sinalizador de processamento de eventos
   int               m_layer;       // camada que pertence ao IGO
   //---
   double            SetPrice(double def,int prop_modifier=0);
public:
   string            m_name;        // nome do objeto IGO
   double            m_price;       // ponto base de âncora do IGO [preço]
   double            m_angle;       // valor do ângulo de âncora do IGO [grau]
                     CIGO();
                    ~CIGO();
   //---
   virtual     // Método: criar o IGO
   void              Create(string name) {on_event=true;}
   virtual     // Método: redesenhar o IGO
   void              Redraw() {ChartRedraw();}
   virtual     // Método de processamento de evento OnChartEvent
   bool              OnEvent(const int id,         // identificador de evento
                             const long &lparam,   // parâmetro de evento do tipo long
                             const double &dparam, // parâmetro de evento do tipo double
                             const string &sparam, // parâmetro de evento do tipo string
                             int iparamemr=0,      // identificador de evento do IGO
                             double dparametr=0.0);// parâmetro de evento do IGO do tipo double
  };

A classe base contém um mínimo de campos e métodos que, mais tarde, podem ser substituídos ou complementados nas classes herdeiras. Consideremos em detalhe apenas dois métodos de classe, a saber: método virtual OnEvent() do processador de eventos OnChartEvent e método de definição do ponto base de âncora SetPrice(). Prestamos mais atenção neles, uma vez que neles são empregados os principais fundamentos dos objetos gráficos interativos.

Método: processamento de eventos de entrada OnEvent.

Processa os eventos recebidos a partir do terminal de cliente, ao trabalhar com o gráfico. O método responde apenas a 4 eventos padrão: excluir um objeto gráfico, redimensionar, alterar as propriedades do gráfico, arrastar e soltar um objeto gráfico e clicar com o mouse sobre ele. Consideremos cuidadosamente cada um deles.

  1. Excluir um objeto gráfico. Após surgir esse evento, o objeto no gráfico deixa de existir, uma vez que ele é excluído. Mas, como temos que cumprir o requisito de capacidade de sobrevivência, ele deve ser restaurado imediatamente, ou seja, recriado com as mesmas propriedades que tinha antes da remoção. Observe que é o objeto gráfico é excluído, e não a instância de classe CIGO vinculada a ele. A instância de classe continua a existir, é ela quem se lembra de informações sobre o objeto gráfico excluído, dai que ele pode ser facilmente recuperado usando o método Create().
  2. Redimensionar ou alterar as propriedades do gráfico. Há muitos eventos deste tipo, a saber: surgimento de uma nova barra, mudança da escala do gráfico, transição para outro timeframe e outros. A resposta deve ser só uma: redesenhar o objeto tendo em conta a mudança do ambiente, utilizando o método Redraw(). É importante notar que, após a transição para outro timeframe, a instância de classe é inicializada novamente, enquanto ela perde os dados - sobre o objeto gráfico criado - que foram armazenados nos campos de classe, embora o gráfico tivesse permanecido no gráfico. Portanto, os campos da instância da classe são restaurados com os valores das propriedades do objeto gráfico, o que, sem dúvida, aumenta a capacidade de sobrevivência do IGO.
  3. Arrastar e soltar um objeto gráfico. Com base neste evento, é construída a interatividade do objeto gráfico. Quando ele é arrastado, são alterados os pontos base de âncora. Antes de arrastar o objeto, ele deve ser selecionado com um clique duplo. Se tal evento ocorrer, o método retornará true, caso contrário, false. Este valor será útil para nós, quando organizemos o trabalho coletivo de objetos gráficos interativos. Vale a pena notar que se nós precisarmos de um objeto gráfico que não pode ser arrastado, será suficiente, após sua criação, proibir a possibilidade de seleção com um clique duplo.
  4. Clicar com o mouse sobre o objeto gráfico. Se o mouse for pressionado em qualquer outro objeto exceto este, retiramos a seleção do objeto, para evitar que ele se mova acidentalmente. Assim, no gráfico, pode ser selecionado apenas um objeto IGO.
    bool CIGO::OnEvent(const int id,
                       const long &lparam,
                       const double &dparam,
                       const string &sparam,
                       int iparamemr=0,
                       double dparametr=0.0)
      {
       bool res=false;
       if(on_event) // manipulação de eventos habilitada
         {
          // Exclusão do objeto gráfico
          if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DELETE && sparam==m_name)
            {
             Create(m_name);
            }
          // Redimensionamento do gráfico ou alteração das propriedades do gráfico através da caixa de diálogo de propriedades
          if((ENUM_CHART_EVENT)id==CHARTEVENT_CHART_CHANGE)
            {
             Redraw();
            }
          // "Arrastar e soltar" o objeto gráfico
          if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DRAG)
             if(ObjectGetInteger(0,sparam,OBJPROP_SELECTED)==1 && sparam==m_name)
               {
                m_price=ObjectGetDouble(0,m_name,OBJPROP_PRICE);
                Redraw();
                res=true;   // anunciamos que foi alterado o ponto base de âncora
               }
          // clique do mouse sobre outro objeto gráfico exceto este
          if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_CLICK && sparam!=m_name)
            {
             ObjectSetInteger(0,m_name,OBJPROP_SELECTED,0);
             ChartRedraw();
            }
         }
       return(res);
      }

Parâmetros: 

 id

   [in] Identificador de eventos. Existem 9 tipos de eventos que podem ser processados usando este método.

 lparam

   [in] Parâmetro de evento do tipo long.

 dparam

   [in] Parâmetro de evento do tipo double.

 sparam

   [in] Parâmetro de evento do tipo string.

 iparametr

   [in] Identificador de evento personalizado.

 dparametr

   [in] Parâmetro de evento personalizado de tipo double.

Valor a ser retornado:

 Retornará true, se for alterada a coordenada do ponto base de âncora de objeto. Caso contrário, false. 

 

Método: definição da coordenada do ponto base de âncora SetPrice.

Define o valor "price", no sistema de coordenadas "Gráfico", para o campo m_price da instância de classe.

Explicarei o que acontece quando este método solicita a coordenada de ponto de âncora.

  1. Após inicializar a instância da classe, o campo m_price (a coordenada do ponto base de âncora) não contém o valor de entrada, por isso m_price=NULL. A inicialização ocorre quer ao criar uma instância de classe quer ao alternar para outro timeframe no gráfico. No entanto, o objeto gráfico em si pode existir no gráfico. Talvez ele seja um resquício a partir da chamada anterior do programa ou da alternância do timeframe. Consequentemente, atribuímos ao campo m_price o valor da propriedade correspondente do objeto gráfico.
  2. Se o objeto gráfico com o nome m_name não existe, após o primeiro passo, a coordenada do ponto base de âncora ainda não está determinada: m_price=NULL. Neste caso, o valor do campo m_price é atribuído por padrão como def.
double CIGO::SetPrice(double def,int prop_modifier=0)
  {
   if(m_price==NULL)             // se não houver valor de variável
      m_price=ObjectGetDouble(0,m_name,OBJPROP_PRICE,prop_modifier);
   if(m_price==NULL)             // se não houver coordenadas
      m_price=def;               // valor por padrão
   return(m_price);
  }

Parâmetros:

 def

   [in] Valor da variável por padrão.

 prop_modifier

   [in] Modificador da propriedade requisitada do objeto gráfico.

Valor a ser retornado:

 Valor da coordenada do ponto base de âncora.  

Agora vamos conhecer os herdeiros diretos da classe base dos objetos interativos. Acima de tudo, estamos interessados em objetos 3D para criar o ambiente necessário para a modelagem tridimensional.

 

Classe C_OBJ_ARROW_RIGHT_PRICE: objeto "Seta preço adequado"

Descendente direto da classe base CIGO

//+------------------------------------------------------------------+
//| Classe OBJ_ARROW_RIGHT_PRICE: objeto "Seta preço adequando"       |
//+------------------------------------------------------------------+
class C_OBJ_ARROW_RIGHT_PRICE:public CIGO
  {
public:
   virtual     // Método: criar objeto
   void              Create(string name);
   virtual     // Método: redesenhar o objeto
   void              Redraw();
  };
//+------------------------------------------------------------------+
//| Método: criar objeto                                            |
//+------------------------------------------------------------------+
void C_OBJ_ARROW_RIGHT_PRICE::Create(string name)
  {
   m_name=name;
   m_price=SetPrice((ChartGetDouble(0,CHART_PRICE_MAX)+ChartGetDouble(0,CHART_PRICE_MIN))/2);
   ObjectCreate(0,m_name,OBJ_ARROW_RIGHT_PRICE,0,0,0);
   ObjectSetInteger(0,m_name,OBJPROP_SELECTABLE,true);
   ObjectSetInteger(0,m_name,OBJPROP_WIDTH,1);
   ObjectSetInteger(0,m_name,OBJPROP_COLOR,clrISO);
//---
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,0,m_price);
//---
   ChartRedraw();
   on_event=true; // permitimos a manipulação do evento
  }
//+------------------------------------------------------------------+
//| Método: redesenhar o objeto                                       |
//+------------------------------------------------------------------+
void C_OBJ_ARROW_RIGHT_PRICE::Redraw()
  {
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ChartRedraw();
  }

Esta classe é a mais adequada para a organização do centro do sistema tridimensional de coordenadas. A instância de classe está ancorada à barra atual e, na verdade, está sobre o eixo Z. 

 

Classe C_OBJ_TREND: objeto "Linha de tendência" 

Descendente direto da classe base CIGO.

//+------------------------------------------------------------------+
//| Classe OBJ_TREND: objeto "Linha de tendência"                        |
//+------------------------------------------------------------------+
class C_OBJ_TREND:public CIGO
  {
public:
   virtual     // Método: criar objeto
   void              Create(string name);
   virtual     // Método: redesenhar o objeto
   void              Redraw();
  };
//+------------------------------------------------------------------+
//| Método: criar objeto                                            |
//+------------------------------------------------------------------+
void C_OBJ_TREND::Create(string name)
  {
   m_name=name;
   m_price=(ChartGetDouble(0,CHART_PRICE_MAX)+ChartGetDouble(0,CHART_PRICE_MIN))/2;
   ObjectCreate(0,m_name,OBJ_TREND,0,0,0);
   ObjectSetInteger(0,m_name,OBJPROP_COLOR,clrISO);
   ObjectSetInteger(0,m_name,OBJPROP_WIDTH,0);
   ObjectSetInteger(0,m_name,OBJPROP_STYLE,styleISO);
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,0,m_price);
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,1,m_price+1);
   ObjectSetInteger(0,m_name,OBJPROP_TIME,1,T(0));
   ObjectSetInteger(0,m_name,OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,m_name,OBJPROP_RAY_LEFT,true);
   ObjectSetInteger(0,m_name,OBJPROP_BACK,true);
   ObjectSetInteger(0,m_name,OBJPROP_SELECTABLE,false);
//---
   ChartRedraw();
   on_event=true; // permitimos a manipulação de eventos
  }
//+------------------------------------------------------------------+
//| Método: redesenhar o objeto                                       |
//+------------------------------------------------------------------+
void C_OBJ_TREND::Redraw()
  {
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ObjectSetInteger(0,m_name,OBJPROP_TIME,1,T(0));
   ChartRedraw();
  }

Com ajuda dessa classe, é mais lógico criar o eixo Z. As possibilidades são mínimas, mas eles são suficientes para a modelagem 3D.

 

Classe C_OBJ_TRENDBYANGLE: objeto "Linha de tendência pelo ângulo" 

Descendente direto da classe base CIGO.

//+------------------------------------------------------------------+
//| Classe OBJ_TRENDBYANGLE: objeto "Linha de tendência pelo ângulo"         |
//+------------------------------------------------------------------+
class C_OBJ_TRENDBYANGLE:public CIGO
  {
protected:
   int               m_bar;   // número da barra para âncora do segundo ponto base
   //---
   double SetAngle(double def)
     {
      if(m_angle==NULL) // se não houver valor de variável
         m_angle=ObjectGetDouble(0,m_name,OBJPROP_ANGLE);
      if(m_angle==NULL)       // se não houver coordenadas
         m_angle=def;         // valor por padrão
      return(m_angle);
     }
public:
                     C_OBJ_TRENDBYANGLE();
   virtual     // Método: criar objeto
   void              Create(string name,double price,double angle);
   virtual     // Método: redesenhar o objeto
   void              Redraw();
   virtual     // Método de processamento do evento OnChartEvent
   bool              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam,
                             int iparamemr=0,
                             double dparametr=0.0);
  };
//+------------------------------------------------------------------+
//| Construtor                                                      |
//+------------------------------------------------------------------+
C_OBJ_TRENDBYANGLE::C_OBJ_TRENDBYANGLE()
  {
   m_bar=c_bar;
  }
//+------------------------------------------------------------------+
//| Método: criar objeto                                            |
//+------------------------------------------------------------------+
void C_OBJ_TRENDBYANGLE::Create(string name,double price,double angle)
  {
   datetime time=T(0);
   datetime deltaT=T(m_bar)-time;
   m_name=name;
   m_price=SetPrice(price);
   m_angle=SetAngle(angle);
   ObjectCreate(0,m_name,OBJ_TRENDBYANGLE,0,time,m_price,time+deltaT,m_price);
   ObjectSetInteger(0,m_name,OBJPROP_COLOR,clrISO);
   ObjectSetInteger(0,m_name,OBJPROP_WIDTH,0);
   ObjectSetInteger(0,m_name,OBJPROP_STYLE,styleISO);
   ObjectSetInteger(0,m_name,OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,m_name,OBJPROP_RAY_LEFT,true);
   ObjectSetInteger(0,m_name,OBJPROP_BACK,true);
   ObjectSetInteger(0,m_name,OBJPROP_SELECTABLE,true);
//--- alteramos o ângulo de inclinação da linha de tendência; no processo de alteração do ângulo, a coordenada
//--- do ponto da linha é redefinido automaticamente de acordo como o novo valor da barra
   ObjectSetDouble(0,m_name,OBJPROP_ANGLE,m_angle);
   ChartRedraw();
//---
   on_event=true; // permitimos a manipulação de eventos
  }
//+------------------------------------------------------------------+
//| Método: redesenhar o objeto                                       |
//+------------------------------------------------------------------+
void C_OBJ_TRENDBYANGLE::Redraw()
  {
   ObjectSetInteger(0,m_name,OBJPROP_TIME,T(0));
   ObjectSetInteger(0,m_name,OBJPROP_TIME,1,T(0)+T(m_bar)-T(0));
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,m_price);
   ObjectSetDouble(0,m_name,OBJPROP_ANGLE,m_angle);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Método de processamento do evento OnChartEvent                             |
//+------------------------------------------------------------------+
bool C_OBJ_TRENDBYANGLE::OnEvent(const int id,
                                 const long &lparam,
                                 const double &dparam,
                                 const string &sparam,
                                 int iparamemr=0,
                                 double dparametr=0.0)
  {
//---
   bool res=false;
   if(on_event) // manipulação de eventos habilitada
     {
      // Exclusão do objeto gráfico
      if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DELETE && sparam==m_name)
        {
         Create(m_name,m_price,m_angle);
        }
      // Redimensionamento do gráfico ou alteração das propriedades do gráfico através da caixa de diálogo de propriedades
      if((ENUM_CHART_EVENT)id==CHARTEVENT_CHART_CHANGE)
        {
         Redraw();
        }
      // "Arrastar e soltar" o objeto gráfico
      if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DRAG)
        {
         //---
         if(ObjectGetInteger(0,sparam,OBJPROP_SELECTED)==1 && sparam==m_name)
           {
            m_angle=ObjectGetDouble(0,m_name,OBJPROP_ANGLE);
            Create(m_name,m_price,m_angle);
            res=true;   // anunciamos que foi alterado o ponto base de âncora
           }
         if(iparamemr==Event_1)// obtemos uma mensagem sobre a alteração do ponto base
           {
            m_price=dparametr;
            Create(m_name,m_price,m_angle);
           }
         if(iparamemr==Event_2)// recebemos uma mensagem sobre a alteração do ângulo base
           {
            m_angle=dparametr;
            Create(m_name,m_price,m_angle);
           }
        }
      // clique do mouse sobre outro objeto gráfico exceto este
      if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_CLICK && sparam!=m_name)
        {
         ObjectSetInteger(0,m_name,OBJPROP_SELECTED,0);
         ChartRedraw();
        }
     }
   return(res);
  }

Ele foi originalmente projetado a fim de ser utilizado para os eixos X e Y dos herdeiros da classe "linha de tendência", porém, no processo, ficou caro que para esses fins o ideal é a "linha de tendência por ângulo". Após reler a documentação sobre a linguagem MQL5, descobri esta poderosa ferramenta: depois de plotar a linha de tendência por ângulo, e alternar os timeframes ou escalas, ela permanecia enraizada no local, ou seja, o ângulo não era alterado.

Conclusão: releia a documentação e você vai descobrir um monte de funções e ferramentas necessárias. 

 

Memória gráfica (GM)

O tema da memória gráfica já foi abordado no artigo "Distribuições estatísticas em forma de histogramas sem buffers de indicador e matrizes". Agora só vou repetir o princípio fundamental: nas propriedades dos objetos gráficos é possível armazenar as informações necessárias a serem utilizadas noutros objetos ou funções do programa.

Para facilitar a programação e aplicação prática da memória gráfica, foi criada a classe especial:

class CGM
  {
private:
public:
   string            m_name;        // nome do objeto gráfico
                     CGM(){}
                    ~CGM(){}
   void              Create(string name) {m_name=name;}
   // leitura da propriedade OBJPROP_PRICE
   double            R_OBJPROP_PRICE(int prop_modifier=0)
     {
      return(ObjectGetDouble(0,m_name,OBJPROP_PRICE,prop_modifier));
     }
   // leitura da propriedade OBJPROP_TIME
   datetime          R_OBJPROP_TIME(int prop_modifier=0)
     {
      return((datetime)ObjectGetInteger(0,m_name,OBJPROP_TIME,prop_modifier));
     }
   // leitura da propriedade OBJPROP_ANGLE
   double            R_OBJPROP_ANGLE()
     {
      return(ObjectGetDouble(0,m_name,OBJPROP_ANGLE));
     }
   // leitura da propriedade OBJPROP_TEXT
   string            R_OBJPROP_TEXT()
     {
      return(ObjectGetString(0,m_name,OBJPROP_TEXT));
     }
   // retorna o valor do preço para o tempo especificado
   double            R_ValueByTime(datetime time)
     {
      return(ObjectGetValueByTime(0,m_name,time));
     }
   // registro da propriedade OBJPROP_TEXT
   void              W_OBJPROP_TEXT(string text)
     {
      ObjectSetString(0,m_name,OBJPROP_TEXT,text);
     }
  };

Como você pode ver a partir do código, existem métodos de leitura e escrita de propriedades de objetos gráficos. A conveniência dessa classe consiste em que ela está ligada ao objeto gráfico - durante a criação - e permite receber/enviar as informações necessárias para todos os gráficos interativos que estão interessados ​​nestas mensagens. Por exemplo,

  • após criado o objeto 3D A e vinculado à instância da classe da memória gráfica AA;
  • agora é criado o objeto 3D B que pode através de AA obter as informações necessárias sobre o objeto A. Não existe conexão direta entre os objetos AB, mas é através da instância de classe AA que está organizada a transferência de dados de A para B;
  • ao criar o objeto B, é criada a instância de classe da memória gráfica BB. Agora o objeto A tem acesso às informações do objeto necessário B;
  • e assim por diante. O número de objetos pode ser ilimitado, mas cada um deles tem a possibilidade de obter e transferir informações através das instâncias de classe da memória gráfica.

A memória gráfica pode ser metaforicamente representada como um quadro de avisos sobre o qual são colocadas a oferta e a procura de determinadas informações. Assim, os dados são trocados entre os objetos gráficos interativos. Os dados necessários para o funcionamento adequado do objeto 3D são programados na etapa de projeção do modelo 3D. Os objetos do sistema de coordenadas possuem as informações mais valiosas, uma vez que, durante a rotação ou transferência do centro de coordenadas, todo o "pacote" dos objetos 3D deve alterar suas propriedades e sua localização no espaço tridimensional.

Veja como usar a memória gráfica na classe do sistema interativo de coordenadas CUSC.

Sistema de coordenadas

A análise visual tridimensional permite analisar dados num espaço 3D: por exemplo, construir uma imagem tridimensional das sequências de dados de entrada (observações) para uma ou mais das variáveis selecionadas. As variáveis ​​selecionadas são apresentadas ao longo do eixo Y, as observações sequenciais, ao longo da linha X, e os valores das variáveis ​​(para esta observação) são representadas no eixo Z.

Esses gráficos tridimensionais são usados ​​para visualizar as sequências dos valores de várias variáveis.

A principal vantagem das representações tridimensionais perante os gráficos bidimensionais lineares compostos consiste em que, para alguns conjuntos de dados com imagem tridimensional, é mais fácil reconhecer as sequências individuais dos valores. Ao escolher o ângulo de visão adequado com a ajuda de, por exemplo, rotação interativa, as linhas do gráfico não se sobrepõem ou não "ficam umas sobre as outras", como muitas vezes acontece nos gráficos bidimensionais lineares compostos. 

Antes de prosseguir com a modelagem 3D, consideremos os sistemas de coordenadas com os quais será necessário lidar. Os dois sistemas de coordenadas "Gráfico" e "Imagem" foram criados pelos desenvolvedores da linguagem MQL5, nós vamos apenas implementar por conta própria o sistema tridimensional (perspectiva). Assim, considerar qual é a diferença e a finalidade de cada um deles.

Fig. 2. Sistema de coordenadas "Gráfico."

Fig. 2. Sistema de coordenadas "Gráfico."

Sistema de coordenadas "Gráfico" (Fig. 2). Trata-se de um sistema de coordenadas bidimensional para exibir dados de preços, TimeSeries e Indicadores. O eixo horizontal é a escala de tempo (time), da esquerda para a direita, enquanto o eixo vertical é o preço (price) do instrumento financeiro. É neste sistema de coordenadas que trabalha a maioria dos objetos gráficos MQL5. A peculiaridade é que o preço atual e a barra estão localizados no mesmo eixo vertical, e ao surgir uma nova barra, o gráfico é automaticamente deslocado para a esquerda.

Fig. 3. Sistema de coordenadas "Imagem."

Fig. 3. Sistema de coordenadas "Imagem."

Sistema de coordenadas "Imagem" (Fig. 3). Cada ponto na tela corresponde a um pixel. As coordenadas dos pontos são medidas a partir do canto superior esquerdo do gráfico. Ela, como o "Gráfico", é 2D e é usada pelos objetos que não estão vinculados ao TimeSeries. Neste artigo, este sistema de coordenadas não será útil.

Fig. 4. Sistema de coordenadas tridimensional.

Fig. 4. Sistema de coordenadas tridimensional.

Sistema tridimensional de coordenadas (Fig. 4).Neste sistema de coordenadas, os três eixos são perpendiculares uns aos outros. No entanto, no sistema de coordenadas "Imagem", visualmente os ângulos entre eixos XYZ não são iguais a 90 graus. Por exemplo, o eixo X e o eixo Y formam entre si um ângulo de 120 graus. Este ângulo pode ser diferente, mas no artigo iremos aceitá-lo dessa forma.

O eixo Z está ancorado à barra atual e a escala do eixo coincide com a escala "price" no sistema de coordenadas "Gráfico". Isto é bastante conveniente, porque não é necessário criar uma escala separada para o eixo Z, e você pode usar a escala "price', ao fazer isto Z=price.

O eixo X é de direita para a esquerda, ou seja, é oposto à escala "time" no sistema de coordenadas "Gráfico". Neste eixo, vamos adiar o valor da variável Bar; por conseguinte, a projeção de qualquer barra no eixo X coincidirá com seu valor (X=Bar).

O eixo Y é concebido para séries de dados bidimensionais XZ. Por exemplo, você pode deslocar a lina TimeSeries Open. Close. High. Low ao longo deste eixo, e elas estarão dispostas cada uma em seu próprio plano. Se você juntar todos os pontos destas linhas em planos paralelos a o plano YZ, aparecerá a grade de superfície, isto é, o objeto tridimensional do TimeSeries (ver fig. 5). 

Fig. 5. Objeto 3D num sistema de coordenadas tridimensional.

Fig. 5. Objeto 3D num sistema de coordenadas tridimensional.

 

Sistema de coordenadas interativo: Classe CUSC

Descendente direto da classe base CIGO

class CUSC:public CIGO
  {
private:
   datetime          m_prev_bar;
   datetime          m_next_bar;
   //---
   C_OBJ_ARROW_RIGHT_PRICE Centr;   // declaração da instância da classe C_OBJ_ARROW_RIGHT_PRICE
   C_OBJ_TREND       AxisZ;         // declaração da instância da classe C_OBJ_TREND
   C_OBJ_TRENDBYANGLE AxisY,AxisX;  // declaração das instâncias da classe C_OBJ_TRENDBYANGLE
   //---
   CGM               gCentr;        // declaração da instância da classe CGM
   CGM               gAxisY,gAxisX; // declaração das instâncias da classe CGM

public:
                     CUSC();
                    ~CUSC();
   //--- Cálculo de coordenadas Z
   sPointCoordinates Z(double price,   // preço no sistema de coordenadas "Gráfico"
                       int barX,       // deslocamento ao longo do eixo X
                       int barY);      // deslocamento ao longo do eixo Y
   //--- nova barra
   bool on_bar()
     {
      m_next_bar=T(0);
      if(m_next_bar>m_prev_bar)
        {
         m_prev_bar=m_next_bar;
         return(true);
        }
      return(false);
     }
   //---
   virtual     // Método: criar objeto IGO
   void              Create(string name);
   virtual     // Método de processamento do evento OnChartEvent
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Consideremos em detalhe apenas três métodos desta classe: Create(), OnEvent(), Z().

MétodoCreate: criar um sistema de coordenadas tridimensional 

Ele cria o objeto 3D do sistema de coordenadas interativo. A interatividade inclui: rotação do sistema de coordenadas em torno do eixo Z e deslocamento do ponto de origem das coordenadas.

void CUSC::Create(string name)
  {
//--- Centro do sistema de coordenadas tridimensional
   Centr.Create("Axis XYZ");        // criamos o centro do sistema de coordenadas
   gCentr.Create(Centr.m_name);     // criamos o objeto da memória gráfica
   m_price=gCentr.R_OBJPROP_PRICE();
//--- Eixo Z
   AxisZ.Create("Axis Z");          // criamos o eixo Z do sistema de coordenadas
//--- Eixo Y
   AxisY.Create("Axis Y",                 // criamos o eixo Y do sistema de coordenadas
                gCentr.R_OBJPROP_PRICE(), // obtemos o valor a partir do GM
                30);                      // definimos o ângulo de inclinação como de 30 graus
   gAxisY.Create(AxisY.m_name);           // criamos o objeto da memória gráfica
   m_angle=gAxisY.R_OBJPROP_ANGLE();
//--- Eixo X
   AxisX.Create("Axis X",                       // criamos o eixo X do sistema de coordenadas
                gCentr.R_OBJPROP_PRICE(),       // obtemos o valor a partir do GM
                gAxisY.R_OBJPROP_ANGLE()+ISO);  // obtemos o valor a partir do GM e aumentamo-lo para ISO graus
   gAxisX.Create(AxisX.m_name);                 // criamos o objeto da memória gráfica
//---
   ChartRedraw();
   on_event=true; // permitimos a manipulação de eventos
  }

Parâmetros:

 name

   [in] Nome do sistema de coordenadas.

Valor a ser retornado:

 Não há nenhum valor de retorno. Se for bem-sucedido, o sistema de coordenadas interativo será criado.

 

Método OnEvent: processamento das mensagem recebidas

Processa os eventos recebidos a partir do terminal de cliente, ao trabalhar com o gráfico. O método responde apenas a um evento padrão: arrastar e solar o objeto gráfico. Os outros eventos são transmitidos para todas as instâncias de classes criadas durante a programação do sistema de coordenadas.

void CUSC::OnEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   if(on_event) // manipulação de eventos habilitada
     {
      //--- transmissão de eventos OnChartEvent
      AxisZ.OnEvent(id,lparam,dparam,sparam);
      //---
      if(Centr.OnEvent(id,lparam,dparam,sparam))
        {// Arrastar e soltar o objeto gráfico
         AxisY.OnEvent(id,lparam,dparam,sparam,Event_1,gCentr.R_OBJPROP_PRICE());
         AxisX.OnEvent(id,lparam,dparam,sparam,Event_1,gCentr.R_OBJPROP_PRICE());
        }
      else
        {
         if(AxisY.OnEvent(id,lparam,dparam,sparam))
           {// Alteração do ângulo do objeto gráfico
            AxisX.OnEvent(id,lparam,dparam,sparam,Event_2,gAxisY.R_OBJPROP_ANGLE()+ISO);
           }
         else
           {
            if(AxisX.OnEvent(id,lparam,dparam,sparam))
              {// Alteração do ângulo do objeto gráfico
               AxisY.OnEvent(id,lparam,dparam,sparam,Event_2,gAxisX.R_OBJPROP_ANGLE()-ISO);
              }
           }
        }
      ChartRedraw();
      m_price=gCentr.R_OBJPROP_PRICE();
      m_angle=gAxisY.R_OBJPROP_ANGLE();
     }
  }

Parâmetros: 

 id

   [in] Identificador de eventos. Existem 9 tipos de eventos que podem ser processados usando este método.

 lparam

   [in] Parâmetro de evento do tipo long.

 dparam

   [in] Parâmetro de evento do tipo double.

 sparam

   [in] Parâmetro de evento do tipo string.

 iparametr

   [in] Identificador de evento personalizado.

 dparametr

   [in] Parâmetro de evento personalizado de tipo double.

Valor a ser retornado:

 Não há nenhum valor de retorno.

 

Estrutura para obter os valores das coordenadas (sPointCoordinates).

Estrutura para armazenar os valores de coordenadas no sistema de coordenadas "Gráfico". Projetada para obter as coordenadas do ponto 3D. 

struct sPointCoordinates
  {
   datetime          time;    // coordenadas no sistema "Gráfico"
   double            price;   // coordenada no sistema "Gráfico"
  };

A variável do tipo sPointCoordinates permite através de uma chamada da função Z() obter os valores das coordenadas do ponto 3D no sistema de coordenadas "Gráfico."

 

MétodoZ: cálculo da coordenada Z. 

O método calcula a coordenada Z.

sPointCoordinates CUSC::Z(double price,int barX,int barY)
  {
   sPointCoordinates res;
   res.price=0;
   res.time=0;
   double dX,dY;
   dX=0;
   dX=gAxisX.R_ValueByTime(T(barX))-m_price;
   dY=0;
   dY=gAxisY.R_ValueByTime(T(-barY))-m_price;
   res.price=price+dX-dY;
   res.time=T(barX-barY);
   return(res);
  }

Parâmetros: 

 price

   [in] Coordenada Z do objeto 3D.

 barX

   [in] Coordenada X do objeto 3D. O valo é definido em barras.

 barY

   [in] Coordenada Y do objeto 3D. O valo é definido em barras.

Valor a ser retornado:

  Se for bem-sucedido, retornará o valor de uma variável de tipo sPointCoordinates. 

 

Exemplo de criação de uma superfície 3D sob a forma de grade

Como um exemplo, consideremos a construção de uma superfície tridimensional segundo a função:

fun[i][j]=close[i*StepX]-_Point*j*j
Não há significado prático nesta fórmula, uma vez que se trata de uma demonstração bastante simples e bem-sucedida das possibilidades das classes desenvolvidas. O código é suficientemente conciso, fácil de entender e acessível a programadores de vários níveis de formação. Após executar este indicador, no gráfico, obtemos uma grade 3D da superfície da função dada (ver fig. 6). Você pode alterar o ponto de vista, alterando os ângulos dos eixos Y ou X. Ao fazer isto, ocorre, de facto, a rotação do objeto em torno do eixo Z. Ao mover o centro de coordenadas, a cor do modelo é alterada: vermelho — valores dos nodos ao longo do eixo Z acima do ponto central 0, enquanto a cor azul, abaixo do centro de coordenadas.
//--- Declaração da constante
#define  StepX    10    // incremento ao longo do eixo X [bar]
#define  StepY    5     // incremento ao longo do eixo Y [bar]
#define  _X       50    // número de pontos ao longo do eixo X
#define  _Y       15    // número de pontos ao longo do eixo Y
#define  W1       1     // espessura das linhas
#define  W2       3     // espessura das linhas exteriores
//--- Ligamos os arquivos das classes
#include <3D\USC.mqh>
//---
#property indicator_chart_window
//--- número de buffers para cálculo do indicador
#property indicator_buffers 0
//--- número de séries gráficas no indicador
#property indicator_plots   0
//---
CUSC        USC;
double fun[_X][_Y];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   USC.Create("3D");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   ArraySetAsSeries(close,true);
//--- Função da superfície 3D
   for(int i=0;i<_X;i++)
      for(int j=0;j<_Y;j++)
        {
         fun[i][j]=close[i*StepX]-_Point*j*j;
         //fun[i][j]=close[i*StepX]-_Point*j*j*i/7;
        }

////--- linhas X
   for(int i=1;i<_X;i++)
      for(int j=0;j<_Y;j++)
        {
         sPointCoordinates a0=USC.Z(fun[(i-1)][j], // fução
                                    (i-1)*StepX,   // X
                                    -j*StepY);     // Y
         sPointCoordinates a1=USC.Z(fun[i][j],     // função
                                    i*StepX,       // X
                                    -j*StepY);     // Y
         string name="line x "+"x"+(string)i+"y"+(string)+j;
         ObjectCreate(0,name,OBJ_TREND,0,a0.time,a0.price,a1.time,a1.price);
         if(fun[i][j]>USC.m_price && fun[i-1][j]>USC.m_price)
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
         else
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,W1);
         if(j==0 || j==_Y-1)
            ObjectSetInteger(0,name,OBJPROP_WIDTH,W2);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
         ObjectSetInteger(0,name,OBJPROP_BACK,true);
         ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
        }
////--- linhas Y
   for(int i=0;i<_X;i++)
      for(int j=1;j<_Y;j++)
        {
         sPointCoordinates a0=USC.Z(fun[i][j-1],   // função
                                    i*StepX,       // X
                                    -(j-1)*StepY); // Y
         sPointCoordinates a1=USC.Z(fun[i][j],     // função
                                    i*StepX,       // X
                                    -j*StepY);     // Y
         string name="line y "+"x"+(string)i+"y"+(string)+j;
         ObjectCreate(0,name,OBJ_TREND,0,a0.time,a0.price,a1.time,a1.price);
         ObjectSetInteger(0,name,OBJPROP_COLOR,clrGreen);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
         ObjectSetInteger(0,name,OBJPROP_BACK,true);
         ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
        }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   USC.OnEvent(id,lparam,dparam,sparam);
  }

As linhas nos planos paralelos ao plano XZ e YZ são desenhadas separadamente. É possível modificar pelo número de nós em cada um dos eixos e na própria função.

Fig. 6. Exemplo construção de superfície 3D.

Fig. 6. Exemplo de construção de superfície 3D.

Fig. 7. Exemplo de construção 3D do indicador Média Móvel.

Fig. 7. Exemplo de construção do indicador 3D Média Móvel. 

Resposta para a pregunta dos céticos: Para que é que serve a modelagem 3D e como ela pode ajudar durante a negociação?

Claro, o terminal MetaTrader 5 é projetado para negociação nos mercados financeiros. Os traders e os desenvolvedores de sistemas de negociação automatizados estão principalmente interessados ​​em algoritmos de negociação e construção de estratégias rentáveis. Por agora a tendência examinada está numa fase inicial de desenvolvimento e não dará resultados concretos na negociação, porém a modelagem 3D pode ser usada a fim de apresentar suas ideias e estratégias de negociação a potenciais investidores. Além disso, no terminal, são criados modelos 3D dinâmicos que respondem ao fluxo de informações de mercado e são deformados ao logo do tempo (ele reconstroem seu corpo), e isto torna possível criar indicadores 3D.

Deixe-me dar como argumento uma analogia com o Windows: a primeira versão do sistema era desajeitado e lento. Os defensores Norton Commander eram céticos em relação à ideia de interfaces gráficas. E onde está agora Norton Commander?

Conclusão

  1. A modelagem 3D abrange uma tarefa bastante difícil em qualquer ambiente de programação, mas os desenvolvedores da linguagem MQL5 têm trabalhado à frente da curva e fornecido aos usuários recursos bastante poderosos, a fim de implementar a visualização tridimensional, no terminal de negociação.
  2. Até agora têm sido fornecidos os primeiros resultados de programação da biblioteca de classes de objetos 3D. O tópico da modelagem 3D é tão ampla que só um único artigo não basta para cobrir todas suas possibilidades e perspectivas. Com sorte, haverá seguidores que sugerirão formas de desenvolvimento para o campo 3D na negociação e programação em linguagem MQL5.
  3. A ferramenta de modelagem 3D criada pode ter um impacto significativo sobre a criação de um novo campo na análise técnica: indicadores tridimensionais e sua análise.
  4. Há problemas com a velocidade de processamento (renderização), mas, nesta etapa do desenvolvimento da biblioteca, isto ainda não é crítico.
  5. O princípio de construção de objetos 3D, tal como proposto no artigo, é talvez mais apropriado para transferência para OpenCL.

Nota:

Os arquivos da biblioteca devem ser colocados na pasta ..\Include\3D.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2828

Arquivos anexados |
GM.mqh (3.77 KB)
IGO.mqh (29.95 KB)
USC.mqh (14.63 KB)
testIGO.mq5 (9.91 KB)
Interfaces Gráficas X: Os Controles Horário, Lista de Caixas de Seleção e Tabela Ordenada (build 6) Interfaces Gráficas X: Os Controles Horário, Lista de Caixas de Seleção e Tabela Ordenada (build 6)
O desenvolvimento da biblioteca para a criação de interfaces gráficas continua. Os controles Horário e a Lista de Caixas de Seleção serão discutidos neste momento. Além disso, agora a classe CTable fornece a capacidade de classificar os dados em ordem crescente ou decrescente.
ZUP - ZigZag universal com padrões Pesavento. Interface gráfica ZUP - ZigZag universal com padrões Pesavento. Interface gráfica
Nos 10 anos que se passaram desde o lançamento da primeira versão da plataforma ZUP, tem havido muitas mudanças e melhorias. O resultado foi um complemento gráfico exclusivo para MetaTrader 4 que permite analisar fácil e rapidamente informações de mercado. Este artigo descreve como trabalhar com a interface gráfica do usuário da plataforma ZUP.
Interfaces Gráficas X: Gestão avançada de listas e tabelas. Otimização do código (build 7) Interfaces Gráficas X: Gestão avançada de listas e tabelas. Otimização do código (build 7)
O código da biblioteca precisa ser otimizado: ele deve ser mais regularizado, o que é - mais legível e compreensível para estudar. Além disso, nós vamos continuar a desenvolver os controles criados anteriormente: listas, tabelas e barras de rolagem.
Detecção automática de pontos extremos com base numa variação de preço especificado Detecção automática de pontos extremos com base numa variação de preço especificado
A automação com estratégias de negociação que envolvem padrões gráficos, requer a capacidade de procurar pontos extremos nos gráficos para processamento e interpretação. As ferramentas existentes nem sempre fornecem essa capacidade. Os algoritmos descritos no artigo permitem encontrar todos os pontos extremos nos gráficos. As ferramentas discutidas aqui são igualmente eficientes durante os movimentos de tendência e de lateralidade. Os resultados obtidos não são fortemente afetados por um período de tempo selecionado e são apenas definidos por uma escala especifica.