English Русский 中文 Español Deutsch 日本語
Distribuições estatísticas em forma de histogramas sem buffers de indicador e matrizes

Distribuições estatísticas em forma de histogramas sem buffers de indicador e matrizes

MetaTrader 5Exemplos | 16 janeiro 2017, 09:03
3 209 0
Sergey Pavlov
Sergey Pavlov

Introdução

Um histograma é uma ferramenta que permite avaliar visualmente a distribuição dos dados estatísticos agrupados pela frequência de seu aparecimento num determinado intervalo (predefinido).

A plotagem de histogramas e seu uso - na analise de dados estatísticos - são um tema suficientemente estudado ao qual já foram dedicados numerosos artigos [1, 2, 3, 4, 5, 6, 7] e de acordo com o qual já foram criados uma boa quantidade de exemplos no CodeBase [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. No entanto, os algoritmos utilizados baseiam-se na aplicação de buffers de indicador ou matrizes. Neste artigo, examina-se a possibilidade de construção de distribuições estatísticas de diferentes características de mercado, sem cálculos complicados, classificação, seleção, etc. Para fazer isso, usamos a memória "gráfica", isto é, a seção onde as propriedades de objetos gráficos são armazenadas. A questão é que, ao plotar histogramas personalizados, seja como for, usam-se objetos gráficos, então por que razão não empregar seus recursos "escondidos" na potência máxima e deixar de aproveitar a rica funcionalidade disponível?

O objetivo deste artigo é oferecer soluções simples para tarefas estatísticas padrão. A ênfase será colocada na visualização das distribuições estatísticas e suas principais características. Não podemos nos distrair na interpretação dos histogramas e sua utilidade.

Princípio de plotagem de histogramas

O histograma é um gráfico de barras de frequência. Num dos eixos são traçados os valores da variável, enquanto noutro, a frequência de ocorrência (surgimento, aparecimento, etc.). A altura de cada barra representa a frequência (número) dos valores pertencentes ao respectivo intervalo igual à largura da coluna. A maioria destes gráficos são apresentados na horizontal, isto é, os valores das variáveis estão localizados no eixo horizontal, e as frequências, no eixo vertical. Os histogramas utilizados para exibir as informações pesquisadas permitem dar expressividade e visibilidade aos dados estatísticos, facilitar não apenas a percepção, mas também, em muitos casos, a análise.

Neste artigo, vamos nos concentrar nos histogramas verticais de séries de variação: os valores de preço das características estudadas estarão localizados no eixo vertical em ordem crescente, enquanto a frequência, no eixo horizontal (Fig. 1). Os dados de preços, que entram ao terminal, são agrupados e alocados na barra atual e podem ser exibidos com respeito ao eixo da esquerda, direita ou em ambos os lados simultaneamente.

Fig. 1. Gráfico de colunas vertical de distribuição de preços Bid e Ask. 

Fig. 1. Gráfico de colunas vertical de distribuição de preços Bid e Ask.

Consideremos uma tarefa específica:

  • plotar o histograma de distribuição de preços Bid e Ask; 
  • colocamos os dados de preços Ask à direita da barra atual, enquanto Bid, à esquerda;
  • ao entrar um novo tick, calcularemos a frequência para cada valor de preço entrante, ou seja, o intervalo do histograma é igual ao tamanho do ponto do instrumento atual.

Agora vamos complicar a condição: não se devem usar os buffers de indicador, matrizes ou estruturas.

Como resolver este problema? 

Em primeiro lugar, é necessário pensar onde salvar as frequências acumuladas para cada coluna do histograma. Mesmo na fig. 1. é evidente que estas colunas podem ser numerosas, e a primeira coisa que vem à mente é usar uma matriz e, eventualmente, uma que seja dinâmica, uma vez que com antecedência é desconhecido o possível intervalo de preços (número de colunas) para o período selecionado no gráfico de preço. Mas, de acordo com as condições da tarefa, é proibido.

Em segundo lugar, é necessário resolver o problema de pesquisa e classificação, isto é: onde e como procurar dados para recalcular e redesenhar o histograma.

Acontece que, para os desenvolvedores da linguagem MQL5, há muito tempo foi criado um recurso, para resolver o problema, que, inclusive, é suficientemente poderoso. Sim, caro leitor, propõe-se a usar os recursos "ocultos" (não-óbvios) do grupo de funções de objetos gráficos. Cada objeto tem propriedades que consistem em variáveis que são criados em conjunto com o objeto e servem para armazenar muitos parâmetros diferentes. Algumas propriedades podem ser usadas fora de seu propósito original sem afetar sua funcionalidade. Chamaremos essas propriedades de memória "gráfica". Em outras palavras, se queremos salvar uma variável e, se necessário, obter seu valor, então devemos criar um objeto gráfico e atribuir o valor da variável a uma propriedade específica.

As propriedade dos objetos gráficos tratam-se de um análogo das variáveis globais, só que são melhores. As variáveis globais existem no terminal de cliente 4 semanas a partir do momento que são acessadas pela última vez, quer dizer, após esse período são automaticamente excluídas. O memória gráfica existe até que o objeto gráfico não é excluído. Imagine as oportunidades que isto nos abre? 

Quais são as propriedades dos objetos gráficos que podem ser usadas na memória gráfica?

Ao criar qualquer objeto gráfico, a ele deve ser atribuído u nome exclusivo, nomeadamente, uma cadeia de caracteres. A cadeia de caracteres pode consistir de subcadeias, enquanto a subcadeia pode conter tipos de dados básicos formatados: completos, lógicos, com ponto flutuante, cor, data e tempo. Portanto, a propriedade OBJPROP_NAME é apropriada para armazenar variáveis, no entanto, principalmente para leitura de dados.

A propriedade a seguir, que pode ser usada, é a descrição do objeto OBJPROP_TEXT. Ela também se trata de uma cadeia de caracteres, cujas possibilidades são superiores em relação à propriedade anterior. Neste campo de propriedade, é possível a leitura e a escrita de variáveis.

Qualquer objeto gráfico tem coordenadas, isto é: preço OBJPROP_PRICE e tempo OBJPROP_TIME. Eles também podem ser usados na memória gráfica.

Voltamos à nossa tarefa. Vamos armazenar a frequência na propriedade OBJPROP_TEXT, enquanto os valores de preços Bid e Ask, na propriedade OBJPROP_NAME. A seguir, é possível ver o código da função que cria objetos e armazena frequências:

void DrawHistogram(bool draw,     // plotamos o histograma à direita ou à esquerda
                   string h_name, // prefixo exclusivo para o nome do objeto
                   double price,  // preço (parâmetro a ser analisado)
                   datetime time, // associação do histograma com a barra atual
                   int span,      // número de bits do parâmetro a ser analisado
                   int swin=0)    // janela do histograma
  {
   double y=NormalizeDouble(price,span);
   string pfx=DoubleToString(y,span);
// se draw=true, plotamos o histograma à direita
   if(draw)
     {
      string name="+ "+h_name+pfx;                   // nome do objeto: prefixo+preço
      ObjectCreate(0,name,OBJ_TREND,swin,time,y);    // criamos o objeto
      ObjectSetInteger(0,name,OBJPROP_COLOR,color_R_active); // definimos a cor do objeto
      ObjSet;                                                // macro para reduzir o código
      if(StringFind(ObjectGetString(0,name,OBJPROP_TEXT),"*",0)<0)
        {// se o preço obtido ficou dentro da amostragem pela primeira vez
         ObjectSetString(0,name,OBJPROP_TEXT,"*1");          // a frequência para esse preço é igual a 1
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time+hsize); // definimos a coordenada do tempo
        }
      else
        {/ / se o preço obtido ficou dentro da amostragem não pela primeira vez
         string str=ObjectGetString(0,name,OBJPROP_TEXT);    // obtemos o valor da propriedade
         string strint=StringSubstr(str,1);                  // selecionamos a subcadeia
         long n=StringToInteger(strint);                     // obtemos a frequência para futuros cálculos
         n++;                                                // incrementamos o valor para 1
         ObjectSetString(0,name,OBJPROP_TEXT,"*"+(string)n); // registramos na propriedade o novo valor
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time+hsize*n);//definimos a coordenada do tempo
        }
     }
// se draw=false, plotamos o histograma à esquerda
   if(!draw)
     {
      string name="- "+h_name+pfx;
      ObjectCreate(0,name,OBJ_TREND,swin,time,y);
      ObjectSetInteger(0,name,OBJPROP_COLOR,color_L_active);
      ObjSet;
      if(StringFind(ObjectGetString(0,name,OBJPROP_TEXT),"*",0)<0)
        {
         ObjectSetString(0,name,OBJPROP_TEXT,"*1");
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time-hsize);
        }
      else
        {
         string str=ObjectGetString(0,name,OBJPROP_TEXT);
         string strint=StringSubstr(str,1);
         long n=StringToInteger(strint);
         n++;
         ObjectSetString(0,name,OBJPROP_TEXT,"*"+(string)n);
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time-hsize*n);
        }
     }
   ChartRedraw();
  }

a função consiste em dois blocos semelhantes: separadamente para Bid e Ask. Por isso, os comentários exitem apenas no primeiro bloco.

Na mente do leitor atento, certamente surgiu uma questão significativa, isto é: por que devemos dobrar o preço no nome do objeto quando ele está disponível na propriedade OBJPROP_PRICE? Qual é o sentido deste trabalho?

Neste ponto, vamos considerar mais detalhadamente o caso. Ao surgir o novo preço, é necessário definir em qual coluna aparecerá, e nela, consequentemente, é preciso aumentar o valor da frequência. Se se utilizasse a coordenada do preço diretamente a partir de sua propriedade, seria preciso selecionar novamente todos os objetos gráficos, solicitar o valor desta propriedade, comparar com o preço que entrou e, só então, armazenar o novo valor de frequência na coluna necessária. Comparar números reais de tipo double é mais uma "emboscada". Em geral, um algoritmo muito ineficiente. Outra coisa, é quando, ao receber um novo valor de preço, imediatamente registramos a frequência alterada no lugar certo. Como? O que acontece é que - ao criar um novo objeto gráfico com o mesmo nome de qualquer um que já exista - o novo objeto não será criado e os campos de propriedades não serão esvaziados. Em outras palavras, podemos criar objetos sem nos preocupar com que eles serão duplicados. Não há necessidade de fazer verificações extras, porque não serão criados cópias e clones. Já a funcionalidade do terminal e dos objetos gráficos toma conta disto. Só resta determinar se pela primeira vez o preço foi incluído na amostragem. Para fazer isso, na função DrawHistogram() a ser examinada, usa-se o prefixo "asterisco" (*) na propriedade do objeto OBJPROP_TEXT. Se não existe o "asterisco" é porque o preço foi incluído pela primeira vez. Na verdade, quando criamos um objeto em vão, este campo está em branco. Nas execuções subsequentes, lá será armazenado o valor de frequência com o prefixo especificado.

O próximo passo consiste em fazer o deslocamento dos histogramas para a direita. Ao surgir uma nova barra, o gráfico é deslocado para a esquerda, no entanto, é preciso que o histograma sempre seja exibido na barra atual, isto él, ele deve ser deslocado na direção aposta. Examinemos como fazer isso:

//--- Deslocamento de diagramas para a barra nova
   if(time[0]>prevTimeBar) // definimos o surgimento da nova barra
     {
      prevTimeBar=time[0];
      // fazemos a pesquisa detalhada de todos os objetos gráficos
      for(int obj=ObjectsTotal(0,-1,-1)-1;obj>=0;obj--)
        {
         string obj_name=ObjectName(0,obj,-1,-1);               // obtemos o nome do objeto encontrado
         if(obj_name[0]==R)                                     // procuramos o prefixo do elemento de histograma
           {                                                    // se encontramos o elemento de histograma 
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // definimos o novo valor de coordenadas
                             0,time[0]);                        // para "0" do ponto de ancoragem
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);// lemos a variável a partir da propriedade do objeto
            string strint=StringSubstr(str,1);                  // selecionamos a subcadeia a partir da variável obtida
            long n=StringToInteger(strint);                     // convertemos a cadeia de caracteres para uma variável long
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // calculamos o novo valor de coordenada
                             1,time[0]+hsize*n);                // para "1" do ponto de ancoragem
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,
                             color_R_passive);                  // alteramos a cor do elemento deslocado do histograma
           }
         if(obj_name[0]==L)
           {
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,0,time[0]);
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);
            string strint=StringSubstr(str,1);
            long n=StringToInteger(strint);
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,1,time[0]-hsize*n);
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,color_L_passive);
           }
        }
      ChartRedraw();
     }

Isso, na verdade, é tudo. O histograma de preços Bid e Ask são plotados no gráfico, e o programa não utiliza nenhuma matriz ou buffer de indicador. Todo o código pode ser encontrado no arquivo anexo ao artigo.


No vídeo, é mostrada uma solução semelhante à tarefa com uso da memória gráfica. O próprio código pode ser localizado no CodeBase: indicador "Histogram bid and ask prices".

Exemplos de plotagem de histogramas na janela principal

A tecnologia de funcionalidade de programação com o uso de memória gráfica já está examinada, agora construamos vários histogramas para indicadores padrão. O princípio de plotagem é semelhante à examinada, no entanto, em vez de preços Bid e Ask, vamos utilizar os valores dos indicadores na barra atual.

     1. Indicador iMA. Pegamos dois indicadores com diferentes períodos de média e plotamos o histograma. O código tem a seguinte aparência:

input int               period_MA1=20;       // Averaging period of iMA1 
input int               period_MA2=14;       // Averaging period of iMA2 
//---- indicator buffers
double      MA1[];
double      MA2[];
//---- handles for indicators
int         iMA1_handle;
int         iMA2_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iMA1_handle=iMA(_Symbol,_Period,period_MA1,0,MODE_SMA,PRICE_CLOSE);
   iMA2_handle=iMA(_Symbol,_Period,period_MA2,0,MODE_SMA,PRICE_CLOSE);
   ArraySetAsSeries(MA1,true);
   ArraySetAsSeries(MA2,true);
   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(time,true);
   
   CopyBuffer(iMA1_handle,0,0,1,MA1);
   CopyBuffer(iMA2_handle,0,0,1,MA2);

   DrawHistogram(true,"iMA("+(string)period_MA1+")=",MA1[0],time[0],_Digits);
   DrawHistogram(false,"iMA("+(string)period_MA2+")=",MA2[0],time[0],_Digits);

//--- Deslocamento de diagramas para a barra nova
   if(time[0]>prevTimeBar) // definimos o surgimento da nova barra
     {
      prevTimeBar=time[0];
      // fazemos a pesquisa detalhada de todos os objetos gráficos
      for(int obj=ObjectsTotal(0,-1,-1)-1;obj>=0;obj--)
        {
         string obj_name=ObjectName(0,obj,-1,-1);               // obtemos o nome do objeto encontrado
         if(obj_name[0]==R)                                     // procuramos o prefixo do elemento de histograma
           {                                                    // se encontramos o elemento de histograma 
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // definimos o novo valor de coordenadas
                             0,time[0]);                        // para "0" do ponto de ancoragem
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);// lemos a variável a partir da propriedade do objeto
            string strint=StringSubstr(str,1);                  // selecionamos a subcadeia a partir da variável obtida
            long n=StringToInteger(strint);                     // convertemos a cadeia de caracteres para uma variável long
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // calculamos o novo valor de coordenada
                             1,time[0]+hsize*n);                // para "1" do ponto de ancoragem
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,
                             color_R_passive);                  // alteramos a cor do elemento deslocado do histograma
           }
         if(obj_name[0]==L)
           {
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,0,time[0]);
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);
            string strint=StringSubstr(str,1);
            long n=StringToInteger(strint);
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,1,time[0]-hsize*n);
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,color_L_passive);
           }
        }
      ChartRedraw();
     }
   return(rates_total);
  }

Fig. 2. Histograma de dois indicadores iMA.

Fig. 2. Histograma de dois indicadores iMA. 

     2. Indicador iBands. Para este indicador plotamos os histograma para as bordas superior (UPPER_BAND) e inferior (LOWER_BAND). Um código mais curto é mostrado abaixo.

input int   period_Bands=14;           // Averaging period of iBands 
//---- indicator buffers
double      UPPER[];
double      LOWER[];
//---- handles for indicators
int         iBands_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iBands_handle=iBands(_Symbol,_Period,period_Bands,0,5.00,PRICE_CLOSE);
   ArraySetAsSeries(UPPER,true);
   ArraySetAsSeries(LOWER,true);
   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(time,true);
   
   CopyBuffer(iBands_handle,1,0,1,UPPER);
   CopyBuffer(iBands_handle,2,0,1,LOWER);

   DrawHistogram(true,"iBands(UPPER)=",UPPER[0],time[0],_Digits);
   DrawHistogram(false,"iBands(LOWER)=",LOWER[0],time[0],_Digits);

//--- Deslocamento do diagrama na nova barra
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

Fig. 3. Histograma bandas de Bollinger do indicador iBands.

Fig. 3. Histograma bandas de Bollinger do indicador iBands. 

Se usarmos todos os 3 buffers de indicador (0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND), obteremos curiosos histogramas para o indicador de Bollinger. Colocamos o histograma das bandas superior e inferior à direita a partir da barra atual, enquanto o histograma da linha principal, à direita da barra de zero. O resultado é mostrado na figura a seguir:

Fig. 4. Histograma bandas de Bollinger em 3 buffers de indicador.

Fig. 4. Histograma bandas de Bollinger em 3 buffers de indicador.  

A seguir, você vai encontrar o código para esta variante de histogramas: Observe que, no lado direito, são mostrados dois histogramas: para as bordas superior e inferior do indicador. Além disso, visualmente eles são percebidos como um único gráfico.

input int   period_Bands=20;       // Averaging period of iBands 
//---- indicator buffers
double      BASE[];
double      UPPER[];
double      LOWER[];
//---- handles for indicators
int         iBands_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iBands_handle=iBands(_Symbol,_Period,period_Bands,0,10.00,PRICE_CLOSE);
   ArraySetAsSeries(BASE,true);
   ArraySetAsSeries(UPPER,true);
   ArraySetAsSeries(LOWER,true);
   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(time,true);
   
   CopyBuffer(iBands_handle,0,0,1,BASE);
   CopyBuffer(iBands_handle,1,0,1,UPPER);
   CopyBuffer(iBands_handle,2,0,1,LOWER);

   DrawHistogram(true,"iBands(UPPER)=",UPPER[0],time[0],_Digits);
   DrawHistogram(true,"iBands(LOWER)=",LOWER[0],time[0],_Digits);
   DrawHistogram(false,"iBands(LOWER)=",BASE[0],time[0],_Digits);

//--- Deslocamento do diagrama na nova barra
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

Até agora foram considerados exemplos das séries de variação cujo intervalo de valores para a o valor procurado (variantes) era igual ao tamanho do ponto do instrumento atual _Point. Em outras palavras, todos os valores do preço eram plotados numa coluna separada do histograma.

Vale a pena ressaltar quão elegante e imperceptivelmente - para o leitor - foram classificadas as séries de variação pelas funcionalidades embutidas no terminal. Para isso, nem foi preciso escrever outra cadeia de caracteres de código.

Agora vamos ver como alterar o intervalo da distribuição, no qual haverá alguns valores de variante, usando apenas uma variável e o poder da funcionalidade dos objetos gráficos. Na função considerada anteriormente DrawHistogram(), encontra-se o parâmetro de entrada span, isto é, o número de bits do parâmetro a ser procurado. Então, variando o número de bits, nós podemos alterar o intervalo para o cálculo das frequências do histograma. Como, no nome do objeto gráfico, é incluído o preço, a seleção ocorre automaticamente na fase de criação e ajuste das propriedades dos objetos. Veja como é fácil ordenar e agrupar usando a memória gráfica sem programação.

Fig. 5. Histograma de preços Bid e Ask com aumento do intervalo das colunas.

Fig. 5. Histograma de preços Bid e Ask com aumento do intervalo das colunas. 

Assim, construir o histograma na janela principal do gráfico resultou muito simples. Agora vamos ver como fazer gráficos semelhantes nas janelas adicionais do gráfico.

Histogramas em janelas adicionais

O princípio de criação de distribuições estatísticas em janelas adicionais é o mesmo como na janela principal. A diferença é que os valores das variáveis ​​estudadas podem ter valores negativos e um número de bits arbitrário. Assim, na janela principal, a escala de preço tem um número de bits conhecido antecipadamente que pode ser obtido usando a variável _Digits. Nas janelas adicionais, as variáveis ​​podem ter valores positivos e negativos. Além disso, o número de dígitos significativos após o ponto decimal pode ser qualquer coisa. Por isso, esta característica deve ser tida em conta ao definir os parâmetros de entrada da funçãoDrawHistogram() para plotagem de histogramas, e, além disso, deve se especificar o número da janela adicional. Por padrão, na função, é usada a janela principal que é igual a zero.

Exemplos de plotagem de histogramas nas janelas adicionais

A melhor maneira de assimilar informações é através de exemplos. Então, vamos a isso. Vamos considerar alguns exemplos de histogramas em janelas adicionais para indicadores técnicos padrão.

     1. Indicador iChaikin.

Fig. 6. Histograma do indicador Chaikin Oscillator.

Fig. 6. Histograma do indicador Chaikin Oscillator.

Código reduzido para este indicador:

input int   period_fast=3;       // Averaging period fast of iChaikin 
input int   period_slow=10;      // Averaging period of slow iChaikin 
//---- indicator buffers
double      CHO[];
//---- handles for indicators
int         iChaikin_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iChaikin_handle=iChaikin(_Symbol,_Period,period_fast,period_slow,MODE_SMA,VOLUME_TICK);
   ArraySetAsSeries(CHO,true);
   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(time,true);
   
   CopyBuffer(iChaikin_handle,0,0,2,CHO);

   DrawHistogram(true,"iChaikin=",CHO[0],time[0],0,1);
   
//--- Deslocamento do diagrama na nova barra
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

     2. Indicador iCCI.

Fig. 7. Histograma do indicador Commodity Channel Index.

Fig. 7. Histograma do indicador Commodity Channel Index.

Código reduzido:

input int   period_CCI=14;             // Averaging period of iCCI 
//---- indicator buffers
double      CCI[];
//---- handles for indicators
int         iCCI_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iCCI_handle=iCCI(_Symbol,_Period,period_CCI,PRICE_CLOSE);
   ArraySetAsSeries(CCI,true);
   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(time,true);
   
   CopyBuffer(iCCI_handle,0,0,2,CCI);

   DrawHistogram(true,"iCCI=",CCI[0],time[0],0,1);
   
//--- Deslocamento do diagrama na nova barra
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

     3. Indicadores: iEnvelopesiATR, iMACD. Combinação de histogramas para três indicadores, cada um dos quais será exibido na janela.

Fig. 8. Histogramas de 3 indicadores: iEnvelopes, iATR e iMACD.

Fig. 8. Histogramas de 3 indicadores: iEnvelopes, iATR e iMACD. 

Código reduzido - que implementa um conjunto de histogramas - ilustrado na fig. 8.

input int   period_Envelopes=14;       // Averaging period of iEnvelopes 
input int   period_ATR=14;             // Averaging period of iATR 
input int   period_fast=12;            // Averaging period fast of iMACD 
input int   period_slow=26;            // Averaging period of slow iMACD 
//---- indicator buffers
double      UPPER[];
double      LOWER[];
double      ATR[];
double      MAIN[];
double      SIGNAL[];
//---- handles for indicators
int         iEnvelopes_handle;
int         iATR_handle;
int         iMACD_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iEnvelopes_handle=iEnvelopes(_Symbol,_Period,period_Envelopes,0,MODE_SMA,PRICE_CLOSE,0.1);
   iATR_handle=iATR(_Symbol,_Period,period_ATR);
   iMACD_handle=iMACD(_Symbol,_Period,period_fast,period_slow,9,PRICE_CLOSE);
   ArraySetAsSeries(UPPER,true);
   ArraySetAsSeries(LOWER,true);
   ArraySetAsSeries(ATR,true);
   ArraySetAsSeries(MAIN,true);
   ArraySetAsSeries(SIGNAL,true);
   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(time,true);
   
   CopyBuffer(iEnvelopes_handle,0,0,1,UPPER);
   CopyBuffer(iEnvelopes_handle,1,0,1,LOWER);
   CopyBuffer(iATR_handle,0,0,1,ATR);
   CopyBuffer(iMACD_handle,0,0,1,MAIN);
   CopyBuffer(iMACD_handle,1,0,1,SIGNAL);

   DrawHistogram(true,"iEnvelopes.UPPER=",UPPER[0],time[0],_Digits);
   DrawHistogram(false,"iEnvelopes.LOWER=",LOWER[0],time[0],_Digits);
   DrawHistogram(true,"iATR=",ATR[0],time[0],_Digits+1,1);
   DrawHistogram(true,"iMACD.SIGNAL=",SIGNAL[0],time[0],_Digits+1,2);
   DrawHistogram(false,"iMACD.MAIN=",MAIN[0],time[0],_Digits+1,2);

//--- Deslocamento do diagrama na nova barra
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

Então, após considerar a tecnologia de plotagem dos diferentes histogramas de distribuições estatísticas para as características de mercado, pode-se argumentar que:

  • a funcionalidade do terminal de negociação MT5;
  • as possibilidades dos objetos gráficos MQL5;
  • a memória gráfica (as pouco conhecidas possibilidades dos objetos gráficos);
  • a função mais simples DrawHistogram()

permitem calcular e visualizar histogramas acumulados de um conjunto de indicadores, de padrão a personalizados. No entanto, para uma análise mais detalhada das características do mercado não é suficiente.

Características numéricas de distribuições estatísticas

Na primeira parte do artigo, foi mostrado como construir histogramas para programadores iniciantes. Os exemplos mencionados são fáceis de levar a cabo para uma grande classe de tarefas relacionadas com o estudo de distribuições estatísticas e sua visualização. Mas, para uma análise mais aprofundada dos histogramas, são necessárias técnicas de programação mais versáteis e avançadas, nomeadamente, POO. Assim, consideremos quais parâmetros de distribuições estatísticas serão interessantes para os leitores.

Em primeiro lugar, estamos interessados ​​nas características numéricas da série de variação:

  • média aritmética (frequência média);
  • média aritmética ponderada;
  • dispersão;
  • desvio padrão.

Em segundo lugar, é necessária a visualização destas características. Claro, tudo isso pode ser implementado num estilo processual, mas, neste artigo, resolveremos tarefas com o uso de programação orientada a objetos.

Classe CHistogram

A funcionalidade desta classe permite exibir, no gráfico de histograma, es características básicas de distribuições estatísticas. Consideremos os métodos básicos.

Método: construtor de classe CHistogram.

Inicializa uma instância da classe. 

void CHistogram(
   string name,                     // prefixo exclusivo de nome
   int    hsize,                    // escala do gráfico
   int    width,                    // espessura das linhas das colunas do gráfico de colunas
   color  active,                   // cor das linhas ativas
   color  passive,                  // cor das linhas passivas
   bool   Left_Right=true,          // left=false or right=true
   bool   relative_frequency=false, // gráfico de colunas relativo ou absoluto
   int    sub_win=0                 // índice da janela de plotagem do gráfico de colunas
   );

Parâmetros:

 name

    [in] Prefixo exclusivo do nome para todas as colunas do gráfico de colunas. 

 hsize

    [in] Escala de exibição do gráfico de colunas.

 width

    [in] Espessura das linhas das colunas do gráfico de colunas.

 active

    [in] Cor das colunas - do gráfico de colunas - atualizadas na barra atual.

 passive

    [in] Cor das colunas - do gráfico de colunas - que na barra atual ainda não se encontram atualizadas.

 Left_Right=true

    [in] Direção de exibição do gráfico de colunas. false — gráfico de colunas localizado ao lado esquerdo da barra atual, enquanto true se encontra à direita.

 relative_frequency=false

    [in] Método de contabilização de valores de frequência. false — valores absolutos das frequências, true — valores relativos das frequências.

 sub_win=0

    [in] Índice da janela para plotagem do gráfico de colunas. 0 — janela principal do gráfico. 

Valor de retorno:

 Não há nenhum valor de retorno. Se for bem-sucedido, ele cria uma instância de classe com os parâmetros especificados.

 

Método: exibição de gráficos de colunas DrawHistogram.

Ele exibe as colunas do histograma, isto é: cria novas; modifica as que estão disponíveis; armazena os valores das frequências na memória gráfica; mostra um gráfico de colunas na barra atual.

void DrawHistogram(
   double price,  // valor das opções
   datetime time  // tempo da barra atual
   );

Parâmetros:

 price

    [in] Valor de opção da característica investigada do mercado.

 time

    [in] Tempo da barra atual. Nesta barra estará o eixo do gráfico de colunas.  

Valor de retorno:

 Não há nenhum valor de retorno. Se for bem-sucedido, a coluna do gráfico de colunas será criada de novo ou corregida. Se não aparecer uma nova barra, o gráfico de colunas se deslocará de tal forma que o eixo fique localizado na barra atual. 

 

Método: cálculo de características do histograma HistogramCharacteristics. 

Retorna as características calculadas de uma série de variação na variável de tipo sVseries.

sVseries HistogramCharacteristics();

Parâmetros:

 Sem parâmetros de entrada.

Valor de retorno:

 Se for bem-sucedido, retorna o valor da variável de tipo sVseries.

 

Estrutura para obter os valores atuais das caraterísticas do gráfico de colunas (sVseries).

Estrutura para armazenar os últimos valores das características da distribuição estatística. Projetada para obter as informações mais procuradas sobre a série de variação. 

struct sVseries
  {
   long     N;    // número total de observações
   double   Na;   // valor médio de frequências
   double   Vmax; // valor máximo de opções
   double   Vmin; // valor mínimo de opções
   double   A;    // amplitude da série
   double   Mean; // média aritmética ponderada
   double   D;    // dispersão
   double   SD;   // desvio padrão
  };

A variável do tipo sVseries permite - através de uma única chamada da função HistogramCharacteristics() - obter os valores de todas as características básicas da série de variação exibida como um gráfico de colunas.

 

Método: visualização do valor da média DrawMean. 

Exibe o valor da média aritmética ponderada de uma série de variações no gráfico. 

void DrawMean(
   double coord,     // valor da média aritmética ponderada
   datetime time,    // tempo da barra atual
   bool marker=false,// exibir o marcador ou não
   bool save=false   // armazenar o valor no histórico ou não
   );

Parâmetros:

 coord

    [in] Valor da média aritmética ponderada.

 time

    [in] Tempo da barra atual. Nesta barra será fixado o valor da média aritmética ponderada.

 marker=false

    [in] Exibir o marcado no gráfico ou não. false — o marcado não é exibido, true — o marcado é exibido no gráfico.

 save=false

    [in]  Armazena o valor da média aritmética ponderada no histórico. false — não é exibido, true — o valor é exibido no gráfico.

Valor de retorno:

Se for bem-sucedido, no gráfico será exibida uma linha horizontal de acordo com o valor da média aritmética ponderada. 

 

Método: visualização do desvio padrão DrawSD.

Exibe o valor do desvio padrão sob a forma de um retângulo, cuja largura é a mesma que a frequência média, e cuja altura corresponde ao desvio padrão atrasado acima e abaixo do valor da média aritmética ponderada.

void DrawSD(
   sVseries &coord,        // variável do tipo sVseries
   datetime time,          // tempo da barra atual
   double deviation=1.0,   // desvio
   color clr=clrYellow     // cor de exibição
   );

Parâmetros:

 coord

    [in] Valor da variável do tipo sVseries.

 time

    [in] Tempo da barra atual.

 deviation=1.0

    [in] Coeficiente para o qual será aumentado o valor do desvio padrão.

 clr=clrYellow

    [in] Cor de retângulo do desvio padrão que está sendo visualizado.

Valor de retorno

 Se for bem-sucedido, no gráfico será exibido um retângulo característico para o desvio padrão a partir do valor da média aritmética ponderada.

  

Exemplo de plotagem de histogramas com uso da classe CHistogram

Como exemplo, podemos construir um histograma de distribuições estatísticas de preços Bid e Ask, bem como dois indicadores ATR e MACD(Fig. 9). Ao contrário dos exemplos anteriores, utilizando a classe já considerada, é possível criar gráficos relativos. Para o preço, além do histograma em si, criamos uma representação gráfica de uma média aritmética ponderada e um desvio padrão. Com isso, armazenaremos a média usando os buffers de indicador. Na figura, os desvios padrão são apresentados como retângulos, cuja largura corresponde à frequência média e a altura é igual a dois desvios, aumentados para melhorar a visibilidade várias vezes. 

 Fig. 9. Histogramas de dois indicadores: iATR, iMACD e preços Bid e Ask.

Fig. 9. Histogramas de dois indicadores: iATR, iMACD e preços Bid e Ask.


O código para este exemplo está no anexo ao artigo. O uso de buffers de indicador, neste programa, tem motivado um desejo de demonstrar a ligação entre a classe considerada e a funcionalidade do indicador.

Conclusão

  • Distribuições estatísticas de séries de variação - em forma de histogramas sem buffers de indicador e matrizes - podem ser implementadas facilmente usando uma memória "gráfica".
  • Para tarefas onde se empregam representações gráficas (objetos), na maioria dos casos, é justificado o uso de propriedades de objetos gráficos como uso de uma memória econômica. Com isto, nós temos acesso aos extensos recursos funcionais do terminal e da linguagem MQL5, isto é: a classificação, agrupamento, busca, amostragem, o acesso direto a elementos, etc.
  • Os métodos considerados, quer dizer, primitivos e básicos, são capazes de utilizar a memória "gráfica". Na verdade, pequenas matrizes e estruturas podem ser armazenadas nas propriedades dos objetos gráficos.

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

Como desenvolver e testar uma estratégia para Opções Binárias com o Testador de Estratégia do MetaTrader 4 Como desenvolver e testar uma estratégia para Opções Binárias com o Testador de Estratégia do MetaTrader 4
Tutorial para desenvolver uma estratégia para Opções Binárias e testa-la no Testador de Estratégia do MetaTrader 4 com o utilitário do Mercado Binary-Options-Strategy-Tester.
Fundamentos Básicos da Programação: Variáveis Globais do Terminal  MetaTrader 5 Fundamentos Básicos da Programação: Variáveis Globais do Terminal MetaTrader 5
As variáveis globais do terminal possibilitam uma ferramenta essencial para o desenvolvimento de Expert Advisors sofisticados e confiáveis. Se você dominar as variáveis globais, você nunca mais vai querer desenvolver EAs no MQL5 sem elas.
Interfaces Gráficas X: Caixa de Edição de Texto, Slider de Imagens e Controles Simples (build 5) Interfaces Gráficas X: Caixa de Edição de Texto, Slider de Imagens e Controles Simples (build 5)
Este artigo irá considerar novos controles: A Caixa de Edição de Texto, o Slider de Imagem, bem como os controles simples adicionais: Rótulo de Texto e Imagem. A biblioteca continua a crescer, e, além da introdução de novos controles, aqueles que foram criados anteriormente também estão sendo melhorados.
Oscilador universal com interface gráfica do usuário Oscilador universal com interface gráfica do usuário
No artigo, descreve-se a criação de um indicador universal baseado em todos os osciladores do terminal com uma interface gráfica do usuário própria. Isto permite rápida e facilmente alterar os parâmetros de cada oscilador individual diretamente a partir da janela do gráfico (em vez de abrir a janela de opções), comparar seu desempenho e selecionar a melhor opção para uma tarefa específica.