English Русский 中文 Español Deutsch 日本語
preview
Como detectar tendências e padrões gráficos usando MQL5

Como detectar tendências e padrões gráficos usando MQL5

MetaTrader 5Negociação | 9 outubro 2023, 11:15
812 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Introdução

Como traders, lidamos todos os dias com gráficos e tentamos interpretá-los corretamente para fazer as previsões e tomar a decisão certa. Em um gráfico, é possível identificar vários padrões que preveem movimentos potenciais de preços. Existem ferramentas úteis podem simplificar nossa tarefa. Este artigo explora a detecção dos padrões de preços mais importantes.

Os seguintes tópicos são abordados: 

Após a leitura deste artigo, você será capaz de identificar máximos e mínimos, determinar tipos de tendências, reconhecer topos duplos e fundos duplos. Escreva o código apresentado aqui por conta própria e teste e desenvolva o que você precisa para obter os melhores resultados antes de usá-los em uma conta real. O objetivo principal deste artigo é compreender o conceito básico de detecção de máximos e mínimos, bem como padrões de gráfico, para melhorar seu código.

No artigo, usaremos o ambiente de desenvolvimento MQL5, integrado ao terminal de negociação MetaTrader 5. Leia a seção "Como escrever código MQL5 no MetaEditor" em meu artigo anterior se você não souber como usar o MQL5 e como carregar e usar o MetaEditor.

Atenção! Todo o conteúdo deste artigo é apresentado "tal qual como está", apenas para fins educacionais e não constitui uma recomendação de trading. O artigo não fornece qualquer garantia de resultados. Tudo o que você colocar em prática com base neste artigo, você faz exclusivamente por sua conta e risco, o autor não garante nenhum resultado.

Detecção de máximos e mínimos

Nesta seção, começaremos identificando máximos e mínimos em um gráfico usando o MQL5 e, em seguida, usaremos isso como base para declarar nossas condições para cada padrão de gráfico.

Máximos:

A formação de um máximo significa que houve um movimento ascendente até um certo nível devido à força dos compradores, seguido pelo aparecimento de vendedores que levaram o preço para baixo. A seguir, um exemplo.   

Máximo

Mínimos:

A formação de um mínimo significa que houve um movimento descendente até um certo nível devido à força dos vendedores, seguido pelo aparecimento de compradores que levaram o preço para cima. A seguir, um exemplo.

Mínimo

Agora, precisamos criar um programa MQL5 ou um Expert Advisor (EA) que possa detectar esses tipos de movimentos. Isso pode ser feito de várias maneiras. Apresentaremos uma delas.

Devemos identificar níveis de preços específicos (máximo e mínimo) e, em seguida, avançar para outros níveis de preços específicos (possível máximo e mínimo) e comparar os máximos e mínimos anteriores com os atuais para determinar se há outro máximo ou mínimo. Para fazer isso, precisamos seguir algumas etapas.

Criaremos uma função dentro da área OnTick() para retornar o máximo ou mínimo. Chamaremos isso de (getNextMove) como uma variável inteira. Abaixo estão os parâmetros que precisamos definir para essa função:

  • Int move - máximo/mínimo.
  • int count - valor startPos.
  • int startPos - posição inicial.
int getNextMove(int move, int count, int startPos)

Dentro desta função, precisamos realizar a seguinte verificação, usando a instrução "if", para determinar o valor dos parâmetros da função. Precisamos verificar se (startPos) é menor que zero, adicionar o valor de (startPos) ao contador e atualizar (startPos) com um valor zero para começar a partir da barra atual.

   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }

Definimos as variáveis (count) e (startPos) dentro da função. A variável (move) será identificada no valor de retorno usando a instrução de retorno, que encerra a execução da função e retorna o valor de (move). O operador ternário (?:) consiste em três expressões, sendo a primeira uma expressão booleana. Se ela for verdadeira, a segunda expressão será executada, caso contrário, a terceira expressão será executada.

Verificaremos se a variável (move) é igual ao máximo na primeira instrução. Se for verdadeira, retornaremos o máximo (High), e se for falsa, retornaremos o mínimo (Low).

Para verificar se o movimento é um máximo, usaremos as funções MODE_HIGH, que são um dos identificadores de séries temporais usados nas funções iHighest() e iLowest() para retornar o preço máximo. Os parâmetros das funções iHighest e iLowest para retornar o índice do maior e do menor valor são:

  • symbol - usa o símbolo atual como uma string const.
  • timeframe - usa Period() para retornar o período de tempo atual como ENUM_TIMEFRAMES.
  • type - usa (ENUM_SERIESMODE) para retornar o tipo de movimento como o identificador da série temporal. Esse tipo é o máximo para iHighest e o mínimo para iLowest.
  • count - usa uma variável inteira (count) para retornar o número de elementos.
  • start - usa uma variável inteira (startPos) para retornar o índice.
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));

Após criar a função que retorna o próximo movimento, criaremos outra função inteira que será a principal para obter o máximo ou mínimo do movimento atual. Chamaremos essa função de (getmove) e forneceremos três variáveis inteiras como parâmetros (move, count e startPos).

int getmove(int move, int count, int startPos)

Dentro da função, precisamos verificar se o movimento não é igual a MODE_HIGH ou MODE_LOW; nesse caso, o valor retornado será (-1).

if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);

Criaremos uma nova variável inteira (currentBar) e a igualaremos a (startPos).

int currentBar=startPos;

Criaremos outra variável inteira (moveReturned) e a igualaremos à função getNextMove com os seguintes parâmetros (move, (count*2+1), currentBar-count).

int moveReturned=getNextMove(move,count*2+1,currentBar-count);

Criaremos um laço usando o "while", pois precisamos verificar uma expressão. Se for verdadeira, o operador será executado. Aqui, na expressão, verificaremos se moveReturned não é igual ao valor de currentBar. Se for verdadeiro, precisamos executar as seguintes instruções:

  • Vamos atualizar a variável (currentBar) usando getNextMove com parâmetros (move, count,currentBar+1).
  • Vamos atualizar a variável (moveReturned) usando getNextMove com parâmetros (move,count*2+1,currentBar-count).
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }

Em seguida, usaremos a função de retorno para encerrar a função, retornando o valor de currentBar.

return(currentBar);

Em seguida, entraremos na função OnTick() e chamaremos o que ajudou a detectar os máximos e mínimos. Primeiro, criaremos três variáveis inteiras.

   int checkBars= 5; 
   int move1;
   int move2;

Vamos atualizar move1 e move2 usando nossa função previamente criada, getmove, com os parâmetros (MODE_HIGH, checkBars, 0) para move1 e (MODE_HIGH, checkBars, move1+1) para move2, a fim de detectar dois máximos.

   move1=getmove(MODE_HIGH,checkBars,0);
   move2=getmove(MODE_HIGH,checkBars,move1+1);

Vamos criar uma linha acima desses dois máximos seguindo estas etapas:

Primeiro, vamos excluir qualquer linha existente usando o (ObjectDelete), que remove um objeto pelo nome. Esta função possui os seguintes parâmetros: o primeiro é o chart_id, que define o identificador do gráfico, e usaremos 0 para o gráfico atual. O segundo parâmetro é o nome do objeto, e usaremos "topLine" como nome.

ObjectDelete(0,"topLine");

Em seguida, vamos criar um novo objeto chamado "topLine" usando a função ObjectCreate. Os parâmetros são os seguintes:

  • chart_id - usa (0) para retornar um long como o identificador do gráfico.
  • name - usa topLine para retornar uma string como o nome do objeto.
  • type - usa OBJ_TREND para retornar o ENUM_OBJECT ou o tipo de objeto.
  • nwin - usa (0) para o gráfico atual como o índice da janela.
  • time1 - deternina o tempo do ponto de ancoragem move2 e retorna o tipo datetime. Usamos iTime(Symbol(),Period(),move2)
  • price1 - determina o preço do ponto de ancoragem move2 e retorna o tipo double. Usamos iHigh(Symbol(),Period(),move2).
  • timeN=0 - determina a hora do ponto de ancoragem move1 e retorna o tipo datetime. Usamos o iTime(Symbol(),Period(),move1).
  • priceN=0 - determina o preço do ponto de ancoragem move1 e retorna o tipo double. Usamos iHigh(Symbol(),Period(),move1).

Como podemos ver, a função iHigh retorna o preço máximo da barra, com os parâmetros sendo o símbolo, o período de tempo e o deslocamento. A função iTime retorna o horário de abertura da barra, com os mesmos parâmetros da função iHigh.

ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iHigh(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iHigh(Symbol(),Period(),move1));

Vamos definir a cor, a largura específica e o tipo de linha para o objeto criado usando a função ObjectSetInteger. Os parâmetros são os seguintes:

  • chart_id - identificador do gráfico (0).
  • name - nome do objeto (TopLine para máximos).
  • prop_id - propriedade do objeto (OBJPROP_COLOR - cor, OBJPROP_WIDTH - largura e OBJPROP_RAY_RIGHT - tipo de linha).
  • prop_value - valor obrigatório (cor - clrRed, largura - 3, tipo de linha - true).
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);

Vamos obter dois mínimos atualizando as variáveis move1 e move2 da mesma forma que fizemos para os máximos, mas desta vez, usaremos o modo MODE_LOW como identificador de série temporal.

   move1=getmove(MODE_LOW,checkBars,0);
   move2=getmove(MODE_LOW,checkBars,move1+1);

A exclusão e a criação do objeto-linha abaixo desses dois mínimos ocorrem da mesma forma que para os máximos, mas o nome do objeto será "bottomLine" e a cor será verde.

   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iLow(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iLow(Symbol(),Period(),move1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);

Aqui está o código completo em um bloco:

//+------------------------------------------------------------------+
//|                                                   moveFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5; 
   int move1;
   int move2;
   move1=getmove(MODE_HIGH,checkBars,0);
   move2=getmove(MODE_HIGH,checkBars,move1+1);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iHigh(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iHigh(Symbol(),Period(),move1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   move1=getmove(MODE_LOW,checkBars,0);
   move2=getmove(MODE_LOW,checkBars,move1+1);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iLow(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iLow(Symbol(),Period(),move1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

Após compilar e executar com sucesso o código, obtemos no gráfico duas linhas para detectar dois máximos, com uma linha vermelha acima deles, e dois mínimos, com uma linha verde abaixo deles. Os exemplos são apresentados a seguir:

moveFinder signal1

Como podemos ver, essas duas linhas podem exibir padrões no gráfico e indicar movimentos de preços. No exemplo dado, observamos um movimento ascendente acentuado, pois temos duas linhas ascendentes e o ângulo superior é mais largo que o inferior. Portanto, elas podem ser ferramentas muito úteis para interpretar a ação do preço.

Aqui está outro exemplo com base em uma ação de preço diferente:

 moveFinder signal2

Aqui estamos lidando com uma outra ação de preço. Temos duas linhas que se movem paralelamente, mas a linha inferior está subindo, enquanto a linha superior está descendo, indicando um equilíbrio entre compradores e vendedores, já que os compradores estão aumentando o preço enquanto os vendedores estão simultaneamente diminuindo o preço.

Abaixo está um exemplo de outro modelo de ação de preço:

moveFinder signal3

Aqui temos duas linhas paralelas descendentes, que podem indicar a força dos vendedores, pois são capazes de empurrar o preço para baixo.

Após aprendermos como detectar máximos e mínimos em um gráfico na parte anterior, podemos desenvolver código para detectar tendências em um gráfico. Aprendemos a identificar dois máximos e dois mínimos. Isso é o que precisamos para determinar uma tendência. Vamos aprimorar nosso código anterior para detectar tendências da melhor maneira possível em um gráfico.

Em termos simples, tendências são movimentos de preços, e esses movimentos podem ser para cima, para baixo ou sem uma direção clara. Vamos considerar os três tipos de movimentos:

Tendência de alta:

Neste caso, os compradores são a força dominante no mercado. No gráfico, podemos ver que o preço está claramente formando mínimos mais altos e máximos mais altos. O gráfico a seguir mostra um exemplo de tendência de alta:

Tendência de alta

Tendência de baixa:

Neste caso, os vendedores são a força dominante no mercado, empurrando os preços para baixo. No gráfico, podemos ver que os preços estão formando máximos mais baixos e mínimos mais baixos.

Exemplo de tendência de baixa:

Tendência de baixa

Movimento lateral:

Neste caso, não há uma direção definida. O movimento lateral pode ter muitas formas. Abaixo estão alguns exemplos:

Sem tendência Sem tendência 2 Sem tendência 3

Agora precisamos criar um EA em MQL5 que possa determinar se temos uma tendência (para cima ou para baixo) ou se não há tendência (movimento lateral). O código a seguir é destinado a criar tal EA:

//+------------------------------------------------------------------+
//|                                                  trendFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5;
   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;
   high1=getmove(MODE_HIGH,checkBars,0);
   high2=getmove(MODE_HIGH,checkBars,high1+1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   low1=getmove(MODE_LOW,checkBars,0);
   low2=getmove(MODE_LOW,checkBars,low1+1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
   if(lowVal1>lowVal2&&highVal1>highVal2)
     {
      Comment("Uptrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }
   else
      if(highVal1<highVal2&&lowVal1<lowVal2)
        {
         Comment("Downtrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
      else
        {
         Comment("Sideways",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

Aqui estão as diferenças neste código para detectar tendências.

Criando quatro variáveis inteiras para dois máximos e dois mínimos e mais quatro variáveis duplas para dois valores máximos e dois valores mínimos dentro do escopo da função OnTick:

   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;

Atualizando dois valores máximos (highVal1, highVal2) usando a função NormalizeDouble para arredondar o resultado dos valores máximos. Seus parâmetros são:

  • value - número que precisamos normalizar. Vamos usar a função iHigh para obter o preço máximo, e seus parâmetros são o símbolo atual (_Symbol), o período de tempo atual (_Period) e o deslocamento do índice (high1 e high2).
  • digits - quantidade de dígitos após a vírgula (5).
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);

Atualização dois valores mínimos (lowVal1, lowVal2) usando a função NormalizeDouble com os mesmos parâmetros que mencionamos antes, com as seguintes diferenças:

  • valor - usamos a função iLow para retornar o preço mínimo. Seus parâmetros são os mesmos, exceto pelo deslocamento do índice, que será low1 e low.
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);

As condições que precisamos estabelecer para determinar as tendências - usaremos a instrução "if". É necessário que o Expert Advisor esteja constantemente verificando os quatro valores máximos e mínimos, em seguida, determinando suas posições em relação uns aos outros e, em seguida, decidindo se temos uma tendência (ascendente ou descendente) ou se é um movimento lateral (sem tendência).

Condições para uma tendência de alta:

Se lowVal1 for maior que lowVal2 e ao mesmo tempo highVal1 for maior que highVal2, temos uma tendência de alta, e o EA deve retornar o seguinte comentário no gráfico:

  • Uptrend (tendência de alta)
  • Current High (máximo atual)
  • Previous High (máximo anterior)
  • Current Low (mínimo atual)
  • Previous Low (mínimo anterior)
   if(lowVal1>lowVal2&&highVal1>highVal2)
     {
      Comment("Uptrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

Condições para uma tendência de baixa:

Se highVal1 for menor que highVal2 e ao mesmo tempo lowVal1 for menor que lowVal2, temos uma tendência de baixa, e o EA deve retornar o seguinte comentário no gráfico:

  • Downtrend (tendência de baixa)
  • Current High (máximo atual)
  • Previous High (máximo anterior)
  • Current Low (mínimo atual)
  • Previous Low (mínimo anterior)
   else
      if(highVal1<highVal2&&lowVal1<lowVal2)
        {
         Comment("Downtrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

Condições para um movimento lateral:

Se as posições das quatro variáveis representarem qualquer coisa diferente das condições de tendência de alta ou de baixa, temos um movimento lateral, e o EA deve retornar o seguinte comentário no gráfico:

      else
        {
         Comment("Sideways",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

Após compilar o código e executar o EA, obteremos os sinais de tendência necessários. Abaixo estão exemplos de testes de tipos de tendência e condições.

Tendência de alta:

trendFinder - sinal de tendência de alta

Na imagem, podemos ver um exemplo de tendência de alta, pois temos mínimos mais altos e máximos mais altos. No canto superior esquerdo do gráfico, vemos um sinal de tendência de alta.

Tendência de baixa:

trendFinder - sinal de tendência de baixa

Na imagem, podemos ver um exemplo de tendência de baixa, pois temos máximos mais baixos e mínimos mais baixos. Recebemos um sinal de tendência de baixa como comentário.

Movimento lateral:

trendFinder - sinal de movimento lateral

Temos um máximo mais baixo e um mínimo mais alto. Isso indica um movimento lateral. Recebemos um sinal de movimento lateral como comentário.

Detecção de topo duplo

Agora é hora de aprimorar nosso código. Vamos fazê-lo detectar certos padrões de preço que podem indicar movimentos potenciais.

Nesta seção, apresentamos exemplos de padrões gráficos com algumas modificações no código para entender a ideia principal e o potencial para detectar outros padrões importantes. Vamos analisar um dos padrões gráficos populares - o topo duplo.

É uma formação composta por máximos aproximadamente iguais, o que indica fraqueza dos compradores e a probabilidade de que os preços caiam. Abaixo está um exemplo:

Potencial topo duplo

Aqui temos um potencial topo duplo. Ela se confirmará se o preço cair abaixo do mínimo entre os dois máximos, como mostrado na próxima imagem:

Topo duplo

Agora precisamos criar um EA MQL5 que possa ser usado para detectar esses dois padrões no MetaTrader 5. Queremos que o EA verifique constantemente dois máximos e dois mínimos e determine suas posições em relação um ao outro. Em seguida, ele deve retornar um resultado específico com base em condições definidas para o padrão "topo duplo". Naturalmente, em condições reais, o padrão raramente é exibido de forma perfeita. Se o máximo atual for menor ou igual ao anterior e, ao mesmo tempo, o mínimo atual for superior que o anterior, isso será um sinal de um potencial topo duplo. Se o máximo atual for menor ou igual ao anterior e, ao mesmo tempo, o mínimo atual for menor que o anterior, isso será um sinal de um topo duplo.

Abaixo está o código completo:

//+------------------------------------------------------------------+
//|                                             DT patternFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5;
   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;
   high1=getmove(MODE_HIGH,checkBars,0);
   high2=getmove(MODE_HIGH,checkBars,high1+1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   low1=getmove(MODE_LOW,checkBars,0);
   low2=getmove(MODE_LOW,checkBars,low1+1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
   if(highVal1<=highVal2&&lowVal1>lowVal2)
     {
      Comment("Potential Double Top",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

   else
      if(highVal1<=highVal2&&lowVal1<lowVal2)
        {
         Comment("Double Top",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
      else
         Comment(" ");
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

O código é diferenciado pelas condições de padrão.

No caso de uma potencial topo duplo, se highVal1 for menor ou igual a highVal2 e lowVal1 for maior que lowVal2, é necessário receber um sinal na forma de um comentário no gráfico com os seguintes valores:

  • Potential Double Top (potencial topo duplo)
  • Current High (máximo atual)
  • Previous High (máximo anterior)
  • Current Low (mínimo atual)
  • Previous Low (mínimo anterior)
   if(highVal1<=highVal2&&lowVal1>lowVal2)
     {
      Comment("Potential Double Top",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

No caso de uma dupla parte superior formada, se highVal1 for menor ou igual a highVal2 e lowVal1 for menor que lowVal2, então precisamos receber um sinal na forma de um comentário no gráfico com os seguintes valores:

  • Double Top (topo duplo)
  • Current High (máximo atual)
  • Previous High (máximo anterior)
  • Current Low (mínimo atual)
  • Previous Low (mínimo anterior)
   else
      if(highVal1<=highVal2&&lowVal1<lowVal2)
        {
         Comment("Double Top",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

No caso da ausência de padrões, nenhum comentário é gerado.

      else
         Comment(" ");

Após a compilação, executamos o Expert Advisor no gráfico.

Potencial topo duplo:

DT patternFinder - potencial topo duplo

Como podemos ver, as condições para acionar o sinal foram satisfeitas: os máximos são iguais e um mínimo está ligeiramente acima do outro.

Topo duplo formado:

 DT patternFinder - topo duplo formado

As condições para acionar o sinal foram satisfeitas: o máximo é menor ou igual e o mínimo é menor.

Detecção de fundo duplo

O duplo fundo é o oposto da dupla parte superior. É uma formação composta por mínimos aproximadamente iguais, o que indica fraqueza dos vendedores e a probabilidade de que os preços subam. Abaixo está um exemplo:

 Potencial fundo duplo

O padrão de duplo fundo é confirmado quando os preços se fecham acima do máximo entre os dois mínimos, conforme ilustrado na figura a seguir:

Fundo duplo

Precisamos criar outro Expert Advisor MQL5 que possa ser usado para detectar as duas formações anteriores no MetaTrader 5. Queremos que o EA verifique constantemente dois mínimos e dois máximos e determine suas posições em relação um ao outro. Em seguida, ele deve retornar um resultado específico com base em condições definidas para o padrão "Duplo Fundo". Se o mínimo atual for maior ou igual ao anterior e, ao mesmo tempo, o máximo atual for menor que o anterior, isso será um sinal de um possível duplo fundo. Se o mínimo atual for maior ou igual ao anterior e, ao mesmo tempo, o máximo atual for maior que o anterior, isso será um sinal de uma dupla parte superior.

Abaixo está o código completo:

//+------------------------------------------------------------------+
//|                                             DB patternFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5;
   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;
   high1=getmove(MODE_HIGH,checkBars,0);
   high2=getmove(MODE_HIGH,checkBars,high1+1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   low1=getmove(MODE_LOW,checkBars,0);
   low2=getmove(MODE_LOW,checkBars,low1+1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
   if(lowVal1>=lowVal2&&highVal1<highVal2)
     {
      Comment("Potential Double Bottom",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }
   else
      if(lowVal1>=lowVal2&&highVal1>highVal2)
        {
         Comment("Double Bottom",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
      else
         Comment(" ");
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

O código é diferenciado pelas condições de padrão.

No caso de um potencial duplo fundo, se lowVal1 for maior ou igual a lowVal2 e highVal1 for menor que highVal2, é necessário receber um sinal na forma de um comentário no gráfico com os seguintes valores:

  • Potential Double Bottom (potencial fundo duplo)
  • Current High (máximo atual)
  • Previous High (máximo anterior)
  • Current Low (mínimo atual)
  • Previous Low (mínimo anterior)
   if(lowVal1>=lowVal2&&highVal1<highVal2)
     {
      Comment("Potential Double Bottom",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

No caso de um duplo fundo formado, se lowVal1 for maior ou igual a lowVal2, e highVal1 for maior que highVal2, então precisamos receber um sinal na forma de um comentário no gráfico com os seguintes valores:

  • Double Bottom (fundo duplo)
  • Current High (máximo atual)
  • Previous High (máximo anterior)
  • Current Low (mínimo atual)
  • Previous Low (mínimo anterior)
   else
      if(lowVal1>=lowVal2&&highVal1>highVal2)
        {
         Comment("Double Bottom",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

Compilamos o código do EA e ele aparecerá na janela do Navegador no terminal, onde podemos executá-lo no gráfico. Abaixo segue um exemplo de como funciona:

Potencial fundo duplo:

 DB patternFinder - potencial fundo duplo

As condições para acionar o sinal foram satisfeitas: um máximo mais baixo e um mínimo igual ou mais alto.

Fundo duplo formado:

Fundo duplo

As condições para acionar o sinal foram satisfeitas: um máximo mais alto e um mínimo igual ou mais alto.

Considerações finais

Um profundo entendimento do comportamento dos preços permite que os traders tomem decisões de investimento e negociação mais eficazes. O comportamento dos preços forma muitos padrões que precisamos conhecer. Tentamos simplificar essa tarefa criando Expert Advisors MQL5 para uso no terminal de negociação MetaTrader 5.

Ao aprender a identificar máximos e mínimos, aprendemos a reconhecer tendências (de alta, de baixa e laterais) e uma das figuras gráficas populares - o topo duplo e seu oposto, o duplo fundo, e a criação de EAs com base neles. Você pode desenvolver as ideias apresentadas no artigo e criar sistemas que detectem outros padrões gráficos, como cabeça e ombros, triângulos, retângulos, etc. Espero que o artigo seja útil para você na elaboração de sistemas de negociação e no alcance de seus objetivos de negociação.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/12479

Arquivos anexados |
moveFinder.mq5 (2.24 KB)
trendFinder.mq5 (3.18 KB)
Desenvolvimento de um indicador Heiken Ashi personalizado usando MQL5 Desenvolvimento de um indicador Heiken Ashi personalizado usando MQL5
Neste artigo, aprenderemos a criar nosso próprio indicador usando MQL5 com base em nossas preferências, que será usado no MetaTrader 5 para interpretar gráficos ou como parte de Expert Advisors.
Desenvolvendo um sistema de Replay (Parte 29): Projeto Expert Advisor — Classe C_Mouse (III) Desenvolvendo um sistema de Replay (Parte 29): Projeto Expert Advisor — Classe C_Mouse (III)
Agora que a classe C_Mouse foi melhorada. Podemos focar em criar uma classe que será usada para promover uma base completamente diferente de estudos. Mas como expliquei no inicio do artigo, não iremos usar herança ou polimorfismo para gerar esta nova classe. Iremos modificar, ou melhor dizendo, agregar alguns objetos novos a linha de preço. Isto neste primeiro momento, no próximo artigo mostrarei como modificar os estudos. Mas faremos isto sem mexer no código da classe C_Mouse. Sei que na pratica, isto seria mais simples ser feito usando herança ou polimorfismo. No entanto, existem técnicas diferentes para se conseguir a mesma coisa.
Estratégia de negociação simples: Reversão à média Estratégia de negociação simples: Reversão à média
A reversão à média é um tipo de negociação contra-tendência em que o trader espera que o preço retorne a algum tipo de equilíbrio, geralmente medido por uma média ou outra estatística de tendência central.
Redes neurais de maneira fácil (Parte 46): Aprendizado por reforço condicionado a metas (GCRL) Redes neurais de maneira fácil (Parte 46): Aprendizado por reforço condicionado a metas (GCRL)
Convido você a conhecer mais uma abordagem no campo do aprendizado por reforço. É chamada de aprendizado por reforço condicionado a metas, conhecida pela sigla GCRL (Goal-conditioned reinforcement learning). Nessa abordagem, o agente é treinado para alcançar diferentes metas em cenários específicos.