English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Traçando canais - vista interna e externa

Traçando canais - vista interna e externa

MetaTrader 5Indicadores | 10 fevereiro 2014, 11:09
2 686 1
Dmitriy Skub
Dmitriy Skub

Introdução

Acredito que não será exagero dizer que os canais são a ferramenta mais popular para a análise de mercado e para a tomada de decisões de negociação após as médias móveis. No primeiro artigo da série, que é dedicado aos canais, vamos discutir a base matemática e a implementação teórica de um indicador que traça um canal estabelecido por três extremos na tela do terminal do cliente.

à primeira vista, traçar um canal parece uma tarefa fácil, visto que se baseia na equação de uma linha reta, que é ensinada na escola primária. Entretanto, a sua implementação prática no terminal do cliente envolve diversas perguntas que não podem ser respondidas imediatamente.

Como organizar a configuração dos extremos e o monitoramento das mudanças deles da melhor forma? O que fazer e como traçar um canal, caso a parte do meio esteja nas barras perdidas? E se o extremo esquerdo de um canal estiver na sexta-feira e o direito na segunda-feira, então os dias de folga sem barras estão entre eles? Como podemos obter os valores atuais das bordas de um canal?

Essas e outras perguntas são respondidas no primeiro artigo da série de artigos sobre canais. Aqui você também pode encontrar a implementação do traçado de canais através de três extremos especificados que utilizam as classes padrão e a abordagem orientada a objetos. Implementaremos o traçador de canal na forma de um indicador.


Configuração de extremos

Na verdade, a posição de um canal no gráfico é determinada por pelo menos três extremos. Se utilizarmos uma definição para um extremo, podemos aceitar esta: ele é o valor máximo ou mínimo de uma função em um determinado intervalo. Um ponto em que se alcança um extremo é chamado de ponto extremo. Respectivamente, caso se atinja o mínimo, o ponto extremo é chamado de ponto mínimo, e se for o máximo, então ele é chamado de ponto máximo.

A análise matemática define outro termo - um extremo local (o mínimo e o máximo, respectivamente). No ponto máximo (mínimo), o valor da função é maior (menor) que os valores de todos os pontos adjacentes. A definição foi obtida na Wikipedia (traduzida do russo).

Para a finalidade de traçar canais, precisamos de extremos locais. Vamos demonstrar isso graficamente, sem entrar nas fórmulas matemáticas. Na figura 1 localizada abaixo, há três extremos locais marcados com níveis de preço vermelhos. Os pontos retangulares mostram dois máximos e um mínimo:

Figura 1. Exemplos de extremos locais

Figura 1. Exemplos de extremos locais

Nem todos os extremos existentes estão marcados no gráfico, e sim apenas os mais significativos. Para um gráfico candlestick ou um gráfico de barras, é conveniente utilizar o termo "fractal" para definir extremos - quando diversas barras adjacentes à esquerda e à direita estão estritamente em declínio ou ascensão (veja a Figura 1).

Porque não temos o objetivo de fazer um traçador de canais automático, então a posição dos extremos serão configuradas conforme exibido na Figura 1 - através da posição nos eixos de hora e preço. O mais adequado para isso é o uso das etiquetas de preço - os objetos gráficos especiais do terminal do cliente MetaTrader 5. Uma etiqueta de preço possui o horário e as propriedades de coordenadas de preços, o que permite identificar sem dúvidas um ponto extremo em um gráfico.


O objeto para armazenar extremos é a classe TExtremum

A primeira coisa que vamos fazer é desenvolver uma classe contêiner para armazenar extremos e uma classe para manipular um grupo de extremos. Visto que utilizaremos o máximo possível as classes padrão incluídas no terminal, a classe TExtremum será herdada da classe padrão CObject. A descrição da nossa classe é fornecida abaixo:

class TExtremum : public CObject
{
private:
  datetime  extr_datetime;              // data/time in an extremum point
  double    extr_price;                 // price in an extremum point
        
protected:
  virtual int  Compare(const CObject* _node, int _mode = 0) const;

public:
  void      TExtremum();               // constructor
  void      ~TExtremum();              // destructor
  void      SetExtremum(datetime _time, double _price);  // change date/time and price in an extremum point
  void      SetDateTime(datetime _time);                 // change date/time in an extremum point
  void      SetPrice(double _price);  // change price in an extremum point

public:
  datetime  GetDateTime() const;      // get date/time in an extremum point
  double    GetPrice() const;         // get price in an extremum point

public:
  virtual bool  SaveExtremum(string _dt_name, string _p_name);  // save extremum
  virtual bool  LoadExtremum(string _dt_name, string _p_name);  // load extremum
  virtual bool  DeleteExtremum(string _dt_name, string _p_name);// delete extremum
};

A maioria dos métodos são triviais e não vale a pena prestarmos atenção à implementação deles. Devemos nos ater é ao método TExtremum::Compare. Esse método é declarado na classe CObject e é utilizado para organização dentro de uma lista. Fizemos a implementação da seguinte maneira:

//---------------------------------------------------------------------
//  Comparing two extremums by time:
//---------------------------------------------------------------------
int TExtremum::Compare(const CObject* _node, int _mode = 0) const
{
  datetime  temp = ((TExtremum* )_node).GetDateTime();
  datetime  curr = GetDateTime();
  if(curr > temp)
  {
    return(_mode > 0 ? 1 : -1);
  }
  else if(curr < temp)
  {
    return(_mode > 0 ? -1 : 1);
  }

  return(0);
}

Aqui, o parâmetro _mode destina-se a estabelecer uma direção de organização. Se ele for maior que zero, então a organização é direta (crescente), caso contrário, ela é reversa (decrescente).

Além disso, há dois métodos projetados para salvar/carregar um extremo. Vamos armazenar o nosso extremo em variáveis globais. Aqui estão esses métodos:

//---------------------------------------------------------------------
//  Save extremum (date/time):
//---------------------------------------------------------------------
bool TExtremum::SaveExtremum(string _dt_name, string _p_name)
{
  datetime  dt_result = GlobalVariableSet(_dt_name, (double)extr_datetime);
  datetime  p_result = GlobalVariableSet(_p_name, (double) extr_price);
  if(dt_result != 0 && p_result != 0)
  {
    return(true);
  }

  return(false);
}

//---------------------------------------------------------------------
//  Load extremum (date/time):
//---------------------------------------------------------------------
bool TExtremum::LoadExtremum(string _dt_name, string _p_name)
{
  double  dt_temp, p_temp;
  bool    result = GlobalVariableGet(_dt_name, dt_temp);
  result &= GlobalVariableGet(_p_name, p_temp);
  if(result != false)
  {
    extr_datetime = (datetime)dt_temp;
    extr_price = p_temp;
    return(true);
  }

  return(false);
}

Dois métodos de leitura/escrita em variáveis TExtremum::LoadExtremum e TExtremum::SaveExtremum retornam "verdadeiro" em caso de execução exitosa.


Manipulação da lista de extremos - a classe TExtremumList

Visto que precisamos tanto armazenar como organizar os extremos pelo horário, então, devemos herdar a classe TExtremumList da classe padrão CList. Com essa herança, obtemos um manipulador universal de extremos sem limites de número e tipo. Isso permite a expansão posterior do número de canais que são traçados. Por exemplo, podemos adicionar o traçado do canal, se de regressão não linear, através de vários extremos.

A descrição dessa classe é fornecida abaixo:

class TExtremumList : public CList
{
private:
  string              channel_prefix;     // channel name (prefix)
  ENUM_TIMEFRAMES      chart_timeframe;    // current timeframe
  string              chart_symbol;       // work symbols of the chart

protected:
  string    MakeDTimeName(int _nmb);     // get name for saving/reading data/time of an extremum
  string    MakePriceName(int _nmb);     // get name for saving/reading price of an extremum

public:
  void      TExtremumList();             // конструктор
  void     ~TExtremumList();             // деструктор
  void     SetChannelParams(string _pref, string _symbol = NULL, ENUM_TIMEFRAMES _curr_tf = PERIOD_CURRENT);
  void     AddExtremum(datetime _time, double  _price);
  void     DeleteAllExtremum();
  void     SaveExtremumList();
  void     LoadExtremumList();
  int      FindExtremum(datetime _dt);  // search extremum by specified time

public:
  datetime GetDateTime(int _index);
  double   GetPrice(int _index);
};

O principal método dessa classe é TExtremumList::AddExtremum. Ele se destina à adição de um novo extremo à lista. A organização de extremos na lista pelo horário do ponto extremo é realizada após a adição. O código desse método é fornecido abaixo:

void TExtremumList::AddExtremum(datetime _time, double  _price)
{
//  Create extremum:
  TExtremum*    extr = new TExtremum();
  extr.SetExtremum(_time, _price);

//  Add it in the list:
  Add(extr);

//  Sort:
  Sort(1);
}

Os seguintes métodos da classe base são usados aqui: CList::Add - para adicionar um novo elemento à lista e CList::Sort - para organizar os elementos na lista. O método TExtremum::Compare é usado em CList::Sort.

Vamos observar o método de busca de um extremo dentro de um horário específico na lista, TExtremumList::FindExtremum. O código do método é fornecido abaixo:

int TExtremumList::FindExtremum(datetime _dt)
{
  int           k = 0;
  TExtremum*    extr = (TExtremum*)(GetFirstNode());
  while(extr != NULL)
  {
    if(extr.GetDateTime() == _dt)
    {
      return(k);
    }
    extr = (TExtremum*)(GetNextNode());
  }
  return(-1);                     // extremum not found
}

Os seguintes métodos da classe base são usados aqui: CList::GetFirstNode - para obter o primeiro elemento da lista (se a lista estiver vazia, ele retorna um ponteiro zero) e CList::GetNextNode - para obter o próximo elemento da lista (se não houver um próximo elemento e a lista tiver terminado, ele retorna um ponteiro zero).

Observação:

Há um ponteiro para um elemento atual nos dados internos da lista de classe CList. Este ponteiro é alterado quando se chama os métodos de movimento na lista (CList::GetFirstNode, CList::GetNextNode, CList::GetPrevNode, etc.). Se nenhum desses métodos foi chamado antes, o ponteiro para um elemento atual aponta para o primeiro.

Caso um extremo com o horário especificado seja encontrado com êxito, o método TExtremumList::FindExtremum indica o elemento encontrado. Se esse elemento não existir, ele retorna -1.

Os métodos TExtremum::MakeDTimeName e TExtremum::MakePriceName são auxiliares. Eles são projetados para obtenção de nomes de variáveis globais que são utilizadas ao salvar e ler extremos. Esses métodos possuem a seguinte implementação:

string TExtremumList::MakeDTimeName(int _nmb)
{
  string    name;
  StringConcatenate(name, channel_prefix, "_", channel_symbol, "_", channel_timeframe, "_DTime_Extr", _nmb);
  return(name);
}

string TExtremumList::MakePriceName( int _nmb )
{
  string    name;
  StringConcatenate(name, channel_prefix, "_", channel_symbol, "_", channel_timeframe, "_Price_DTime_Extr", _nmb);
  return(name);
}

Um exemplo de nome obtido: "MainChannel_EURUSD_5_DTime_Extr1". Esse nome corresponde a um ponto extremo temporário do canal MainChannel (nome convencional), o símbolo EURUSD, o período de tempo 5M e o número de extremo 1. O número para um extremo é atribuído na ordem crescente de horário, iniciando a partir de 1. Praticamente, ele é o índice deslocado em 1 em uma lista organizada de forma crescente.

Um exemplo do valor de três extremos salvos no terminal é exibido na figura abaixo:

Figura 2. Extremos armazenados nas variáveis globais

Figura 2. Extremos armazenados nas variáveis globais

As classes descritas acima estão anexas ao artigo no arquivo ExtremumClasses.mqh.


Indicador para configuração manual dos extremos - ExtremumHandSet

Bem, temos todo o necessário para o desenvolvimento do primeiro indicador, através do qual estabeleceremos a posição dos extremos no modo manual. O código do indicador está anexo ao artigo no arquivo ExtremumHandSet.MQ5. Vamos analisar o traçado em detalhes.

Antes de mais nada, vamos imaginar visualmente o que desejamos ver na tela:

Figura 3. Indicador de configuração de extremos

Figura 3. Indicador de configuração de extremos

Utilizando as etiquetas de preço, estabelecemos as posições dos extremos nos eixos de hora e preço. O indicador deve determinar a posição dessas etiquetas no gráfico, exibir pontos extremos temporários na tela e salvá-los nas variáveis globais do terminal do cliente no formato descrito acima. Além disso, o indicador deve rastrear o movimento das etiquetas de preço no gráfico e corrigir os pontos extremos temporários carregados.

O rastreamento do movimento das etiquetas de preço no gráfico será realizado uma vez por segundo. Isso permite que o sistema seja independente do recebimento de cotações e dias úteis/não úteis.

Primeiramente, vamos conectar as bibliotecas requeridas:

//---------------------------------------------------------------------
//  Included libraries:
//---------------------------------------------------------------------
#include  <TextDisplay.mqh>
#include  <ExtremumClasses.mqh>

A primeira biblioteca contém as classes para organização da exibição de informações de texto na tela (veja o artigo "Crie sua própria observação do mercado usando as classes da biblioteca padrão"). Utilizando ela, exibiremos os valores dos pontos extremos temporários.

Em seguida, adicionamos os parâmetros de entrada do indicador (apenas o principais estão descritos aqui):

input string  PrefixString = "MainChannel";
//---------------------------------------------------------------------
input color   ExtremumPointColor = Yellow;
//---------------------------------------------------------------------
input bool    ShowInfo = true;

O primeiro parâmetro, PrefixString, estabelece um prefixo que é usado para compor os nomes de variáveis globais ao escrever/ler um extremo. Ele também possibilita o uso de diversos indicadores desse tipo em um único gráfico. A única coisa a ser feita é estabelecer diferentes prefixos para eles.

O parâmetro ExtremumPointColor define uma cor para as etiquetas de preço à esquerda que determinam a posição dos extremos. As etiquetas de preço devem ter uma cor específica. Essa concordância é verificada no indicador. Etiquetas com parâmetros diferentes são ignoradas.

O parâmetro ShowInfo controla a exibição de informações de texto relativas aos pontos extremos especificados na tela.

Em seguida, vamos criar os objetos para exibição de informações e manipulação dos extremos:

TableDisplay    TitlesDisplay;    // displaying information on the screen
//---------------------------------------------------------------------
TExtremumList*  PrevExtr_List;    // list of previous extremums
TExtremumList*  CurrExtr_List;    // list of current extremums
TExtremumList*  NewExtr_List;     // list of new extremums

Esses objetos são inicializados a seguir:

PrevExtr_List = new TExtremumList();
PrevExtr_List.SetChannelParams(PrefixString, Symbol(), Period());
PrevExtr_List.LoadExtremumList();

CurrExtr_List = PrevExtr_List;

NewExtr_List = new TExtremumList();
NewExtr_List.SetChannelParams(PrefixString, Symbol(), Period());

Na lista PrevExtr_List, carregados extremos a partir das variáveis globais utilizando o método TExtremumList::LoadExtremumList. Essa lista armazenará os extremos para compará-los com novos, os quais serão lidos a partir de um gráfico ao arrastar-se as etiquetas de preço na tela.

A lista CurrExtr_List é usada como a atual. Ela armazena os extremos atuais. Visto que no começo temos apenas os extremos lidos a partir das variáveis globais, eles são tomados como os atuais.

Na lista NewExtr_List, escreveremos os novos extremos encontrados no gráfico.

Vamos observar as funções principais que são utilizadas no indicador. A primeira função, FindExtremumPoints, é utilizada para ler e verificar os parâmetros das etiquetas de preço que determinam a posição dos extremos:

bool FindExtremumPoints(long _chart_id)
{
  string  name;

//  1. Busca pelo número total de objetos com parâmetros específicos e os escreve na lista:
  int total_objects = ObjectsTotal(_chart_id, -1, OBJ_ARROW_LEFT_PRICE);
  if(total_objects <= 0)
  {
    return(false);
  }

  NewExtr_List.Clear();
  for(int i = 0; i < total_objects; i++)
  {
    name = ObjectName(_chart_id, i, -1, OBJ_ARROW_LEFT_PRICE);

    if( IsGraphicObjectGood(_chart_id, name, OBJ_ARROW_LEFT_PRICE, ExtremumPointColor) == true)
    {
      NewExtr_List.AddExtremum(ObjectGetInteger( _chart_id, name, OBJPROP_TIME),
                               ObjectGetDouble(_chart_id, name, OBJPROP_PRICE));
    }
  }

//  2. Se forem encontrados três extremos, podemos tentar traçar um canal:
  if(NewExtr_List.Total() == 3)
  {

//  Save the list of new extremums:
    NewExtr_List.SaveExtremumList();
    return(true);
  }

  NewExtr_List.Clear();
  return(false);
}

Antes de tudo, a lista NewExtr_List é limpa através da chamada do método TExtremumList::Clear e, então, todos os pontos extremos encontrados, que contêm os parâmetros especificados, são adicionados a ela. Se o número de pontos encontrados é três, a lista é salva nas variáveis globais e a função retorna "verdadeiro".

A outra função, CheakExtremumMoving, rastreia o movimento dos pontos extremos no gráfico. Se pelo menos um ponto se move ao longo do eixo de hora no gráfico, essa função retorna "verdadeiro".

O seu código é apresentado abaixo:

//---------------------------------------------------------------------
//  Check whether extremums have been moved on the screen:
//---------------------------------------------------------------------
bool CheakExtremumMoving()
{
  if(FindExtremumLines(0) == true)
  {
    int  count = NewExtr_List.Total();
    int  index;
    for(int i = 0; i < count; i++)
    {
      index = CurrExtr_List.FindExtremum(NewExtr_List.GetDateTime(i));

//  If a new extremum is found:
      if(index == -1)
      {
        PrevExtr_List = CurrExtr_List;
        CurrExtr_List = NewExtr_List;
        return(true);
      }
    }
    CurrExtr_List = PrevExtr_List;
  }

  return(false);
}

Consideramos a forma de configurar pontos extremos no modo manual. Temos o indicador pronto que permite controlar este processo e escrever os pontos nas variáveis globais. O código completo do indicador está no arquivo ExtremumHandSet.mq5 anexo. Agora podemos passar à parte principal - traçar um canal.


Traçar um canal - breve teoria

Um canal linear consiste em duas linhas paralelas que correm estritamente entre pontos extremos. Além disso, uma linha deve passar por dois pontos e a outra deve passar pelo ponto restante, de forma paralela à primeira linha. Isso pode ser mostrado em uma simples figura:

Traçar um canal utilizando três pontos extremos

Figura 4. Traçar um canal utilizando três pontos extremos

Como aprendemos em geometria, apenas uma linha reta pode ser traçada entre dois pontos. Essa linha tem a cor vermelha na Figura 4. Ela passa por dois pontos que têm as seguintes coordenadas - (T1, P1) e (T2, P2); os pontos estão marcados com as letras A e B. A equação dessa linha é:

(1) P(t) = P1 + (t - T1)*(P2 - P1) / (T2 - T1); P(t) aqui é o preço calculado na hora 't'.


Através do ponto C (o terceiro extremo), devemos traçar outra linha reta paralela à primeira. Essa linha tem a cor verde na Figura 3. Visto que os pontos T1 e T2 são os mesmos para as duas linhas, devemos encontrar os valores de P1' e P2' (veja a Figura 4).

Antes de passar adiante, precisamos fazer uma observação importante. O gráfico do terminal não exibe "buracos" de tempo. Por exemplo, os dias de folga, em que as cotações não são recebidas pelo terminal, devem ser exibidos como quebras de preços. E é ruim que não seja assim. Qual o sentido em observar um gráfico vazio? Entretanto, se utilizarmos um tempo absoluto na equação acima, obteremos um canal errado.

Felizmente, a situação não é irremediável. Se alterarmos o tempo absoluto para o número relativo de uma barra, seremos capazes de usar aquelas coordenadas para traçar um canal, porque a enumeração das barras não pode ter quebras (praticamente, é um índice em um array de preços).

Se seguirmos adiante e considerarmos que o ponto A na Figura 4 sempre está em uma coordenada zero (barra zero) do eixo de hora, então a nossa equação ficará ainda mais fácil. Assim, T1=0, T3=B3,Т2=В2. Aqui, В3 e В2 são os números de uma barra em relação ao ponto Т1 (ou seja, o ponto zero). é claro que esta hipótese não leva a uma inclinação da linha. Então, obtemos a seguinte equação de uma linha reta que passa pelos pontos A e B:

(2) P(n) = P1 + n * (P2-P1) / B2, em que P(n) é o preço calculado para uma barra que possui o número "n".


Assim, sabemos os valores P1, P2, P3 e B2, B3. Agora precisamos encontrar os valores P1' e P2'. Ao combinar e resolver duas equações, obtemos as seguintes fórmulas, com o uso das quais podemos encontrar os valores desconhecidos:

(3) P1' = P3 - B3 * (P2 - P1) / B2

(4) P2' = P2 - P1 + P1'


Quando encontrarmos o valor P1' e o substituirmos pela fórmula (4), obteremos o valor P2'. Agora temos a base teórica para traçar um canal. Vamos começar a implementação.


Traçar bordas de canal - a classe TChannelBorderObject

Esta classe deriva da classe padrão CChartObjectTrend. A sua finalidade é armazenar todos os parâmetros conectados às bordas de um canal bem como traçar/apagar as linhas de borda e controlar os parâmetros gráficos destas linhas.

A descrição dessa classe é fornecida abaixo:

class TChannelBorderObject : public CChartObjectTrend
{
//  General properties of a border:
private:
  bool             is_created;       // whether the graphical object is created on the screen
  long             chart_id;         // identifier of the chart window
  int              window;           // identifier of the subwindow

//  Parameters of a border line:
private:
  string           border_name;      // name of the border line
  color            border_color;     // color of the border line
  int              border_width;     // thickness of the border line
  ENUM_LINE_STYLE   border_style;     // style of the border line

//  Coordinates of a border:
private:
  datetime         point_left;       // time of the left point (T1)
  datetime         point_right;      // time of the right point (T2)
  double           price_left;       // price of the left point (P1)
  double           price_right;      // price of the right point (P2)

public:
  void     TChannelBorderObject();  // constructor
  void    ~TChannelBorderObject();  // destructor
  bool     IsCreated();             // check whether the line is created

//  Creating/deleting a line:
public:
  bool     CreateBorder(long _chart_id, int _window, string _name, datetime _t_left, datetime _t_right, 
                           double _p_left, double _p_right, color _color, int _width, ENUM_LINE_STYLE _style);
  bool     CreateBorder(long _chart_id, int _window, string _name, datetime _t_left, datetime _t_right, 
                           double _p_left, double _p_right);
  bool     CreateBorder(datetime _t_left, datetime _t_right, double _p_left, double _p_right);
  bool     RemoveBorder();          // delete line from the chart

//  Setting parameters of the line:
public:
  void     SetCommonParams(long _chart_id, int _window, string _name);
  bool     SetBorderParams(color _color, int _width, ENUM_LINE_STYLE _style);
  bool     SetBorderColor(color _color);
  bool     SetBorderWidth(int _width);
  bool     SetBorderStyle(ENUM_LINE_STYLE _style);

//  Getting values on the line:
  double   GetPrice(datetime _dt); // get price value in the specified position of the border line
};

Essa classe não requer comentários especiais.

Vamos nos ater apenas ao método de obtenção do preço da borda em um ponto especificado:

//---------------------------------------------------------------------
//  Get price value in the specified position of the border line:
//---------------------------------------------------------------------
double TChannelBorderObject::GetPrice(datetime _dt)
{
//  If the graphical object is created:
  if(is_created == true)
  {
    return(ObjectGetValueByTime( chart_id, border_name, _dt));
  }
  return(0.0);
}

A função ObjectGetValueByTime do terminal é utilizada aqui; ela retorna o valor do preço para um horário especificado. é conveniente utilizar as possibilidades do terminal em vez de calcular o valor através de uma fórmula matemática.


Traçar um canal - a classe TSlideChannelObject

Esta classe deriva da classe padrão CList. O seu objetivo é o seguinte:

  • armazenar objetos da classe TChannelBorderObject e realizar diferentes ações com eles;
  • calcular pontos para traçar linhas requeridas que compõem um canal;
  • armazenar e modificar os parâmetros de um canal;
  • obter valores calculados que descrevem um canal traçado (sua altura, valores de preço nas bordas, etc.);

O código que descreve esta classe é muito grande para ser exibido integralmente aqui. Os interessados podem vê-lo no arquivo SlideChannelClasses.mqh anexo ao artigo. Vamos analisar algumas de suas partes principais.

Primeiramente, ela obtém os valores B2 e B3 nos pontos T2 e T3, respectivamente (veja a Figura 4). O seguinte código é utilizado:

//  Get relative shifts in bars relatively to the extremum points:
  total_bars = Bars(symbol, time_frame);     // total number of bars in history
  if(total_bars == 0)
  {
    return(false);                           // channel cannot be drawn
  }
  double  B2 = Bars(symbol, time_frame, point_left, point_right);
  double  B3 = Bars(symbol, time_frame, point_left, point_middle);

Para evitar uma situação em que barras ausentes são chamadas, utilizamos a função Barras do terminal, a qual retorna o número de barras no histórico para os símbolos e período especificados. Se a informação ainda não está formada, a função retornará o valor zero; ele é usado para verificação.

Se a função retorna um valor diferente de zero, então podemos obter os valores В2 e В3. Isso é feito com o uso da mesma função (Barras), mas ela é chamada de outra forma. Definimos os valores de hora e obtemos o número de barras dentro desse limite. Visto que a nossa borda esquerda é a mesma, obtemos o deslocamento nas barras para os pontos T2 e T3. O deslocamento para o ponto T1 é sempre igual a zero.

Agora, podemos calcular todos os pontos das linhas do canal. Pode haver no máximo nove deles, visto que o nosso canal exibirá (além das bordas superior e inferior) a linha do meio e as linhas de zonas de percentuais ao redor das bordas e da linha do meio.


Vamos analisar a principal parte do cálculo. O cálculo completo está no método TSlideChannelObject::CalcChannel.

//  Coefficient of the line inclination:
  koeff_A = (price_right - price_left) / B2;

//  Price value on the AB line in the point T3:
  double  P3_AB = price_left + B3 * koeff_A;

// Determine the channel type - 2MAX_1MIN или 1MAX_2MIN:
  if(P3_AB > price_middle)              // 2MAX_1MIN
  {
    channel_type = CHANNEL_2MAX_1MIN;

    left_prices[BORDER_UP_INDEX] = price_left;
    right_prices[BORDER_UP_INDEX] = price_right;
        
    left_prices[BORDER_DN_INDEX] = price_middle - B3 * koeff_A;
    right_prices[BORDER_DN_INDEX] = left_prices[BORDER_DN_INDEX] + (price_right - price_left);
  }
  else if(P3_AB < price_middle)         // 1MAX_2MIN
  {
    channel_type = CHANNEL_1MAX_2MIN;

    left_prices[BORDER_DN_INDEX] = price_left;
    right_prices[BORDER_DN_INDEX] = price_right;
        
    left_prices[BORDER_UP_INDEX] = price_middle - B3 * koeff_A;
    right_prices[BORDER_UP_INDEX] = left_prices[BORDER_UP_INDEX] + (price_right - price_left);
  }
  else
  {
    return( false );                      // channel cannot be drawn (all extremums are on the same line)
  }

Aqui, left_prices e right_prices são os arrays que armazenam as coordenadas de preço das nove linhas do canal. A coordenada de hora de todas as linhas do canal já são conhecidas.

Primeiramente, determine o coeficiente de inclinação de linha (veja a fórmula (2)) koeff_A. Em seguida, calculamos o valor de preço da linha AB no ponto T3 (veja a Figura 4). Isso é feito para determinar qual tipo de canal é especificado para ser traçado - por dois máximos e um mínimo ou por dois mínimos e um máximo. Verificamos qual ponto está mais alto no eixo de preço - o ponto C ou o ponto das coordenadas (P3', T3). Dependendo da posição, determinamos se o canal é do primeiro ou do segundo tipo.

Logo que as coordenadas das duas linhas principais do canal (superior e inferior) sejam determinadas, o cálculo das sete outras linhas não é algo difícil. Por exemplo, calculamos as coordenadas da linha do meio utilizando as coordenadas das bordas superior e inferior do canal da seguinte forma:

  left_prices[BORDER_MD_INDEX] = (left_prices[BORDER_DN_INDEX] + left_prices[BORDER_UP_INDEX ]) / 2.0;
  right_prices[BORDER_MD_INDEX] = (right_prices[BORDER_DN_INDEX] + right_prices[BORDER_UP_INDEX]) / 2.0;

Apenas utilize o valor médio das bordas superior e inferior do canal.


Indicador para traçar um canal através de extremos especificados - SlideChannel

Bem, já temos a classe para traçar um canal. Agora vamos escrever um indicador que lerá os parâmetros dos extremos a partir das variáveis globais e traçar um canal no gráfico. Ele terá o seguinte aspecto:

Figura 5. Exemplo de canal traçado com o uso de extremos

Figura 5. Exemplo de canal traçado com o uso de extremos

As informações sobre o canal traçado também são exibidas aqui - elas são largura, distância em pontos desde o preço atual às bordas do canal e à linha do meio.

Vamos conectar as bibliotecas requeridas:

#include  <TextDisplay.mqh>
#include  <SlideChannelClasses.mqh>

A primeira biblioteca contém as classes para organização da exibição de informações de texto na tela (veja o artigo "Crie sua própria observação do mercado usando as classes da biblioteca padrão"). Utilizando ela, exibiremos os valores dos pontos extremos temporários.

Em seguida, adicione os parâmetros de entrada do indicador (apenas o principais estão descritos aqui):

input string          PrefixString = "MainChannel";
//---------------------------------------------------------------------
input ENUM_TIMEFRAMES  ExtremumTimeFrame = PERIOD_CURRENT;
//---------------------------------------------------------------------
input bool            ShowInfo = true;

O primeiro parâmetro, PrefixString, da mesma forma que o indicador ExtremumHandSet, define um prefixo que é usado para compor os nomes de variáveis globais durante a leitura de extremos. Ele também possibilita o uso de diversos indicadores desse tipo em um único gráfico. A única coisa a ser feita é estabelecer diferentes prefixos para eles.

O parâmetro ExtremumTimeFrame define um período de tempo, o qual será usado para ler os pontos extremos a partir das variáveis globais. Este parâmetro é muito útil. Ele permite traçar canais simultâneos em diferentes períodos de tempo. Por exemplo, se você definir os extremos para H1, você pode traçar o mesmo canal o período de tempo M5. Para fazer isso, apenas adicione o nosso indicador para traçar canais ao gráfico M5; e ele exibirá simultaneamente todas as mudanças.

O parâmetro ShowInfo controla a exibição de informações de texto relativas aos parâmetros do canal na tela.

Em seguida, crie objetos para exibir as informações e traçar o canal:

TableDisplay         ChannalDisplay;  // displaying of general information about a channel on the screen
TableDisplay         BordersDisplay;  // displaying information about the borders of a channel on the screen
//---------------------------------------------------------------------
TSlideChannelObject  Channel;         // drawing of a channel

O objeto para traçar um canal é inicializado da seguinte forma:

  Channel.CreateChannel(PrefixString, 0, 0, Symbol(), period_current, curr_left_point, curr_middle_point, 
                        curr_right_point, curr_left_price, curr_middle_price, curr_right_price);
  Channel.SetBorderWidth(BorderWidth );
  Channel.SetmiddleWidth(middleLineWidth);
  Channel.SetUpBorderColor(UpBorderColor);
  Channel.SetDnBorderColor(DnBorderColor);
  Channel.SetmiddleColor(middleLineColor );
  Channel.ShowBorderZone(ShowBorderPercentageLines);
  Channel.BorderZonePercentage( PercentageZoneSize);
  Channel.Showmiddle(ShowmiddleLine);
  Channel.ShowmiddleZone( ShowmiddlePercentageLines);
  Channel.middleZonePercentage(PercentagemiddleZoneSize);

Aqui, primeiramente criamos um canal chamando o método TSlideChannelObject::CreateChannel e, em seguida, definimos os parâmetros requeridas na linha do canal. A sequência da definição não importa. Você pode fazer ao contrário - estabelecer os parâmetros e criar o canal.

O parâmetro period_current é o período que é utilizado durante a leitura dos extremos a partir das variáveis globais. Ele pode ser diferente do período de um gráfico atual.

Vamos observar as funções principais que são utilizadas no indicador. A primeira função, GetExtremums, é usada para ler a posição dos extremos e atualizar o canal de acordo com os valores obtidos:

void GetExtremums()
{
  double  temp;
  string  name;

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_DTime_Extr1");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_left_point = (datetime)temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_DTime_Extr2");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_middle_point = (datetime)temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_DTime_Extr3");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_right_point = (datetime)temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_Price_Extr1");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_left_price = temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol( ), "_", period_current, "_Price_Extr2");
  if( GlobalVariableGet(name, temp) != false )
  {
    curr_middle_price = temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_Price_Extr3");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_right_price = temp;
  }

//  Update the position of channel:
  Channel.SetExtremums(curr_left_point, curr_middle_point, curr_right_point, 
                       curr_left_price, curr_middle_price, curr_right_price);
}

Para atualizar o canal na tela, utilizamos o método TSlideChannelObject::SetExtremums. Este método calcula novamente as coordenadas das linhas do canal e traça o canal na tela outra vez.

Um exemplo do traçado de um canal em períodos de tempo diferentes é apresentado no vídeo abaixo:


A sequência de indicadores de início não importa, mas o lógico é iniciar primeiro o indicador ExtremumHandSet e, em seguida, adicionar três etiquetas de preço à esquerda da cor amarela (a cor das etiquetas é definida nos parâmetros do indicador; a cor amarela é definida automaticamente) e inicie o indicador SlideChannel que traça o canal através dos extremos especificados.

Para que um canal seja traçado simultaneamente aos extremos do primeiro gráfico, você deve definir um período de tempo no parâmetro ExtremumTimeFrame do indicador SlideChannel igual ao do gráfico em que os extremos são definidos.

Esse é o resultado quando separa-se a função de definição dos pontos extremos do canal da função do seu traçado na tela do terminal.


Conclusão

Consideramos o ciclo completo - desde a definição da posição de um canal na tela ao seu traçado. Tudo pareceu não ser tão complicado, especialmente quando utilizamos as classes padrão e OOP (programação orientada a objetos).

Mas há uma dúvida: como devemos utilizar os canais para trabalhar no mercado? Primeiramente, eles são necessários para a análise técnica do estado atual de um instrumento financeiro. E, em segundo lugar, após a análise, eles são necessários para a tomada de decisões. Os canais podem ajudar muito nisso.

é possível desenvolver um Expert Advisor semiautomático que analisará as bordas de um canal para abrir ou fechar uma posição. Ele pode trabalhar ou abrindo caminho através de uma borda ou reduzindo os preços a partir dela. Este será o assunto do próximo artigo - Métodos para trabalhar com um canal - Redução de preços e passagem forçada.

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

Arquivos anexados |
extremumclasses.mqh (12.58 KB)
extremumhandset.mq5 (11.89 KB)
slidechannel.mq5 (13.7 KB)
textdisplay.mqh (15.21 KB)
Últimos Comentários | Ir para discussão (1)
Felipe Pasqualotto
Felipe Pasqualotto | 15 fev 2019 em 00:38
Good night!
I'm trying to use the gauge, but it does not capture price data.


I compiled it for MT5. I saved the files together with the compiled ones.
I performed according to the video.

I tested it with several assets.

Thank you very much




MainChannel



Criação de Consultores Multiespecializados com base em Modelos de Comércio Criação de Consultores Multiespecializados com base em Modelos de Comércio
Usar a abordagem orientada a objeto no MQL5 simplifica a criação de Expert Advisors de várias moedas/vários sistemas/vários períodos de tempo. Imagine seu único EA comercializar simultaneamente em várias dezenas de estratégias comerciais, em todos os instrumentos disponíveis, e em todos os possíveis prazos! Além disso, a EA é facilmente testada no dispositivo de teste e, para todas as estratégias, incluídas na sua composição, existe um ou vários sistemas de trabalho de gestão de dinheiro.
Cálculos paralelos no MetaTrader 5 Cálculos paralelos no MetaTrader 5
O tempo tem sido de grande valor por toda a história da humanidade, e tentamos não desperdiçá-lo sem necessidade. Este artigo dirá a você como acelerar o trabalho do seu Expert Advisor se seu computador tiver um processador com vários núcleos. Além disso, a implementação do método proposto não requer conhecimento de nenhuma outra linguagem além de MQL5.
Os indicadores das tendências micro, média e principal Os indicadores das tendências micro, média e principal
O foco deste artigo é investigar a possibilidade de automação do comércio e a análise, com base em algumas das ideias descritas no livro por James Hyerczyk "Pattern, Price & Time: Using Gann Theory in Trading Systems" na forma de indicadores e Expert Advisor. Sem pretender ser exaustivo, aqui vamos investigar apenas o Modelo - a primeira parte da teoria Gann.
Desenhando e implementando novos widgets GUI com base no objeto CChartObject Desenhando e implementando novos widgets GUI com base no objeto CChartObject
Depois que escrevi um artigo anterior sobre um Expert Advisor semiautomático com interface GUI, descobri que seria desejável aprimorar a interface com algumas novas funcionalidades para Expert Advisors e indicadores mais complexos. Após familiarizar-me com as classes da Biblioteca Padrão do MQL5, implementei novos widgets. Este artigo descreve um processo de planejamento e implementação de novos widgets GUI MQL5 que podem ser usados em indicadores e Expert Advisors. Os widgets apresentados no artigo são CChartObjectSpinner, CChartObjectProgressBar e CChartObjectEditTable.