O Histograma de preço (Perfil de mercado) e sua implementação no MQL5

Dmitry Voronkov | 30 dezembro, 2013

"O Perfil de mercado tenta fornecer essa lógica interna, no contexto do mercado.
É um método de análise que começa com a compreensão de que somente o preço
não comunica as informações ao participante, apenas com palavras
sem sintaxe
ou o contexto pode não ter nenhum significado. O volume é uma parte integrante da expressão direta
do mercado - compreenda-o e você compreenderá a linguagem do mercado."
Robin Mesh

Introdução

Há muito tempo atrás, examinando uma assinatura de revista, encontrei um artigo "Perfil de mercado e compreensão da linguagem de mercado" (outubro de 2002) no jornal russo "Valutny Spekulant" (atualmente é chamado como "Trader ativo"). O artigo original foi publicado em "New Thinking in Technical Analysis: Trading Models from the Masters".

O Perfil de mercado foi desenvolvido pelo pensador realmente brilhante Peter Steidlmayer. Ele descobriu a expressão natural de (volume) de mercado e organizou-a de uma forma que fosse legível (a curva de Gaussian), de modo que a informação objetiva gerada pelo mercado fosse acessível para participantes do mercado. Steidlmayer sugeriu o uso da representação alternativa de informação sobre movimentos de mercados "horizontais" e "verticais" que levam a um conjunto de modelos completamente diferentes. Ele presumiu que existe um pulso subjacente do mercado ou de um padrão fundamental chamado de ciclo de equilíbrio e desequilíbrio.

O Perfil de mercado mede o movimento do mercado horizontal através de um vertical. Vamos chamar isso de "equilíbrio" através de "desequilíbrio". Esta relação é o princípio organizador fundamental do mercado. Todo estilo do negócio de trading pode mudar dependendo de que parte do ciclo de equilíbrio/desequilíbrio do mercado ele esteja. O Perfil de mercado pode determinar tanto quando o mercado vai mudar de equilíbrio para o desequilíbrio e o quão grande o movimento será.

Os dois conceitos básicos de Perfil de mercado são:

  1. O mercado é um leilão e move-se na direção da faixa de preço onde a oferta e a demanda são mais ou menos iguais.
  2. O mercado tem duas fases: atividade horizontal e atividade vertical. O mercado se move verticalmente quando oferta e procura não são iguais ou estão em desequilíbrio e horizontalmente quando estão em equilíbrio ou equilibradas.

O mercado de equilíbrio mostrado utilizando o Perfil de mercado no quadro abaixo, tende a formar uma curva em forma de sino quase perfeita, com rotação de 90 graus, devido à orientação do gráfico:

Fig 1. O Perfil de mercado do mercado de equilíbrio

A tendência, mercado não-equilibrado também forma uma curva em forma de sino, mas o seu centro é deslocado para cima ou para baixo. Outras configurações que formam dois picos de sinos, dependendo do movimento do preço e da confiança dos agentes do mercado, são possíveis.

Fig 2. O Perfil de mercado do mercado (tendência) de desequilíbrio

A utilização das formas perfil diárias para determinar o grau do nível de equilíbrio/desequilíbrio do mercado pode ser útil, porque lhe dá um ponto de partida para a compreensão das mudanças entre os vários participantes do mercado.

A oportunidade de trading com o maior benefício aparece quando a mudança de equilíbrio para o desequilíbrio está prestes a ocorrer. Além disso, se você pode identificar essa oportunidade de trading e estimar com precisão a magnitude potencial dessa mudança, então você pode estimar a qualidade de negócio e a quantidade de tempo que é necessário para isso.

É necessário notar que, neste artigo, consideraremos o código para desenhar uma versão simplificada do Perfil de mercado, o chamado Histograma de preço, baseado na relação entre preço e tempo.

Você pode encontrar o exemplo de metodologia de trabalho com esta ferramenta em http://www.enthios.com/, onde um grupo de traders estuda o Histograma de preço desde 1998. A estratégia Universal Enthios e exemplo do seu uso podem também ser encontradas aqui.

1. Histograma de preço

O Histograma de preço é uma ferramenta muito confiável. É um pouco intuitivo, mas extremamente eficaz. O Histograma de preço simplesmente mostra os "mais convenientes" pontos de negociação do mercado. Este é um indicador líder, porque mostra os pontos onde o mercado pode mudar a sua direção com antecedência. Os indicadores, como médias móveis ou osciladores não podem especificar os pontos exatos de resistência e de apoio, eles podem apenas mostrar o fato caso o mercado esteja sobrecomprado ou sobrevendido.

Normalmente, o Histograma de preço (ou Perfil de mercado) é aplicado a gráficos de preços de 30 min. para estudar a atividade do mercado durante um dia. Eu prefiro usar os gráficos de 5 min. para os mercados de ações e gráficos de 15-30 min. para FOREX.

2. Ponto de controle

Na figura acima você pode ver o nível em que o mercado foi negociado em quantidade máxima de tempo, está descrito com a linha mais longa do histograma. É chamado de Ponto de Controle, ou POC. Por vezes, como visto na figura, o histograma tem dois topos, um deles é um pouco inferior. Nesse caso, vemos que o indicador mostra apenas um POC, mas na verdade existem dois deles, e isso deve ser levado em conta.

Além disso, o nível de percentagem da taxa no histograma também cria níveis adicionais, assim chamados de Níveis de POC secundários:

Fig 3. Pontos de controle

O que mostra o POC? O preço que é lembrado pela maioria dos traders. O mercado mais longo é negociado a este preço, o mercado mais longo, lembre-se.

O POC age psicologicamente como um centro de atração.

O próximo gráfico mostra o que aconteceu poucos dias antes. É uma boa demonstração do poder do Histograma de preço.

Fig 4. O Ponto de controle não é absoluto, mostra a taxa de negócio

O Ponto de controle não é absoluto, indica a taxa de negociação. Assim, o trader deve estar pronto para agir quando o mercado se aproximar ao POC. Auxilia a melhorar ordens, utilizando as observações históricas.

Vamos considerar a fig. 4. O POC em 29.12.2009 está localizado ao preço de 68,87. É claro que, mesmo sem o histograma e a linha de POC, o mercado estava dentro da taxa de 68,82~68,96 quase durante o dia todo. O mercado fechou no final do dia com 5 pontos abaixo do POC. No dia seguinte causou a abertura do mercado com um deslizamento.

É importante entender que não podemos prever se o mercado subirá ou descerá. Podemos apenas supor que o mercado retornará à linha do POC e ao acúmulo máximo das linhas do histograma. Mas o que acontecerá quando o preço encostar no POC? A mesma coisa que acontece com um objeto elástico que cai no chão, saltará para trás. Se isso acontecer rapidamente, como uma bola de tênis que dá golpes em uma raquete, o preço retornará muito rapidamente para o nível inicial.

Após a abertura do mercado em 30.12.2009, vemos que era uma lacuna e, em seguida, o mercado encostou no POC do dia anterior e, então, rapidamente voltou para o preço de abertura e atualizou o mínimo.

Observe que o POC não é absolutamente preciso (traders experientes sabem que não há níveis de resistência claros quando o preço atinge um alcance máximo, mínimo ou taxa de concentração). O que acontece neste momento depende dos agentes de mercado. Se o desejo coletivo (por exemplo, publicação de notícias) coincidir, então o mercado vai passar do POC, mas é raro e pode ser utilizado para desenvolver um sistema de negociação.

Preste atenção que o comportamento do mercado era o mesmo em 31.12.2009. Quando o preço encostou no POC, os compradores renderam-se aos vendedores.

3. Ponto virgem de controle

O POC Virgem (Ponto virgem de controle) é um nível que o preço não atingiu nos próximos dias.

A lógica é simples, como descrito acima, o POC é um ponto de atração para o mercado. Como o preço se afasta do POC, a força de atração aumenta. E quanto mais o preço se afasta do POC Virgem, maior é a possibilidade de que quando retornar a este nível, o salto de retorno ocorrerá e, provavelmente, um preço invertido também ocorrerá.

Fig 5. POC Virgem anterior e atual

Na fig. 5, os POCs Virgens anteriores que eram os níveis de suporte e resistência estão marcados com círculos. Os POCs Virgens em funcionamento estão marcados com valores de preço.

Uma vez que o preço tenha encostado no POC Virgem, ele deixa de ser um "virgem". Psicologicamente, o mercado já não o vê como um nível substancial de suporte ou resistência. Os traders ainda podem ver os níveis de preços, que inicialmente formaram o POC, mas como uma simples acumulação de preços.

Você pode obter mais detalhes sobre os níveis de preço no "Master-trading: The X-Files" livro de Eric Naiman (Capítulo 4, "Nível de preços é uma linha de base").

4. Implementação de Histograma de preço no MQL5

Minha primeira versão do Histograma de preço apareceu em 2006, foi escrita no MQL4 no MetaTrader 4 para uso pessoal. Durante o desenvolvimento deste indicador, enfrentei alguns problemas, aqui estão alguns deles:

    1. Pouquíssimo número de barras no histórico para M5, sem falar do M1;
    2. A necessidade de desenvolver funções especiais para trabalhar com histórico, como voltar para trás por um dia, considerando feriados, verificar o tempo de fechamento do mercado na sexta-feira, verificar o tempo de abertura e fechamento para o mercado de CFD, etc.;
    3. Recalcular o indicador ao alterar prazos e, como resultado, os atrasos terminais.

    Portanto, quando o teste inicial do МetaТrader 5 e MQL5 começou, decidi convertê-lo ao MQL5.

    Como as pessoas dizem, "a primeira panqueca é sempre um pouco mais complicada", tentei implementá-lo como um indicador.

    Vamos começar com a boa: a presença de um longo histórico de citações de um minuto para todos os símbolos, a possibilidade de obtenção de dados históricos para um determinado período de tempo, em qualquer intervalo de tempo.

    Agora vou explicar por que acabou. Não considerei os recursos dos indicadores do MQL5:

      1. O tempo de execução do indicador é crítico;
      2. Os recursos do trabalho do indicador após as mudanças de período de tempo.

      A execução da função OnCalculate(), que corresponde a Calcular programa, tem um tempo de execução crítico. Assim, o processo de 260 dias (período anual) utilizando as barras de minuto, a história leva muito tempo, até vários minutos. Claro que podemos aceitar isso, se os cálculos realizados de uma só vez, após o indicador anexar-se ao gráfico. Mas este não é o caso para alterações no período de tempo. Quando o indicador muda para os diferentes prazos, a cópia antiga do indicador é destruída e criada uma nova. É por isso que após alterações do período de tempo temos de recalcular os mesmos níveis novamente e isso leva muito tempo.

      Mas como costuma-se dizer, se você não sabe o que fazer - "Leia a documentação primeiro", no nosso caso, a documentação do MQL5. A solução era muito simples - implementar este indicador como Expert Advisor que não negocia.

      As vantagens do Expert Advisor são:

        1. O tempo de processamento não é crítico para programa Init no OnTick();
        2. A possibilidade de obter os parâmetros do programa OnDeinit (const razão int).

        Os Experts Advisors diferem-se dos indicadores conforme a seguir: após o prazo mudar o consultor especialista apenas gera o evento DeInit com o parâmetro de razão REASON_CHARTCHANGE, ele não descarrega o Expert Advisor a partir da memória e serve os valores das variáveis globais. Ele nos permite realizar todos os cálculos de uma só vez após anexar o Expert Advisor, alterando seus parâmetros e novos dados que apareçam, no nosso caso, para um novo dia de negociação.

        Vamos apresentar algumas definições que serão necessárias mais tarde.

        A programação de objeto orientado (OOP) - é um estilo de programação que conceitos básicos são os conceitos de objetos e classes.

        O objeto é uma entidade no espaço virtual, com estado e comportamento especificado, que possui alguns valores de propriedades (chamados de atributos) e operações com tais (chamadas de métodos).

        Na OOP a classe é um tipo de dado abstrato especial, caracterizado por meio de sua construção. Ela é um conceito chave na OOP e difere-se dos outros tipos de dados abstratos. A definição de dados na classe também contém métodos de classe de seu processamento de dados (interface).

        Na programação, há um conceito de interface de software que significa uma lista de possíveis cálculos que podem ser realizados por uma parte do programa, incluindo algoritmos, a descrição dos argumentos e ordem de parâmetros de entrada para prosseguir e os seus valores de retorno. A interface de tipo abstrato de dados foi desenvolvida para uma descrição formal de tal lista. Os próprios algoritmos e o código que irão executar todos esses cálculos não são especificados e chamados como implementação de interface.

        A criação de classe é a criação de uma estrutura com campos e métodos. A classe inteira pode ser considerada como um modelo para a criação de objetos, que são objetos específicos de classes. Os objetos específicos de classe são criados usando o mesmo modelo, para que eles tenham os mesmos campos e métodos.

        Vamos começar...

        O código fonte está localizado em 4 arquivos. O principal arquivo é PriceHistogram.mq5, os outros são: ClassExpert.mqh, ClassPriceHistogram.mqh e ClassProgressBar.mqh. Os arquivos com extensão .mqh contêm as descrições e métodos de classes. Todos os arquivos devem estar localizados no mesmo diretório. Meu diretório é: \MQL5\ Experts\PriceHistogram.

        4.1. PriceHistogram.mq5

        A primeira afirmação no código-fonte é:

        #include "ClassExpert.mqh"

        A diretiva de compilação #include inclui o texto a partir do arquivo especificado. No nosso caso é a descrição de classe CExpert (discutida abaixo).

        A próxima é um bloco de variáveis de entrada que são parâmetros do Expert Advisor.

        // The block input parameters
        input int         DayTheHistogram   = 10;          // Days for histogram
        input int         DaysForCalculation= 500;         // Days for calculation(-1 all)
        input uint        RangePercent      = 70;          // Percent range
        input color       InnerRange        =Indigo;       // Inner range
        input color       OuterRange        =Magenta;      // Outer range
        input color       ControlPoint      =Orange;       // Point of Control
        input bool        ShowValue         =true;         // Show Values
        

        Após isso, a variável ExtExpert (do tipo de classe CExpert) é declarada.

        A próxima é programas padrões que estão nos programas MQL5. Os programas requerem os métodos correspondentes da classe CExpert.

        Há apenas um método que executa algumas operações antes da execução do CExpert - é o método OnInit():
        int OnInit()
          {
        //---
        // We check for symbol synchronization before the start of calculations
           int err=0;
           while(!(bool)SeriesInfoInteger(Symbol(),0,SERIES_SYNCRONIZED) && err<amount_of_attempts) {="" Sleep(500);
              err++;
             }
        // CExpert class initialization
           ExtExpert.RangePercent=RangePercent;
           ExtExpert.InnerRange=InnerRange;
           ExtExpert.OuterRange=OuterRange;
           ExtExpert.ControlPoint=ControlPoint;
           ExtExpert.ShowValue=ShowValue;
           ExtExpert.DaysForCalculation=DaysForCalculation;
           ExtExpert.DayTheHistogram=DayTheHistogram;
           ExtExpert.Init();
           return(0);
          }
        

        Quando escrevi a primeira versão do Expert Advisor e executei-o, tive alguma dificuldade em compreender o porquê dele terminar com erro após o reinício do terminal do cliente ou um símbolo mudar. E ocorre quando o terminal de cliente foi desconectado ou um símbolo não é utilizado por um longo tempo.

        É ótimo que os desenvolvedores adicionaram o depurador para o MetaEditor5. Lembro-me de um monte de comandos Print() and Comment() (imprimir e comentar) utilizados para a verificação dos valores de variáveis no MetaEditor4. Muito obrigado aos desenvolvedores do MetaEditor5.

        No meu caso, tudo era fácil, o especialista começa antes da conexão com o servidor e da atualização dos dados do histórico. Para resolver este problema, tive que usar o SeriesInfoInteger(Symbol(),0,SERIES_SYNCRONIZED), que relata se os dados estão sincronizados ou não, e o tempo de ciclo (), no caso de ausência de conexão que utiliza a variável do contador de erro.

        Uma vez que os dados tenham sido sincronizados, ou que o ciclo tenha sido concluído por causa do contador na ausência de conexão, passamos os parâmetros de entrada da nossa classe especialista CExpert e chamamos o método de inicialização de classe Init().

        Como se pode ver, graças ao conceito de classes em MQL5, nosso arquivo PriceHistogram.mq5 se transformou em um modelo simples, e todo o processamento está na classe CExpert, declarado no arquivo ClassExpert.mqh.

        4.2. ClassExpert.mqh

        Vamos considerar sua descrição.

        //+------------------------------------------------------------------+
        //|   Class CExpert                                                  |
        //|   Class description                                              |
        //+------------------------------------------------------------------+
        class CExpert
          {
        public:
           int               DaysForCalculation; // Days to calculate (-1 for all)
           int               DayTheHistogram;    // Days for Histogram 
           int               RangePercent;       // Percent range
           color             InnerRange;         // Internal range color
           color             OuterRange;         // Outer range color
           color             ControlPoint;       // Point of Control (POC) Color
           bool              ShowValue;          // Show value
        

        A seção pública está aberta e acessível a partir de variáveis externas. Você vai notar que os nomes das variáveis coincidem com nomes de seção de parâmetros de entrada descritos no PriceHistogram.mq5. Não é necessário, porque os parâmetros de entrada são globais. Mas neste caso - é um tributo às boas regras de boas reproduções, é desejável para evitar o uso de variáveis externas dentro da classe.

        private:
           CList             list_object;        // The dynamic list of CObject class instances
           string            name_symbol;        // Symbol name
           int               count_bars;         // Number of daily bars
           bool              event_on;           // Flag of events processing
        

        A seção privada é fechada a partir do exterior e apenas acessível dentro da classe. Eu gostaria de descrever a variável list_object of CList type, que é uma classe da biblioteca padrão do MQL5. A Classe CList é uma classe dinâmica com uma lista de exemplos de classe CObject e seus herdeiros. Usarei essa lista para o armazenamento de referências para os elementos de classe CPriceHistogram, que é um herdeiro da classe CObject, nós consideraremos os detalhes abaixo. A descrição da classe CList está no List.mqh e inclui usando a diretiva de compilação #include.

        public:
           // Class constructor
                             CExpert();
           // Class destructor
                            ~CExpert(){Deinit(REASON_CHARTCLOSE);}
           // Initialization method
           bool              Init();
           // Deinitialization method
           void              Deinit(const int reason);
           // Method of OnTick processing
           void              OnTick();
           // Method of OnChartEvent() event processing
           void              OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
           // Method of OnTimer() event processing
           void              OnTimer();
        };

        O seguinte é uma seção de métodos públicos. Como você já deve ter adivinhado tais métodos (funções) estão disponíveis fora da classe.

        E, finalmente, a chave com um ponto e vírgula completa a descrição da classe.

        Vamos considerar métodos de classe em detalhe.

        O construtor da classe é um bloco especial de declarações, chamado quando o objeto é criado. O construtor é semelhante ao método, mas difere-se do método que não tenha forma explícita de um certo tipo de dados retornados.

        Na linguagem MQL5, os construtores não podem ter parâmetros de entrada e cada classe deve ter um único construtor. No nosso caso, o construtor é uma inicialização primária de variáveis.

        O destrutor é um método de classe especial que é utilizado para a finalização do objeto (por exemplo, memória livre). No nosso caso o método é chamado de Deinit (REASON_CHARTCLOSE).

        O Init() é um método para a inicialização da classe. Este é o método mais importante da classe CExpert, a criação de objetos de histograma executaram-se lá. Por favor, olhe os comentários para os detalhes. Mas eu gostaria de considerar alguns pontos.

        O primeiro, para construir um Histograma de preço diário, precisamos dos dados de tempo abertos por dias para continuar. Aqui eu gostaria de divagar e chamar a atenção para os recursos de trabalho com série de tempo. Para a solicitação de dados de outros prazos que precisávamos de um tempo, então as funções de Bars() e CopyTime(), bem como outras funções para trabalhar com série de tempo não são sempre o retorno dos dados da primeira chamada.

        Então, eu tive que colocar essa função no do (...) while () loop, mas para torná-lo finito, usei a variável do contador.

         int err=0;
         do
           {
            // Calculate the number of days which available from the history
            count_bars=Bars(NULL,PERIOD_D1);
            if(DaysForCalculation+1<count_bars) count="DaysForCalculation+1;
            else
               count=count_bars;
            if(DaysForCalculation<=0) count=count_bars;
            rates_total=CopyTime(NULL,PERIOD_D1,0,count,day_time_open);
            Sleep(1);
            err++;
           }
         while(rates_total<=0 && err<amount_of_attempts); if(err>=AMOUNT_OF_ATTEMPTS)
           {
           Print("There is no accessible history PERIOD_D1");
           name_symbol=NULL;
           return(false);
           }

        Em segundo lugar, o histórico minuto do MetaTrader 5 está disponível igual aos dias de hoje, para que se possa ter um monte de tempo para prosseguir, por isso é necessário visualizar o processo de cálculo. A classe CProgressBar (#include "ClassProgressBar.mqh"), foi desenvolvida para este fim. Ela cria a barra de progresso na janela do gráfico e atualiza-o durante o processo de cálculo.

         // We create the progress bar on the char to shot the loading process
         CProgressBar   *progress=new CProgressBar;
         progress.Create(0,"Loading",0,150,20);
         progress.Text("Calculation:");
         progress.Maximum=rates_total; 

        O terceiro, em ciclo, utilizando a instrução "novo", criamos o objeto CPriceHistogram, configure-o usando seus métodos e inicialize-o chamando o Init(). Se bem sucedido, adicionamos à lista list_object, se não, deletamos o hist_obj usando instrução de exclusão. A descrição da classe CPriceHistogram será apresentada ainda, ver comentários no código.

         // In this cycle there is creation of object CPriceHistogram
         // its initialization and addition to the list of objects
         for(int i=0;i<rates_total;i++) {="" cpricehistogram="" *hist_obj="new CPriceHistogram();
            //         hist_obj.StepHistigram(step);
            // We set the flag to show text labels
            hist_obj.ShowLevel(ShowValue);
            // We set POCs colour
            hist_obj.ColorPOCs(ControlPoint);
            // We set colour for inner range
            hist_obj.ColorInner(InnerRange);
            // We set colour for outer range
            hist_obj.ColorOuter(OuterRange);
            // We set the percent range
            hist_obj.RangePercent(RangePercent);
            //  hist_obj.ShowSecondaryPOCs((i>=rates_total-DayTheHistogram),PeriodSeconds(PERIOD_D1));
            if(hist_obj.Init(day_time_open[i],day_time_open[i]+PeriodSeconds(PERIOD_D1),(i>=rates_total-DayTheHistogram)))
               list_object.Add(hist_obj);
            else
               delete hist_obj; // Delete object if there was an error
            progress.Value(i);
           }; 

        O OnTick() é um método chamado quando você recebe uma nova escala para um símbolo. Nós comparamos os valores do número de dias armazenados nas variáveis count_bars com o número de barras diárias retornadas por Barras (Symbol(), PERIOD_D1) e se elas não são iguais nós chamamos forçadamente o método Init () para inicialização da classe, limpando a lista list_object e mudando a variável para NULL name_symbol. Se o número de dias não mudou, o circuito passa por todos os objetos armazenados na classe CPriceHistogram list_object, e executa um método Redesenhar (), para aqueles que são virgens («virgin").

        O Deinit() é um método de finalização de classe. No caso de REASON_PARAMETERS (parâmetros de entrada foram alterados pelo usuário) limpamos a lista list_object e definimos a variável name_symbol para NULL. Em outros casos, o especialista não faz nada, mas se você desejar adicionar algo, leia os comentários.

        O OnEvent() é um método para o processamento de evento do terminal do cliente. Os eventos são gerados pelo terminal do cliente quando o usuário trabalhar com gráfico. Os detalhes podem ser encontrados na documentação da linguagem do MQL5. Neste Expert Advisor, o evento de gráfico CHARTEVENT_OBJECT_CLICK foi usado. Ao clicar no elemento histograma, ele mostra os níveis de POC secundários e inverte a cor do histograma.

        O OnTimer(void) é um método para processamento de eventos de temporizador. Não é utilizado nos meus programas, mas se você desejar adicionar algumas ações temporizadas (por ex. mostrar a hora) - está aqui. Antes de usá-lo é necessário adicionar a seguinte linha para o construtor da classe:

        EventSetTimer(tempo em segundos);

        E a seguinte linha para o destrutor:

        EventKillTimer(); 

        antes de chamar o método Deinit (REASON_CHARTCLOSE ).

        Consideramos a classe CExpert, que foi criada para a demonstração dos métodos de classe CPriceHistogram.

        4.3. ClassPriceHistogram.mqh
        //+------------------------------------------------------------------+
        //|   Class CPriceHistogram                                          |
        //|   Class description                                              |
        //+------------------------------------------------------------------+
        class CPriceHistogram : public CObject
          {
        private:
           // Class variables
           double            high_day,low_day;
           bool              Init_passed;      // Flag if the initialization has passed or not
           CChartObjectTrend *POCLine;
           CChartObjectTrend *SecondTopPOCLine,*SecondBottomPOCLine;
           CChartObjectText  *POCLable;
           CList             ListHistogramInner; // list for inner lines storage 
           CList             ListHistogramOuter; // list for outer lines storage
           bool              show_level;         // to show values of level
           bool              virgin;             // is it virgin
           bool              show_second_poc;    // show secondary POC levels
           double            second_poc_top;     // value of the top secondary POC level
           double            second_poc_bottom;  // value of the bottom secondary POC level
           double            poc_value;          // POC level value
           color             poc_color;          // color of POC level
           datetime          poc_start_time;
           datetime          poc_end_time;
           bool              show_histogram;     // show histogram  
           color             inner_color;        // inner color of the histogram
           color             outer_color;        // outer color of the histogram
           uint              range_percent;      // percent range
           datetime          time_start;         // start time for construction
           datetime          time_end;           // final time of construction
        public:
           // Class constructor
                             CPriceHistogram();
           // Class destructor
                            ~CPriceHistogram(){Delete();}
           // Class initialization
           bool              Init(datetime time_open,datetime time_close,bool showhistogram);
           // To level value
           void              ShowLevel(bool show){show_level=show; if(Init_passed) RefreshPOCs();}
           bool              ShowLevel(){return(show_level);}
           // To show histogram
           void              ShowHistogram(bool show);
           bool              ShowHistogram(){return(show_histogram);}
           // To show Secondary POC levels
           void              ShowSecondaryPOCs(bool show){show_second_poc=show;if(Init_passed)RefreshPOCs();}
           bool              ShowSecondaryPOCs(){return(show_second_poc);}
           // To set color of POC levels
           void              ColorPOCs(color col){poc_color=col; if(Init_passed)RefreshPOCs();}
           color             ColorPOCs(){return(poc_color);}
           // To set internal colour of histogram
           void              ColorInner(color col);
           color             ColorInner(){return(inner_color);}
           // To set outer colour of histogram
           void              ColorOuter(color col);
           color             ColorOuter(){return(outer_color);}
           // To set percent range
           void              RangePercent(uint percent){range_percent=percent; if(Init_passed)calculationPOCs();}
           uint              RangePercent(){return(range_percent);}
           // Returns value of virginity of POC level
           bool              VirginPOCs(){return(virgin);}
           // Returns starting time of histogram construction
           datetime          GetStartDateTime(){return(time_start);}
           // Updating of POC levels
           bool              RefreshPOCs();
        private:
           // Calculations of the histogram and POC levels
           bool              calculationPOCs();
           // Class delete
           void              Delete();
          }; 

        Na descrição da classe, eu tentei fazer comentários para as variáveis ​e métodos de classe. Vamos considerar alguns deles em detalhes.

        //+------------------------------------------------------------------+
        //|   Class initialization                                           |
        //+------------------------------------------------------------------+
        bool CPriceHistogram::Init(datetime time_open,datetime time_close,bool showhistogram) 

        Este método usa três parâmetros de entrada - a abertura da construção, o tempo de fechamento da construção e a marcação indicando para construção de um histograma, ou apenas os níveis de POCs.

        No meu exemplo (classe CExpert), os parâmetros de entrada são passados no dia e hora de abertura da abertura do day_time_open [i] + PeriodSeconds (PERIOD_D1) do próximo dia. Mas quando você usa essa classe, nada impede de perguntar, por exemplo, o horário da sessão da Europa, América ou o tamanho da distância na semana, mês, etc.

        //+---------------------------------------------------------------------------------------+
        //|   Calculations of the histogram and POCs levels                                       |
        //+---------------------------------------------------------------------------------------+
        bool CPriceHistogram::calculationPOCs() 

        Neste método, a origem de todos os níveis e cálculos de sua construção é método privado fechado, inacessível por fora.

        // We get the data from time_start to time_end
           int err=0;
           do
             {
              //--- for each bar we are copying the open time
              rates_time=CopyTime(NULL,PERIOD_M1,time_start,time_end,iTime);
              if(rates_time<0)
                 PrintErrorOnCopyFunction("CopyTime",_Symbol,PERIOD_M1,GetLastError());
        
              //--- for each bar we are copying the High prices
              rates_high=CopyHigh(NULL,PERIOD_M1,time_start,time_end,iHigh);
              if(rates_high<0)
                 PrintErrorOnCopyFunction("CopyHigh",_Symbol,PERIOD_M1,GetLastError());
        
              //--- for each bar we are copying the Low prices
              rates_total=CopyLow(NULL,PERIOD_M1,time_start,time_end,iLow);
              if(rates_total<0)
                 PrintErrorOnCopyFunction("CopyLow",_Symbol,PERIOD_M1,GetLastError());
        
              err++;
             }
           while((rates_time<=0 || (rates_total!=rates_high && rates_total!=rates_time)) && err<amount_of_attempts&&!IsStopped());
           if(err>=AMOUNT_OF_ATTEMPTS)
             {
              return(false);
             }
           poc_start_time=iTime[0];
           high_day=iHigh[ArrayMaximum(iHigh,0,WHOLE_ARRAY)];
           low_day=iLow[ArrayMinimum(iLow,0,WHOLE_ARRAY)];
           int count=int((high_day-low_day)/_Point)+1;
        // Count of duration of a finding of the price at each level
           int ThicknessOfLevel[];    // create an array for count of ticks
           ArrayResize(ThicknessOfLevel,count);
           ArrayInitialize(ThicknessOfLevel,0);
           for(int i=0;i<rates_total;i++) {="" double C=iLow[i];
              while(C<ihigh[i]) {="" int Index=int((C-low_day)/_Point);
                 ThicknessOfLevel[Index]++;
                 C+=_Point;
                }
             }
           int MaxLevel=ArrayMaximum(ThicknessOfLevel,0,count);
           poc_value=low_day+_Point*MaxLevel;</amount_of_attempts&&!

        Em primeiro lugar, temos os dados de histórico de barras minutos por um determinado período de tempo (iTime [], iHigh[], iLow[]). Então, encontramos o elemento máximo e o mínimo de iHigh[] iand Low[]. Em seguida, calculamos o número de pontos (contagem) do mínimo ao máximo e reservamos a formação ThicknessOfLevel com os elementos ThicknessOfLevel. No ciclo que passamos a cada minuto da vela de Baixo para o Alto e adicionamos os dados da presença do período de tempo, a este nível de preço. Então, encontramos o elemento máximo da formação ThicknessOfLevel, ele será o nível em que o preço foi o tempo mais longo. Este é o nosso nível POC.

        // Search for the secondary POCs
           int range_min=ThicknessOfLevel[MaxLevel]-ThicknessOfLevel[MaxLevel]*range_percent/100;
           int DownLine=0;
           int UpLine=0;
           for(int i=0;i<count;i++) {="" if(ThicknessOfLevel[i]>=range_min)
                {
                 DownLine=i;
                 break;
                }
             }
           for(int i=count-1;i>0;i--)
             {
              if(ThicknessOfLevel[i]>=range_min)
                {
                 UpLine=i;
                 break;
                }
             }
           if(DownLine==0)
              DownLine=MaxLevel;
           if(UpLine==0)
              UpLine=MaxLevel;
           second_poc_top=low_day+_Point*UpLine;
           second_poc_bottom=low_day+_Point*DownLine;
        

        O próximo passo é encontrarmos os níveis POC secundários. Lembre-se que nosso diagrama é dividido. Lembre-se que nosso histograma é dividido em duas faixas, a faixa interna e a externa (exibida em cores diferentes) e a taxa do tamanho é definida em percentagem de tempo no preço a este nível. A taxa dos limites internos são níveis POC secundários.

        Depois de encontrar o POC secundário - taxa de porcentagem de bordas, prosseguir à construção do histograma.

        // Histogram formation 
           if(show_histogram)
             {
              datetime Delta=(iTime[rates_total-1]-iTime[0]-PeriodSeconds(PERIOD_H1))/ThicknessOfLevel[MaxLevel];
              int step=1;
              
              if(count>100)
                 step=count/100;  // Calculate the step of the histogram (100 lines as max)
        
              ListHistogramInner.Clear();
              ListHistogramOuter.Clear();
              for(int i=0;i<count;i+=step) {="" string name=TimeToString(time_start)+" "+IntegerToString(i);
                 double StartY= low_day+_Point*i;
                 datetime EndX= iTime[0]+(ThicknessOfLevel[i])*Delta;
        
                 CChartObjectTrend *obj=new CChartObjectTrend();
                 obj.Create(0,name,0,poc_start_time,StartY,EndX,StartY);
                 obj.Background(true);
                 if(i>=DownLine && i<=UpLine)
                   {
                    obj.Color(inner_color);
                    ListHistogramInner.Add(obj);
                   }
                 else
                   {
                    obj.Color(outer_color);
                    ListHistogramOuter.Add(obj);
                   }
                }
             }
        
        

        Deve ser mencionado que, para reduzir a carga no terminal, eu trago à tela um máximo de 100 linhas para cada histograma. Linhas de histograma são armazenadas em duas listas ListHistogramInner e ListHistogramOuter, que são os objetos já conhecidos à nossa classe CList. Mas esses ponteiros são armazenados em uma classe padrão de objetos CChartObjectTrend. Por que duas listas, eu acho que você pode imaginar do título, são capazes de alterar o histograma de cores.

        // We receive data beginning from the final time of the histogram till current time
           err=0;
           do
             {
              rates_time=CopyTime(NULL,PERIOD_M1,time_end,last_tick.time,iTime);
              rates_high=CopyHigh(NULL,PERIOD_M1,time_end,last_tick.time,iHigh);
              rates_total=CopyLow(NULL,PERIOD_M1,time_end,last_tick.time,iLow);
              err++;
             }
           while((rates_time<=0 || (rates_total!=rates_high && rates_total!=rates_time)) && err<amount_of_attempts); // If there isn't history, the present day, level is virgin, we hoist the colours
           if(rates_time==0)
             {
              virgin=true;
             }
           else
        // Otherwise we check history
             {
              for(index=0;index<rates_total;index++) if(poc_valueiLow[index]) break;
        
              if(index<rates_total) // If level has crossed
                 poc_end_time=iTime[index];
              else
                 virgin=true;
             }
           if(POCLine==NULL)
             {     
              POCLine=new CChartObjectTrend();
              POCLine.Create(0,TimeToString(time_start)+" POC ",0,poc_start_time,poc_value,0,0);
             }
           POCLine.Color(poc_color);
           RefreshPOCs();

        Tentei desenhar o CPriceHistogram com todos os métodos necessários, caso sejam insuficientes, você pode adicioná-los e irei ajudá-lo.

        Resumo

        Mais uma vez eu gostaria de lembrar que o Histograma de preço é confiável, mas a ferramenta é intuitiva, portanto, os sinais de confirmação são necessários para a sua utilização.

        Obrigado por seu interesse. Estou pronto para responder a todas as suas perguntas.