Download MetaTrader 5

Indicadores personalizados e infográficos no CCanvas

14 agosto 2017, 15:19
Alexander Fedosov
0
631

Conteúdo

Introdução

No nosso artigo anterior, nós consideramos os princípios da construção de indicadores gráficos usando os métodos para o desenvolvimento de primitivas simples da classe CCanvas. No entanto, a biblioteca de gráficos personalizados possui capacidades muito mais amplas, por isso sugiro que vejam novos tipos de indicadores com uma implementação estrutural mais complexa. Além disso, nós vamos traçar os tipos de indicadores pseudo-3D e infográficos dinâmicos.

Classe CLineRounded

Neste artigo, eu não vou recriar a estrutura geral das classes básicas para implementar objetos gráficos. Em vez disso, nós usaremos a biblioteca CustomGUI, que foi descrita no artigo anterior. Em particular, a classe CCanvasBase deve ser usada como base. As classes recém desenvolvidas são para complementar a lista no arquivo CustomGUI.mqh.

Para desenvolver um indicador linear simples, nós devemos definir sua estrutura e elementos básicos. A Fig. 1 apresenta os elementos que você pode gerenciar ao usar esse tipo de indicador. Note que eles não são elementos simples. 


Fig. 1. A estrutura básica de um indicador linear simples

O elemento implementado pelos métodos da classe CCanvas são considerados simples (básicos). Por exemplo, apenas o Valor é considerado simples no indicador linear, uma vez que ele é implementado usando o método CCanvas::TextOut(). Os três elementos restantes são semelhantes e consistem principalmente de três figuras básicas — dois círculos preenchidos (implementação CCanvas::FillCircle()) e um retângulo (CCanvas::FillRectangle()).

Crie o arquivo LineRounded.mqh na pasta CustomGUI/Indicators, crie a classe СLineRounded no arquivo recém gerado e atribua a classe CCanvasBase, criada anteriormente, como base para isso.

//+------------------------------------------------------------------+
//|                                                  LineRounded.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| Indicador linear arredondado                                     |
//+------------------------------------------------------------------+
class CLineRounded : public CCanvasBase

Além disso, adicione-o para a lista do arquivo CustomGUI.mqh que oferece acesso rápido a todas as classes da biblioteca. Considerando as classes desenvolvidas anteriormente, seu código será o seguinte:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"
#include "Indicators\LineGraph.mqh"
#include "Indicators\LineRounded.mqh"
//+------------------------------------------------------------------+

A lista completa de propriedades e métodos da classe СLineRounded pode ser estudada no arquivo CustomGUI/Indicators/LineRounded.mqh. Vamos nos concentrar nos métodos para desenvolver e definir o indicador e atualizar os seus valores. Conforme mencionado acima, o indicador deve consistir de elementos compostos. A implementação desses elementos é comentada em blocos separados na listagem abaixo.

//+------------------------------------------------------------------+
//| Cria o indicador                                                 |
//+------------------------------------------------------------------+
void CLineRounded::Create(string name,int x,int y)
  {
//--- Modifica a localização do indicador em relação aos eixos das coordenadas
   x=(x<m_x_size/2)?m_x_size/2:x;
   y=(y<m_y_size/2)?m_y_size/2:y;
   Name(name);
   X(x);
   Y(y);
   XSize(m_x_size);
   YSize(m_y_size);
   if(!CreateCanvas())
      Print("Erro. Não é possível criar o Canvas.");
//--- Estrutura
   m_canvas.FillRectangle(YSize()/2,0,XSize()-YSize()/2,YSize(),ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(YSize()/2,YSize()/2-1,YSize()/2,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(XSize()-YSize()/2-1,YSize()/2,YSize()/2,ColorToARGB(m_border_color,m_transparency));
//--- Cenário
   m_canvas.FillRectangle(YSize()/2,m_border,XSize()-YSize()/2,YSize()-m_border,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillCircle(XSize()-YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.Update();
  }

Como você pode ver, a Estrutura e o Cenário são implementados usando os dois métodos. Não esqueça que esses objetos são camadas e são desenhados se sobrepondo. Portanto, primeiro, desenhamos a estrutura seguido pelo cenário menor, escala do indicador e o valor numérico.

Considere o método CLineRounded::NewValue()

//+------------------------------------------------------------------+
//| Define e atualiza o valor do indicador                           |
//+------------------------------------------------------------------+
void CLineRounded::NewValue(double value,double maxvalue,int digits=0)
  {
   int v;
   v=int((XSize()-YSize()/2)/maxvalue*value);

//--- Cenário
   m_canvas.FillRectangle(YSize()/2,m_border,XSize()-YSize()/2,YSize()-m_border,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillCircle(XSize()-YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency));
//--- Escala
   if(v>=YSize()/2 && v<=(XSize()-YSize()/2))
     {
      m_canvas.FillRectangle(YSize()/2,m_border,v,YSize()-m_border,ColorToARGB(m_scale_color,m_transparency));
      m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency));
      m_canvas.FillCircle(v,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency));
     }
   else if(v>(XSize()-YSize()/2))
     {
      m_canvas.FillRectangle(YSize()/2,m_border,XSize()-YSize()/2,YSize()-m_border,ColorToARGB(m_scale_color,m_transparency));
      m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency));
      m_canvas.FillCircle(XSize()-YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency));
     }
   else if(v>0 && v<YSize()/2)
      m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency));

//--- Valor numérico
   m_canvas.FontSizeSet(m_font_size);
   m_canvas.TextOut(XSize()/2,YSize()/2,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }
//+------------------------------------------------------------------+

Como podemos ver, o bloco de implementação do cenário está presente novamente no início do método. Por quê? Lembre-se do desenho camada por camada dos elementos por meio da aplicação consistente dos métodos correspondentes. Em outras palavras, primeiro, mostramos o fundo da escala seguido pelo indicador com o comprimento definido pelo valor máximo do parâmetro. Quando o novo valor do indicador chega, a camada de contexto é reescrita primeiro seguido da escala com um novo comprimento.

Desse modo, a criação e a passagem do valor para o indicador é implementado de forma muito simples. Na lista abaixo, você pode ver os dois indicadores com pequenas configurações que diferem apenas naquele que tem um valor máximo duas vezes maior que o do outro. 

//+------------------------------------------------------------------+
//|                                                          001.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CLineRounded ind1,ind2;
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ind1.XSizeInd(350);
   ind1.YSizeInd(30);
   ind1.Create("line1",300,100);
//---
   ind2.XSizeInd(350);
   ind2.YSizeInd(30);
   ind2.ScaleColor(clrFireBrick);
   ind2.Create("line2",300,150);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
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[])
  {
//---
   ind1.NewValue(50,100,2);
   ind2.NewValue(50,200,2);
//--- valor de retorno de prev_calculated para a próxima chamada
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do indicador personalizado            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind1.Delete();
   ind2.Delete();
  }
//+------------------------------------------------------------------+

A Fig. 2 mostra claramente que o comprimento da escala do indicador possui um valor apropriado no caso de valores semelhantes e escalas diferentes. O valor de 50 e máximo (100 para o primeiro e 200 para o segundo) são selecionados para que o resultado da exibição possa ser avaliado visualmente.

Fig. 2. Exemplo do indicador linear arredondado


Classe CHexagon

Nós podemos usar uma série de métodos para traçar um indicador na forma de um hexágono regular usando as primitivas definidas pela classe CCanvas. Eles incluem a construção de uma poli linha com um preenchimento posterior na área e uma "montagem" de seis triângulos equiláteros, como fatias de pizza. No entanto, nosso objetivo é a simplicidade máxima. Portanto, nós vamos construir um hexágono regular usando três primitivas — um retângulo e dois triângulos isósceles.


Fig. 3. Uma estrutura hexagonal regular

O hexágono direito é inserido na tela quadrada. Lembre-se das propriedades da figura:

  • O lado do hexágono regular é igual ao raio do círculo circunscrito em torno dele (no nosso caso, esta é a metade do lado da tela). Esse recurso é necessário ao traçar um retângulo.
  • O ângulo hexagonal é de 120 graus, portanto, os ângulos nas bases dos triângulos isósceles são de 30 graus cada. Estes dados são necessários para definir a altura do triângulo e encontrar as coordenadas dos pontos de base dos triângulos ao usar o método CCanvas::FillTriangle().

A estrutura básica do próprio indicador é bastante simples e, além do hexágono, inclui dois objetos de texto - valor numérico e descrição (Fig. 4).


Fig. 4. A estrutura básica do indicador hexagonal

Crie o arquivo Hexagon.mqh na pasta CustomGUI/Indicators, crie a classe Cexagon no arquivo recém gerado e atribua a classe CCanvasBase, criada anteriormente, como base para isso. Inclua ele a lista geral:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"
#include "Indicators\LineGraph.mqh"
#include "Indicators\LineRounded.mqh"
#include "Indicators\Hexagon.mqh"
//+------------------------------------------------------------------+

A lista completa de propriedades e métodos pode ser encontrada no arquivo criado acima. Vamos destacar os métodos responsáveis ​​pela visualização do indicador. 

//+------------------------------------------------------------------+
//| Cria o indicador                                                 |
//+------------------------------------------------------------------+
void CHexagon::Create(string name,int x,int y)
  {
   int a,r;
   r=m_size;
//--- Modifica a localização do indicador em relação aos eixos das coordenadas 
   x=(x<r/2)?r/2:x;
   y=(y<r/2)?r/2:y;
   Name(name);
   X(x);
   Y(y);
   XSize(r);
   YSize(r);
   if(!CreateCanvas())
      Print("Erro. Não é possível criar o Canvas.");
//--- Calcula a altura dos triângulos
   a=int(YSize()/2*MathSin(30*M_PI/180));
//--- Forma hexagonal
   m_canvas.FillTriangle(XSize()/2,0,0,a,XSize(),a,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillRectangle(0,a,XSize(),a+YSize()/2,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillTriangle(0,a+YSize()/2,XSize(),a+YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency));
//--- Descrição e valores numéricos
   m_canvas.FontNameSet("Trebuchet MS");
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r/2,r/2,"-",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r/2,r/2+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.Update();
  }

No método Create() , o valor dos triângulos isósceles é atribuído a uma variável. Na verdade, desta forma, nós encontramos as coordenadas dos pontos do triângulo ao longo do eixo das ordenadas (Y). O método de transferência e atualização do valor numérico é diferente apenas no fato de que o objeto de texto responsável pelo desenho de um valor numérico recebe o valor do argumento:

//+------------------------------------------------------------------+
//| Define e atualiza o valor do indicador                           |
//+------------------------------------------------------------------+
void CHexagon::NewValue(double value,int digits=2)
  {
   int a,r;
   r=m_size;
//--- Calcula a altura dos triângulos
   a=int(YSize()/2*MathSin(30*M_PI/180));
//--- Forma hexagonal
   m_canvas.FillTriangle(XSize()/2,0,0,a,XSize(),a,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillRectangle(0,a,XSize(),a+YSize()/2,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.FillTriangle(0,a+YSize()/2,XSize(),a+YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency));
//--- Texto
   m_canvas.FontNameSet("Trebuchet MS");
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r/2,r/2,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r/2,r/2+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.Update();
  }
//+------------------------------------------------------------------+

Vamos implementar um pequeno conjunto de indicadores hexagonais de exemplo:

//+------------------------------------------------------------------+
//|                                                          002.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CHexagon ind1,ind2,ind3,ind4,ind5,ind6,ind7,ind8,ind9,ind10,ind11,ind12,ind13,ind14;
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
//---   
   ind1.BgColor(clrWhite);
   ind1.Size(110);
   ind1.Create("hex1",300,200);
   ind2.BgColor(C'60,170,220');
   ind2.Create("hex2",300,200);
//---
   ind3.BgColor(clrWhite);
   ind3.Size(110);
   ind3.Create("hex3",300,80);
   ind4.BgColor(C'230,80,25');
   ind4.Create("hex4",300,80);
//---
   ind5.BgColor(clrWhite);
   ind5.Size(110);
   ind5.Create("hex5",300,320);
   ind6.BgColor(C'150,190,15');
   ind6.Create("hex6",300,320);
//---
   ind7.BgColor(clrWhite);
   ind7.Size(110);
   ind7.Create("hex7",180,140);
   ind8.BgColor(C'10,115,185');
   ind8.Create("hex8",180,140);
//---
   ind9.BgColor(clrWhite);
   ind9.Size(110);
   ind9.Create("hex9",420,140);
   ind10.BgColor(C'20,150,150');
   ind10.Create("hex10",420,140);
//---
   ind11.BgColor(clrWhite);
   ind11.Size(110);
   ind11.Create("hex11",420,280);
   ind12.BgColor(C'225,0,80');
   ind12.Create("hex12",420,280);
//---
   ind13.BgColor(clrWhite);
   ind13.Size(110);
   ind13.Create("hex13",180,280);
   ind14.BgColor(C'240,145,5');
   ind14.Create("hex14",180,280);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do indicador personalizado            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind1.Delete();
   ind2.Delete();
   ind3.Delete();
   ind4.Delete();
   ind5.Delete();
   ind6.Delete();
   ind7.Delete();
   ind8.Delete();
   ind9.Delete();
   ind10.Delete();
   ind11.Delete();
   ind12.Delete();
   ind13.Delete();
   ind14.Delete();
  }
//+------------------------------------------------------------------+

O resultado é exibido na Fig. 5. Este modelo básico é muito fácil de configurar e mudar. 

Fig. 5. A implementação de exemplo do conjunto de indicadores usando a classe CHexagon


Classe CPetal

A implementação do indicador em forma de pétala requer 2-3 primitivas da classe CCanvas de métodos. Estes são o círculo preenchido (o método FillCircle()) e, dependendo da forma, 1-2 triângulos preenchidos (FillTriangle()). A estrutura e o conjunto de elementos são mostrados na Fig. 6.


Fig. 6. A estrutura básica do indicador de pétala

Como podemos ver, dois triângulos são usados ​​aqui, mas vamos incluir vários tipos de formas na classe usando a enumeração 'enum'. Vamos considerar esses tipos com mais detalhes:

enum ENUM_POSITION
  {
   TOPRIGHT=1,
   TOPLEFT=2,
   BOTTOMRIGHT=3,
   BOTTOMLEFT=4,
   BOTHRIGHT=5,
   BOTHLEFT=6
  };
  • TOPRIGHT — consiste em um círculo e triângulo com seu vértice visível direcionado ao canto superior direito.
  • TOPLEFT — consiste em um círculo e triângulo com seu vértice visível direcionado ao canto superior esquerdo.
  • BOTTOMRIGHT — consiste em um círculo e triângulo com seu vértice visível direcionado ao canto inferior direito.
  • BOTTOMLEFT — consiste em um círculo e triângulo com seu vértice visível direcionado ao canto inferior esquerdo.
  • BOTHRIGHT — consiste de um círculo e dois triângulos. O triângulo superior está localizado no canto superior direito.
  • BOTHLEFT— consiste de um círculo e dois triângulos. O triângulo superior está localizado no canto superior esquerdo.


Fig. 7. Formas do indicador de pétala

Vamos começar a implementar. Crie o arquivo Petal.mqh na pasta CustomGUI/Indicators, crie a classe СPetal no arquivo recém gerado e atribua a classe CCanvasBase, criada anteriormente, como base para isso. Além disso, inclua ele a lista, que agora se parece com o seguinte:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"
#include "Indicators\LineGraph.mqh"
#include "Indicators\LineRounded.mqh"
#include "Indicators\Hexagon.mqh"
#include "Indicators\Petal.mqh"
//+------------------------------------------------------------------+

Nós não vamos nos ocupar das propriedades padrão. Consideremos os métodos de criação de um indicador e atualização de seus valores. No método Create() , após a construção do círculo preenchido, os triângulos preenchidos são desenhados nas posições especificadas usando a enumeração ENUM_POSITION, dependendo da propriedade "tipo da forma" anteriormente considerada:

//+------------------------------------------------------------------+
//| Cria o indicador                                                 |
//+------------------------------------------------------------------+
void CPetal::Create(string name,int x,int y)
  {
   int r=m_radius;
//--- Modifica a localização do indicador em relação ao raio
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("Erro. Não é possível criar o Canvas.");
//---
   m_canvas.FillCircle(r,r,r,ColorToARGB(m_bg_color,m_transparency));
   if(m_orientation==TOPRIGHT)
      m_canvas.FillTriangle(XSize()/2,0,XSize(),0,XSize(),YSize()/2,ColorToARGB(m_bg_color,m_transparency));
   else if(m_orientation==TOPLEFT)
      m_canvas.FillTriangle(0,0,XSize()/2,0,0,YSize()/2,ColorToARGB(m_bg_color,m_transparency));
   else if(m_orientation==BOTTOMRIGHT)
      m_canvas.FillTriangle(XSize(),YSize(),XSize(),YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency));
   else if(m_orientation==BOTTOMLEFT)
      m_canvas.FillTriangle(0,YSize(),0,YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency));
   else if(m_orientation==BOTHRIGHT)
     {
      m_canvas.FillTriangle(XSize()/2,0,XSize(),0,XSize(),YSize()/2,ColorToARGB(m_bg_color,m_transparency));
      m_canvas.FillTriangle(0,YSize(),0,YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency));
     }
   else if(m_orientation==BOTHLEFT)
     {
      m_canvas.FillTriangle(0,0,XSize()/2,0,0,YSize()/2,ColorToARGB(m_bg_color,m_transparency));
      m_canvas.FillTriangle(XSize(),YSize(),XSize(),YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency));
     }
//--- Descrição e valores numéricos
   m_canvas.FontNameSet("Trebuchet MS");
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,"-",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

Como exemplo, vamos desenvolver um modelo para criar um indicador em forma de borboleta.

//+------------------------------------------------------------------+
//|                                                          003.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CPetal ind1,ind2,ind3,ind4;
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
   int b=2;
//---   
   ind1.BgColor(C'230,80,80');
   ind1.Orientation(BOTHLEFT);
   ind1.Create("petal1",200-b,100-b);

   ind2.BgColor(C'35,170,190');
   ind2.Orientation(BOTHRIGHT);
   ind2.Create("petal2",300+b,100-b);

   ind3.BgColor(C'245,155,70');
   ind3.Orientation(BOTHRIGHT);
   ind3.Create("petal3",200-b,200+b);

   ind4.BgColor(C'90,200,130');
   ind4.Orientation(BOTHLEFT);
   ind4.Create("petal4",300+b,200+b);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
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[])
  {
//---

//--- valor de retorno de prev_calculated para a próxima chamada
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do indicador personalizado            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind1.Delete();
   ind2.Delete();
   ind3.Delete();
   ind4.Delete();
  }
//+------------------------------------------------------------------+


Fig. 8. Usando o indicador de pétala

Classe CHistogram

A classe de histograma é baseada na classe CLIneGraph, ou mais precisamente — a estrutura geral de construção dos eixos de coordenadas, escalas do eixo e seus valores. Portanto, não há necessidade de descrever este estágio. Vamos detalhar mais sobre os tipos de histogramas e a implementação usando as primitivas da classe CCanvas. Antes de passar para a implementação do indicador de histograma, vamos definir os tipos de formas.

enum ENUM_TYPE_HISTOGRAM
  {
   SIMPLE=1,
   TRIANGLE=2,
   RECTANGLE=3
  };
  • SIMPLE — uma forma simples baseada no histograma em forma de triângulo (Fig. 10) com a sua altura fixada como um valor numérico no gráfico.
  • TRIANGLE — histograma na forma de um triângulo do tipo de um pseudo-volume (Fig. 11).
  • RECTANGLE — tipo de histograma padrão na forma de colunas (Fig. 12).


Fig. 10. Histograma do tipo SIMPLE


Fig. 11. Histograma do tipo TRIANGLE

Fig. 12. Histograma do tipo RECTANGLE

Crie o arquivo Histogram.mqh na pasta CustomGUI/Indicators, crie a classe СHistogram no arquivo recém gerado e atribua a classe CCanvasBase, criada anteriormente, como base para isso. Além disso, inclua ele a lista no arquivo CustomGUI.mqh. A maioria dos métodos e propriedades da classe são semelhantes aos da classe CLineGraph, incluindo o método Create(). Portanto, vamos dar uma olhada apenas nas diferenças fundamentais, ou seja, no método SetArrayValue().

//+------------------------------------------------------------------+
//| Define o array de entrada                                        |
//+------------------------------------------------------------------+
void CHistogram::SetArrayValue(double &data[])
  {
   int x,y,y1,y2;
//--- Cria a estrutura e o cenário do gráfico
   m_canvas.FillRectangle(0,0,XSize(),YSize(),ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(1,1,XSize()-2,YSize()-2,ColorToARGB(m_bg_color,m_transparency));
//--- Cria os eixos e o fundo do gráfico
   m_canvas.FillRectangle(m_gap-1,m_gap-1,XSize()-m_gap+1,YSize()-m_gap+1,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(m_gap,m_gap,XSize()-m_gap,YSize()-m_gap,ColorToARGB(m_bg_graph_color,m_transparency));
//--- Se o valor máximo do array de dados exceder a margem superior Y, o eixo é dimensionado.
   if(data[ArrayMaximum(data)]>m_y_max)
      m_y_max=data[ArrayMaximum(data)];
//--- Cria as escalas dos eixos e seus valores
   VerticalScale(m_y_min,m_y_max,m_num_grid);
   HorizontalScale(ArraySize(data));
   ArrayResize(m_label_value,ArraySize(data));
//--- Cria o histograma com base no array de dados
   for(int i=0;i<ArraySize(data);i++)
     {
      if(data[i]>0)
        {
         x=int(m_x[i]+(m_x[i+1]-m_x[i])/2);
         y=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]);
         y1=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]*0.3);
         y2=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]*0.1);
         y=(y<m_gap)?m_gap:y;
         if(m_type==SIMPLE)
            m_canvas.FillTriangle(m_x[i],YSize()-m_gap,m_x[i+1],YSize()-m_gap,x,y,ColorToARGB(m_graph_color1,m_transparency));
         else if(m_type==TRIANGLE)
           {
            m_canvas.FillTriangle(m_x[i],YSize()-m_gap,x,y,x,y1,ColorToARGB(m_graph_color1,m_transparency));
            m_canvas.FillTriangle(m_x[i],YSize()-m_gap,m_x[i+1],YSize()-m_gap,x,y1,ColorToARGB(m_graph_color2,m_transparency));
            m_canvas.FillTriangle(x,y,x,y1,m_x[i+1],YSize()-m_gap,ColorToARGB(m_graph_color3,m_transparency));
           }
         else if(m_type==RECTANGLE)
           {
            int x1,x2;
            x1=int(m_x[i]+(m_x[i+1]-m_x[i])/3);
            x2=int(m_x[i]+2*(m_x[i+1]-m_x[i])/3);
            m_canvas.FillRectangle(x1,y,x2,YSize()-m_gap,ColorToARGB(m_graph_color1,m_transparency));
           }
         //--- Descrição e valores numéricos
         m_canvas.FontNameSet("Trebuchet MS");
         m_canvas.FontSizeSet(m_value_font_size);
         m_canvas.TextOut(x,y2,DoubleToString(data[i],2),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
         m_canvas.TextOut(x,y-5,m_label_value[i],ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
        }
     }
   m_canvas.Update();
  }

A diferença fundamental é que o tipo de histograma ENUM_TYPE_HISTOGRAM descrito acima é considerado no bloco de desenho do histograma pelo array de dados especificado. O método de exibição visual de três indicadores RSI com diferentes períodos foi implementado como um exemplo de uso.

//+------------------------------------------------------------------+
//|                                                          004.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//+------------------------------------------------------------------+
//|  Entradas do indicator                                           |
//+------------------------------------------------------------------+

input int                  RSIPeriod1=10;        //Período do indicador1
input int                  RSIPeriod2=14;        //Período do indicador2
input int                  RSIPeriod3=18;        //Período do indicador3
input ENUM_TYPE_HISTOGRAM  Type=RECTANGLE;       //Tipo do Histograma
//---
CHistogram ind;
int InpInd_Handle1,InpInd_Handle2,InpInd_Handle3;
double rsi1[],rsi2[],rsi3[],ex[3];
string descr[3];
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- obtém o manipulador do indicador
   InpInd_Handle1=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod1,PRICE_CLOSE);
   InpInd_Handle2=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod2,PRICE_CLOSE);
   InpInd_Handle3=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod3,PRICE_CLOSE);
   if(InpInd_Handle1==INVALID_HANDLE ||
      InpInd_Handle2==INVALID_HANDLE ||
      InpInd_Handle3==INVALID_HANDLE
      )
     {
      Print("Falha ao obter o manipulador do indicador");
      return(INIT_FAILED);
     }
//---
   descr[0]="RSI("+IntegerToString(RSIPeriod1)+")";
   descr[1]="RSI("+IntegerToString(RSIPeriod2)+")";
   descr[2]="RSI("+IntegerToString(RSIPeriod3)+")";
   ind.NumGrid(10);
   ind.YMax(100);
   ind.Type(Type);
   ind.Create("rsi",350,250);
   ind.SetArrayLabel(descr);
//---
   ArraySetAsSeries(rsi1,true);
   ArraySetAsSeries(rsi2,true);
   ArraySetAsSeries(rsi3,true);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
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[])
  {
//---
   if(CopyBuffer(InpInd_Handle1,0,0,1,rsi1)<=0   ||
      CopyBuffer(InpInd_Handle2,0,0,1,rsi2)<=0   ||
      CopyBuffer(InpInd_Handle3,0,0,1,rsi3)<=0
      )
      return(0);
   ex[0]=rsi1[0];
   ex[1]=rsi2[0];
   ex[2]=rsi3[0];
   ind.SetArrayValue(ex);
//--- valor de retorno de prev_calculated para a próxima chamada
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do indicador personalizado            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+


Fig. 13. Resultado do indicador na forma de um histograma de três períodos RSI


Classe CPyramid

Nós examinamos as classes e os princípios de construção de formas complexas usando primitivas. Ao descrever a classe para a construção de indicadores do tipo histograma, eu mencionei a possibilidade de desenho de objetos pseudo-3D (Fig. 13) por meio da seleção de cores. No entanto, a pirâmide não é uma figura plana. Portanto, nós temos que usar sua projeção isométrica em um determinado sistema de coordenadas bidimensional. A estrutura básica não contém muitos elementos (Fig.14), mas o método da projeção e visualização da pirâmide é a parte principal da implementação da classe.


Fig. 14. Estrutura básica da classe CPyramid

Crie o arquivo Pyramid.mqh na pasta CustomGUI/Indicators, crie a classe СPyramid no arquivo recém gerado e atribua a classe CCanvasBase, criada anteriormente, como base para isso. Além disso, inclua ele a lista no arquivo CustomGUI.mqh. Agora, a lista geral de classes incluídas é a seguinte:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"
#include "Indicators\LineGraph.mqh"
#include "Indicators\LineRounded.mqh"
#include "Indicators\Hexagon.mqh"
#include "Indicators\Petal.mqh"
#include "Indicators\Histogram.mqh"
#include "Indicators\Pyramid.mqh"
//+------------------------------------------------------------------+

Você pode encontrar a lista completa de propriedades e métodos na própria classe. Vamos nos concentrar nos principais. Antes de considerar o básico dos métodos Create() e NewValue(), abordaremos os métodos privados utilizados nele. 

//+------------------------------------------------------------------+
//| Escreve o coeficiente da equação linear entre dois pontos no array |
//+------------------------------------------------------------------+
void CPyramid::Equation(double x1,double y1,double x2,double y2,double &arr[])
  {
   ArrayResize(arr,2);
   arr[0]=(y1-y2)/(x1-x2);
   arr[1]=y2-arr[0]*x2;
  }

O método Equation() é usado para encontrar a equação linear entre dois pontos. Em particular, ele define os coeficientes K e B para a equação geral Y = kx + b a partir da equação linear convencional entre dois pontos:

Este método é usado para definir posteriormente as equações de linha reta por pontos especificados para encontrar os pontos de cruzamento das extremidades AB e AC da pirâmide e as linhas retas paralelas às linhas da base dela. As coordenadas dos pontos mostrados na Fig. 15 são necessárias para a construção de triângulos semelhantes às faces laterais da pirâmide. Por sua vez, são partes do nosso indicador.

Fig. 15. Pontos de cruzamento das extremidades da pirâmide e as linhas retas paralelas às linhas da base da pirâmide

Conhecendo os coeficientes das duas linhas retas, vamos calcular as coordenadas do ponto de cruzamento usando um sistema de equações. O método Cross() é usado para isso:

//+---------------------------------------------------------------------------------+
//| Escreve as coordenadas do ponto de cruzamento de duas linhas retas pelo seus coeficientes |
//+---------------------------------------------------------------------------------+
void CPyramid::Cross(double k1,double b1,double k2,double b2,double &arr[])
  {
   double x,y;
   ArrayResize(arr,2);
   y=(b1*k2-b2*k1)/(k2-k1);
   x=(y-b2)/k2;
   arr[0]=x;
   arr[1]=y;
  }
//+------------------------------------------------------------------+

Agora, que nós conhecemos as funções usadas no desenho da pirâmide, nó podemos focar totalmente no método Create().

//+------------------------------------------------------------------+
//| Cria o indicador                                                 |
//+------------------------------------------------------------------+
void CPyramid::Create(string name,int x,int y)
  {
   int size=m_size;
//--- Modifica a localização do indicador em relação ao seu tamanho
   x=(x<size/2)?size/2:x;
   y=(y<size/2)?size/2:y;
   Name(name);
   X(x);
   Y(y);
   XSize(size);
   YSize(size);
   if(!CreateCanvas())
      Print("Erro. Não é possível criar o Canvas.");
//--- Descrição e valores numéricos
   m_canvas.FontNameSet("Trebuchet MS");
   m_canvas.FontSizeSet(m_font_size);
   m_canvas.FontAngleSet(200);
//--- Encontra a equação da linha reta de AB
   double x1,x2,y1,y2;
   x1=XSize()/2;y1=0;
   x2=0;y2=4*YSize()/5;
   Equation(x1,y1,x2,y2,m_line1);
//--- Constrói as seções à esquerda do indicador
   for(int i=5;i>0;i--)
     {
      //--- Define as coordenadas dos dois pontos 
      y1=i*YSize()/5;
      y2=(i-1)*YSize()/5;
      Equation(x1,y1,x2,y2,m_line2);
      //--- Define as coordenadas do ponto de cruzamento entre a borda AB e uma linha reta paralela ao lado da base da pirâmide
      Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross);
      m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1],m_transparency));
      //--- Traça as marcas da escala
      m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_scale_color,m_transparency));
     }
//--- Encontra a equação da linha reta AC
   x1=XSize()/2;y1=0;
   x2=XSize();y2=4*YSize()/5;
   Equation(x1,y1,x2,y2,m_line1);
//--- Constrói as seções à direita do indicador
   for(int i=5;i>0;i--)
     {
      //--- Define as coordenadas dos dois pontos 
      y1=i*YSize()/5;
      y2=(i-1)*YSize()/5;
      Equation(x1,y1,x2,y2,m_line2);
      //--- Define as coordenadas do ponto de cruzamento entre a borda AC e uma linha reta paralela ao lado da base da pirâmide
      Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross);
      m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1]));
      //--- Traça as marcas da escala
      m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_scale_color,m_transparency));
      //--- Traça os valores da escala
      m_canvas.TextOut(int(x1+2),int(y2+YSize()/6)," - ",ColorToARGB(m_label_color,m_transparency),TA_LEFT|TA_VCENTER);
     }
   m_canvas.LineVertical(XSize()/2,0,YSize(),ColorToARGB(m_scale_color,m_transparency));
   m_canvas.Update();
  }

O algoritmo de construção da pirâmide é o seguinte:

  • Construa o lado esquerdo da pirâmide. Defina as coordenadas dos pontos A e B e use-os para encontrar a equação de linha reta.
  • Então, encontre as equações de linha reta paralelas aos lados da base da pirâmide e o ponto de seu cruzamento com a borda AB consequentemente no ciclo.
  • Use os pontos obtidos para criar seções e marcas de escala (divisões).
  • Construa o lado direito da pirâmide de forma semelhante.
  • Além das seções e marcas de escala, adicione os valores da escala do lado direito.
  • Separação vertical de duas seções.

O método de configuração e atualização de dados apresenta os dois argumentos: os valores atuais passados ​​e sua máxima. A essência do método: Defina os valores de limiar com base no valor máximo definido. Ao passá-los, cada seção muda de cor. Quando exceder o valor da limiar, a seção recebe uma cor especificada. Quando o valor cruza para baixo, a seção recebe o conjunto de cores como inativo. 

//+------------------------------------------------------------------+
//| Define e atualiza o valor do indicador                           |
//+------------------------------------------------------------------+
void CPyramid::NewValue(double value,double maxvalue)
  {
//---
   double s;
   color col;
//--- Encontra a equação da linha reta AB
   double x1,x2,y1,y2;
   x1=XSize()/2;y1=0;
   x2=0;y2=4*YSize()/5;
   Equation(x1,y1,x2,y2,m_line1);
//--- Constrói as seções à esquerda do indicador
   for(int i=5;i>0;i--)
     {
      //--- Encontra os valores da escala
      s=maxvalue-(i-1)*maxvalue/5;
      //--- Define a cor da seção
      col=(value>=s)?m_section_color[i-1]:m_bg_color;
      //--- Define as coordenadas dos dois pontos 
      y1=i*YSize()/5;
      y2=(i-1)*YSize()/5;
      Equation(x1,y1,x2,y2,m_line2);
      //--- Define as coordenadas do ponto de cruzamento entre a borda AB e uma linha reta paralela ao lado da base da pirâmide
      Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross);
      m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1],m_transparency));
      m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(col,m_transparency));
      //--- Traça as marcas da escala
      m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(clrWhite));
     }
//--- Encontra a equação da linha reta AC
   x1=XSize()/2;y1=0;
   x2=XSize();y2=4*YSize()/5;
   Equation(x1,y1,x2,y2,m_line1);
//--- Constrói as seções à direita do indicador
   for(int i=5;i>0;i--)
     {
      //--- Encontra os valores da escala
      s=maxvalue-(i-1)*maxvalue/5;
      //--- Define a cor da seção
      col=(value>=s)?m_section_color[i-1]:m_bg_color;
      //--- Define as coordenadas dos dois pontos 
      y1=i*YSize()/5;
      y2=(i-1)*YSize()/5;
      Equation(x1,y1,x2,y2,m_line2);
      //--- Define as coordenadas do ponto de cruzamento entre a borda AC e uma linha reta paralela ao lado da base da pirâmide
      Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross);
      m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1],m_transparency));
      m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(col,m_transparency));
      //--- Define as marcas da escala
      m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_scale_color,m_transparency));
      //--- Traça os valores da escala
      m_canvas.TextOut(int(x1+2),int(y2+YSize()/6),DoubleToString(s,0),ColorToARGB(m_label_color,m_transparency),TA_LEFT|TA_VCENTER);
     }
   m_canvas.LineVertical(XSize()/2,0,YSize(),ColorToARGB(m_scale_color,m_transparency));
   m_canvas.Update();
  }

Apesar da implementação mais complexa da renderização do indicador, sua criação, configuração e aplicação não são mais difíceis do que as anteriores. Vamos aplicá-lo para exibir os valores ADX padrão como um pequeno exemplo. Dependendo da localização do indicador, adicione o indicador circular na parte superior esquerda para comparação e visibilidade.

//+------------------------------------------------------------------+
//|                                                          005.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//+------------------------------------------------------------------+
//|  Entradas do indicator                                           |
//+------------------------------------------------------------------+
input int               period=10;                                //ADX Period
input double            maxval=50;                                //Max value
//---
CPyramid ind1;
CCircleSimple ind2;
int InpInd_Handle;
double adx[];
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- obtém o manipulador do indicador
   InpInd_Handle=iADX(Symbol(),PERIOD_CURRENT,period);
   if(InpInd_Handle==INVALID_HANDLE)
     {
      Print("Falha ao obter o manipulador do indicador");
      return(INIT_FAILED);
     }
   ArraySetAsSeries(adx,true);
//---
   ind1.Size(250);
   ind1.Create("pyramid",200,0);
//---
   ind2.FontSize(ind1.FontSize());
   ind2.LabelSize(ind1.FontSize());
   ind2.Label("ADX");
   ind2.Radius(30);
   ind2.Create("label",ind1.X()-ind1.Size()/3,ind1.Y()-ind1.Size()/4);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
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[])
  {
//---
   if(CopyBuffer(InpInd_Handle,0,0,2,adx)<=0)
      return(0);
   ind1.NewValue(adx[0],maxval);
   ind2.NewValue(adx[0]);
//--- valor de retorno de prev_calculated para a próxima chamada
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do indicador personalizado            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind1.Delete();
   ind2.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

Como podemos ver na Fig. 16, o indicador circular exibe o valor acima do terceiro limiar de 30, portanto, três seções são coloridas no valor máximo atual, que neste caso é ajustado para 50.

Fig. 16. Utilizando um indicador em forma de pirâmide

Conclusão

A implementação das classes baseadas em CCanvas prova que as capacidades da biblioteca são bastante extensas em termos de gráficos. Os tipos e formas de exibir esses tipos de indicadores são limitados apenas pela imaginação. A criação e a estruturação de tais indicadores na forma de uma biblioteca de classes incluídas não requerem conhecimento ou esforço especial.

O arquivo anexado contém todos os arquivos aplicados no artigo, que estão localizados nas pastas apropriadas. Para uma correta operação, coloque a pasta MQL5 ao diretório raiz do terminal a partir do arquivo

Arquivos dos programas usados no artigo:

#
 Nome
Tipo
Descrição
1
CanvasBase.mqh Biblioteca  Classe gráfica base personalizada
2
CustomGUI.mqh Biblioteca  O arquivo contendo uma lista de todas as classes da biblioteca inclusas 
3 CircleArc.mqh Biblioteca  Contém a classe do indicador CCircleArc
4 CircleSection.mqh Biblioteca  Contém a classe do indicador CCircleSection
5 CircleSimple.mqh Biblioteca  Contém a classe do indicador CCircleSimle
LineGraph.mqh Biblioteca  Contém a classe do gráfico linear CLineGraph
7 LineRounded.mqh  Biblioteca   Contém a classe do indicador linear arredondado CLineRounded
 8 Hexagon.mqh  Biblioteca   Contém a classe do indicador hexagonal CHexagon
 9 Petal.mqh  Biblioteca   Contém a classe do indicador de pétala CPetal
 10 Histogram.mqh  Biblioteca   Contém a classe do indicador de histograma CHistogram
 11 Pyramid.mqh  Biblioteca   Contém a classe do indicador de pirâmide CPyramid
12 CLineRounded.mq5 Indicador  Exemplo de implementação da classe CLineRounded
13 CHexagon.mq5 Indicador  Exemplo de implementação da classe CHexagon
14 CPetal.mq5 Indicador  Exemplo de implementação da classe CPetal
 15 CHistogram.mq5 Indicador  Exemplo de implementação da classe CHistogram
 16 CPyramid.mq5  Indicador   Exemplo de implementação da classe CPyramid

Traduzido do russo por MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/3298

Arquivos anexados |
MQL5.zip (29.88 KB)
Expert Advisor multiplataforma: Sinais Expert Advisor multiplataforma: Sinais

No artigo, são discutidas as classes CSignal e CSignals, que serão usadas em Expert Advisors multiplataforma. Além disso, serão examinadas as diferenças entre MQL4 e MQL5 quanto à organização de dados necessários para avaliar os sinais de negociação obtidos. O resultado será um código compatível com os compiladores das duas versões.

Expert Advisor multiplataforma: Gerenciador de ordens Expert Advisor multiplataforma: Gerenciador de ordens

No artigo, é discutida a criação de um gerenciador de ordens para um EA de negociação multiplataforma. O gerenciador de ordens é responsável pela abertura e fechamento de ordens ou posições efetuadas pelo Expert Advisor, bem como pela manutenção de seus registros independentes, e estará disponível para ambas as versões do terminal.

Expert Advisor multiplataforma: Controle de capital Expert Advisor multiplataforma: Controle de capital

Neste artigo, falaremos sobre a implementação do controle de capital num EA multiplataforma. As classes de controle de capital são responsáveis ​​pelo cálculo do tamanho do lote, que o EA usa para entrar na próxima transação.

Métodos de ordenação e sua visualização usando a MQL5 Métodos de ordenação e sua visualização usando a MQL5

A biblioteca Graphic.mqh foi projetada para trabalhar com gráficos na MQL5. O artigo fornece um exemplo de sua aplicação prática e explica a ideia de ordenação. O conceito geral de ordenação é descrito aqui, pois cada tipo de ordenação já possui pelo menos um artigo separado, enquanto que alguns tipos de ordenação são objetos de estudos detalhados.