
Traçando canais - vista interna e externa
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
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
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
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:
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
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





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
I tested it with several assets.
Thank you very much