English Русский 中文 Español Deutsch 日本語
preview
Introdução ao MQL5 (Parte 17): Criação de EAs para reversões de tendência

Introdução ao MQL5 (Parte 17): Criação de EAs para reversões de tendência

MetaTrader 5Negociação |
23 5
ALGOYIN LTD
Israel Pelumi Abioye

Introdução

Olá novamente e bem-vindos à Parte 17 da série "Introdução ao MQL5"! Nesta parte, continuaremos a desenvolver tudo o que aprendemos até agora, usando nossa abordagem própria baseada em projetos para ajudar você a aprimorar suas habilidades em MQL5 por meio de por meio de exemplos reais. Na Parte 16, nos concentramos no padrão "cabeça e ombros": criamos um EA que conseguia detectá-lo automaticamente, abrir operações e até visualizá-lo no gráfico. Foi uma excelente forma de aprender a trabalhar com padrões gráficos na linguagem MQL5.

Para este artigo, eu havia planejado inicialmente usar um dos padrões de continuação ou reversão, por exemplo, a bandeira, a cunha descendente ou a cunha ascendente. No entanto, quando aprofundei a pesquisa, descobri algo importante: muitos desses padrões têm estrutura semelhante e, muitas vezes, dependem de rompimentos de linhas de tendência ou de reversões a partir delas. Nesta seção, vamos nos concentrar no desenvolvimento de um EA capaz de identificar rompimentos de linhas de tendência e reversões a partir delas, além de operar com base nesses sinais, sem ficar limitado a um único padrão. Depois de dominar essas ideias centrais, você poderá aplicá-las, com alguns ajustes, a diversos padrões gráficos.

Neste artigo, você aprenderá:

  • Como obter o valor de preço exato da linha de tendência no instante de qualquer candle especificado usando a função ObjectGetValueByTime().
  • Identificação de possíveis sinais de rompimento e reversão com base na estrutura dos candles próximos às linhas de tendência.
  • A diferença entre linhas de tendência de alta e de baixa, e como interpretar a interação delas com o price action.
  • A importância de verificar o comportamento recente dos candles, por exemplo, pavios e fechamentos, para confirmar uma rejeição do preço ou um rompimento da linha.
  • Uso prático de comparações temporais para evitar a execução de operações com base em sinais antigos ou irrelevantes.
  • Como criar um EA simples, mas flexível, que opera automaticamente com base nas interações com linhas de tendência.

1. Interpretação das linhas de tendência

Uma linha reta que passa por dois ou mais pontos de preço em um gráfico é chamada de linha de tendência. Quando traçada corretamente, ela ajuda a reconhecer a tendência atual e possíveis oportunidades de negociação. Ao contrário dos níveis horizontais fixos de suporte e resistência, as linhas de tendência têm inclinação e acompanham a direção natural do mercado, que pode ser lateral, de alta ou de baixa.

Como muitos padrões gráficos se formam ou se estruturam em torno de linhas de tendência, elas são especialmente importantes para identificar determinados padrões. Por exemplo, em uma cunha descendente ou ascendente, o preço fica comprimido entre duas linhas de tendência convergentes até ocorrer um rompimento. Nos padrões "bandeira" e "flâmula", as linhas de tendência delimitam a zona de consolidação antes do movimento de continuação. 

1.1. Tipos de linhas de tendência

1.1.1. Linha de tendência de alta

Em uma tendência de alta, a linha de tendência de alta atua como nível de suporte, conectando fundos cada vez mais altos. Ela ajuda a identificar possíveis oportunidades de venda quando o preço rompe abaixo dela, indicando uma possível reversão da tendência ou uma queda mais profunda, bem como oportunidades de compra durante os repiques.

Figura 1. Linha de tendência de alta

1.1.2. Linha de tendência de baixa

A linha de tendência de baixa indica momentum de baixa, enquanto a linha de tendência de alta é inclinada para cima. Em um mercado de baixa, ela é traçada conectando dois ou mais conectando dois ou mais topos cada vez mais baixos. Atuando como nível de resistência, essa linha inclinada para baixo indica que o mercado se move em uma o mercado se move em uma direção geral de baixa.

Durante altas de curto prazo do preço, as linhas de tendência de baixa oferecem oportunidades de venda, pois podem sinalizar uma reversão do preço para baixo quando ele toca a linha. Ao mesmo tempo, os rompimentos também são importantes, assim como no caso das linhas de tendência de alta. O rompimento da linha de tendência de baixa pelo preço pode sinalizar uma provável reversão da tendência de baixa para uma tendência de alta e representar uma possível oportunidade de compra.

Figura 2 Linha de tendência de baixa

A linha de tendência de baixa indica momentum de baixa, enquanto a linha de tendência de alta é inclinada para cima. Em um mercado de baixa, ela é traçada conectando dois ou mais conectando dois ou mais topos cada vez mais baixos. Atuando como nível de resistência, essa linha inclinada para baixo indica que o mercado se move em uma o mercado se move em uma direção geral de baixa.

Ao destacar rompimentos significativos, que muitas vezes indicam mudanças no momentum do mercado, as linhas de tendência as linhas de tendência proporcionam clareza visual e oportunidades táticas para abrir operações. Entender o comportamento das linhas de tendência, em vez de memorizar padrões específicos, fornece uma base universal para o reconhecimento de padrões e é especialmente útil no desenvolvimento de soluções automatizadas, como EAs na linguagem MQL5.


2. Configuração do projeto

2.1. Como o EA funciona

Neste projeto, criaremos um EA que usará as linhas de tendência do gráfico para operar automaticamente. Dependendo da condição do mercado, o EA traça linhas de tendência de alta ou de baixa confirmadas, analisando topos e fundos de swing recentes.

O EA monitora como o preço interage com as linhas de tendência depois que elas são traçadas. Ele executará operações em dois cenários principais. Primeiro, o EA operará na direção da tendência, aguardando uma reversão, isto é, uma situação em que o preço alcança a linha de tendência e reverte. Em segundo lugar, o EA identificará o padrão de rompimento e reteste e abrirá uma operação na direção do rompimento quando o preço romper a linha de tendência e depois testá-la pelo lado oposto. Graças a essa lógica, o EA pode ser usado com vários padrões gráficos, incluindo cunhas, bandeiras, triângulos e canais, que dependem principalmente da dinâmica das linhas de tendência.

2.1.1. Linha de tendência de alta

2.1.1.1. Lógica para compra

  • O EA detectará dois fundos de swing e traçará uma linha de tendência de alta passando por eles.
  • O EA monitora continuamente o movimento do preço em busca de um retorno à linha de tendência, isto é, um toque ou uma aproximação sem rompimento.
  • Quando o preço toca a linha de tendência e imediatamente forma um candle de alta, o EA considera isso um repique confirmado.
  • Após a confirmação do repique de alta na linha de tendência de alta, o EA envia uma ordem de compra.
  • Como SL, o usuário pode definir uma determinada distância em pontos abaixo do preço de entrada ou a mínima do candle de repique.
  • O usuário pode definir o TP como uma determinada distância em pontos acima do preço de entrada.

Figura 3. Lógica de compra na linha de tendência de alta

2.1.1.2. Lógica para venda

  • O EA detectará dois fundos de swing e traçará uma linha de tendência de alta passando por eles.
  • Ele monitora continuamente o preço em busca de um rompimento abaixo dessa linha de tendência de alta.
  • Assim que o preço romper a linha de tendência para baixo, o EA começará a aguardar um reteste, isto é, uma situação em que o preço retorna à linha de tendência, tocando-a por baixo.
  • Se imediatamente após o reteste se formar um candle de baixa, e sua mínima estiver abaixo da linha de tendência, o EA considera isso um rompimento válido e uma confirmação.
  • Após a confirmação do reteste de baixa, o EA envia uma ordem de venda.
  • Como SL, o usuário pode definir uma determinada distância em pontos acima do preço de entrada ou a máxima do candle de reteste.
  • O usuário também pode definir o TP como uma determinada distância em pontos abaixo do preço de entrada.

Figura 4. Lógica de venda na linha de tendência de alta

2.1.2. Linha de tendência de baixa

2.1.2.1. Lógica para compra

  • O EA detectará dois topos de swing e traçará uma linha de tendência de baixa passando por eles.
  • Ele monitora continuamente o preço em busca de um rompimento acima dessa linha de tendência de baixa.
  • Assim que o preço romper a linha de tendência para cima, o EA começará a aguardar um reteste, isto é, uma situação em que o preço retorna à linha de tendência rompida, tocando-a ou aproximando-se dela por cima.
  • Se imediatamente após o reteste se formar um candle de alta, e sua máxima estiver abaixo da linha de tendência, o EA considera isso um rompimento válido e uma confirmação.
  • Após a confirmação do reteste de alta, o EA envia uma ordem de compra.
  • Como stop-loss (SL), o usuário pode definir uma determinada distância em pontos abaixo do preço de entrada ou o fundo do candle de reteste.
  • O usuário também pode definir o take-profit (TP) como certa distância em pontos acima do preço de entrada.

Figura 5. Lógica de compra na linha de tendência de baixa

2.1.2.2. Lógica para venda

  • O EA detectará dois topos de swing e traçará uma linha de tendência de baixa passando por eles.
  • Ele monitora continuamente o preço em busca de um toque na linha de tendência de baixa.
  • Quando o preço toca a linha de tendência e imediatamente forma um candle de baixa, o EA considera isso uma reação confirmada.
  • Após a confirmação da reação de baixa na linha de tendência de baixa, o EA envia uma ordem de venda.
  • Como stop-loss (SL), o usuário pode definir uma determinada distância em pontos acima do preço de entrada ou a máxima do candle de repique.
  • O usuário também pode definir o take-profit (TP) como uma determinada distância em pontos abaixo do preço de entrada.

Figura 6. Lógica de venda na linha de tendência de baixa

Nota:  O objetivo principal da estratégia de trading que será estudada neste projeto é desenvolver seus conhecimentos sobre a linguagem MQL5, especialmente sobre como trabalhar com padrões gráficos e criar EAs úteis. Esta estratégia não se destina ao uso com dinheiro real nem à negociação em tempo real. Antes de implementar qualquer estratégia no mercado real, sempre realize testes abrangentes com dados históricos e consulte seu assessor financeiro.


3. Identificação da linha de tendência de alta e de baixa

Agora que discutimos em detalhes a ideia de como o EA funcionará, podemos começar a implementá-la como um programa. O primeiro passo é implementar no EA o reconhecimento e o traçado automático de linhas de tendência. Antes de traçar uma linha de tendência, o EA encontrará dois pontos de swing relevantes no gráfico. No caso de uma linha de tendência de alta, ele buscará os dois fundos cada vez mais altos mais recentes, que indicam momentum de alta.

No caso de uma linha de tendência de baixa, ele buscará os dois topos descendentes mais recentes, que indicam pressão vendedora. Após identificar esses pontos de swing, o EA os conectará com uma linha de tendência. Em seguida, essa linha servirá como referência para identificar possíveis rompimentos ou reversões.

3.1. Obtenção dos dados dos candles

Uma parte essencial de qualquer projeto relacionado a padrões gráficos são os dados dos candles. Sem eles, é impossível avaliar a estrutura do preço ou identificar topos e fundos de swing. Por esse motivo, a extração de dados históricos de candles do gráfico é a primeira etapa na criação do nosso EA. Esses dados incluem informações importantes de preço, como os o preço de abertura, a máxima, a mínima e o fechamento de cada candle de cada candle. Com esses dados, poderemos reconhecer linhas de tendência, identificar a estrutura do mercado e usar sinais de reversão ou rompimento para encontrar possíveis pontos de entrada em uma operação.

Exemplo:
// Timeframe to use for retrieving candlestick data (default is the current chart timeframe)
input ENUM_TIMEFRAMES time_frame = PERIOD_CURRENT;

// Number of past bars (candlesticks) to check
int bars_check = 500;

// Arrays to store candlestick data
double close_price[];   // Stores close prices
double open_price[];    // Stores open prices
double low_price[];     // Stores low prices
double high_price[];    // Stores high prices
datetime time_price[];  // Stores time data for each candle

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Set arrays as series so the newest bar is index 0 ( start from the latest bar)
   ArraySetAsSeries(close_price, true);
   ArraySetAsSeries(open_price, true);
   ArraySetAsSeries(low_price, true);
   ArraySetAsSeries(high_price, true);
   ArraySetAsSeries(time_price, true);

   return(INIT_SUCCEEDED);  // Signal that the EA initialized successfully
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy the latest candlestick data into the arrays
   CopyOpen(_Symbol, time_frame, 1, bars_check, open_price);     // Open prices
   CopyClose(_Symbol, time_frame, 1, bars_check, close_price);   // Close prices
   CopyLow(_Symbol, time_frame, 1, bars_check, low_price);       // Low prices
   CopyHigh(_Symbol, time_frame, 1, bars_check, high_price);     // High prices
   CopyTime(_Symbol, time_frame, 1, bars_check, time_price);     // Candle times

  }

Explicação:

Para criar um EA que reaja a padrões gráficos, é necessário coletar dados históricos de candles. O código armazena essas informações em cinco arrays: open, close, high, low e time. Esses arrays servem como base para traçar a linha de tendência e identificar pontos de swing. Com o valor padrão PERIOD_CURRENT, o EA usa o timeframe do gráfico atual. Os usuários podem escolher o timeframe em que o EA deve operar, por exemplo, M1, H1 ou D1, usando a variável de entrada time_frame. A variável bars_check, que no nosso caso tem o valor 500, define quantos candles anteriores devem ser considerados. Assim, ignorando o candle que está se formando no momento atual, o EA extrairá e analisará os últimos 500 candles fechados.

Cada um desses arrays é configurado com a função ArraySetAsSeries(..., true); dentro da função OnInit(). Como ela altera a ordem do array, essa etapa é fundamental, pois faz com que o índice 0 fique associado ao candle fechado mais recente, o índice 1, ao candle anterior, e assim por diante. Partindo do movimento mais recente do preço e retrocedendo, essa forma de indexação corresponde à forma como os traders analisam os gráficos intuitivamente. 

O método OnTick() executa a maior parte da extração dos dados. Essa função é acionada a cada tick ou sempre que ocorre uma mudança de preço no mercado. Com ela, o EA extrai os dados dos candles e os coloca nos arrays correspondentes usando as funções CopyOpen, CopyClose, CopyHigh, CopyLow e CopyTime. O deslocamento inicial garante que o EA ignore o candle ainda em formação, pois ele pode mudar rapidamente e não é confiável para detectar padrões; já o segundo argumento define o timeframe selecionado. As informações obtidas correspondem aos últimos bars_check candles fechados.

3.2. Identificação da linha de tendência de alta

A próxima etapa após obter os dados dos candles é procurar pontos de swing no movimento do preço. Nosso primeiro objetivo será encontrar uma linha de tendência de alta, que geralmente aparece quando o mercado forma conectando fundos cada vez mais altos. Isso significa que é preciso encontrar primeiro dois fundos de swing válidos. Para ajudar o EA a identificar possíveis setups de alta, como reações ou rompimentos nessa linha de suporte ascendente, esses fundos de swing servirão como pontos de ancoragem para traçar a linha de tendência de alta.

Exemplo:

// Timeframe to use for retrieving candlestick data (default is the current chart timeframe)
input ENUM_TIMEFRAMES time_frame = PERIOD_CURRENT;
// Input to enable or disable drawing of the ascending trend line (true = allow drawing)
input bool allow_uptrend = true;
// Number of candles to look back when identifying swing lows for drawing the trend line
input int LookbackBars = 5;

// Number of past bars (candlesticks) to check
int bars_check = 500;

// Arrays to store candlestick data
double close_price[];   // Stores close prices
double open_price[];    // Stores open prices
double low_price[];     // Stores low prices
double high_price[];    // Stores high prices
datetime time_price[];  // Stores time data for each candle

double first_low;           // Price value of the first identified swing low
datetime first_low_time;    // Time when the first swing low occurred

double second_low;          // Price value of the second identified swing low
datetime second_low_time;   // Time when the second swing low occurred

string up_trend = "Up Trend";  // Label used to name the ascending trend line object on the chart

long chart_id = ChartID();     // Stores the current chart ID for referencing during object creation or manipulation

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Set arrays as series so the newest bar is index 0 ( start from the latest bar)
   ArraySetAsSeries(close_price, true);
   ArraySetAsSeries(open_price, true);
   ArraySetAsSeries(low_price, true);
   ArraySetAsSeries(high_price, true);
   ArraySetAsSeries(time_price, true);

   return(INIT_SUCCEEDED);  // Signal that the EA initialized successfully
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

   ObjectsDeleteAll(chart_id);

  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy the latest candlestick data into the arrays
   CopyOpen(_Symbol, time_frame, 1, bars_check, open_price);     // Open prices
   CopyClose(_Symbol, time_frame, 1, bars_check, close_price);   // Close prices
   CopyLow(_Symbol, time_frame, 1, bars_check, low_price);       // Low prices
   CopyHigh(_Symbol, time_frame, 1, bars_check, high_price);     // High prices
   CopyTime(_Symbol, time_frame, 1, bars_check, time_price);     // Candle times

// If the user allows drawing of ascending trend line
   if(allow_uptrend)
     {
      // First loop: Find the most recent swing low (first low)
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check if current point is a swing low
         if(IsSwingLow(low_price, i, LookbackBars))
           {
            // Store price and time of the first (latest) swing low
            first_low = low_price[i];
            first_low_time = time_price[i];
            break;  // Exit loop after finding the first swing low
           }
        }

      // Second loop: Find an earlier swing low that is lower than the first low
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check for earlier swing low that is lower and occurs before the first low
         if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time)
           {
            // Store price and time of the second (older) swing low
            second_low = low_price[i];
            second_low_time = time_price[i];
            break;  // Exit loop after finding the second swing low
           }
        }

      // Create an ascending trend line from the second low to the first low
      ObjectCreate(chart_id, up_trend, OBJ_TREND, 0, second_low_time, second_low, first_low_time, first_low);
      ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);  // Temporarily hide line on all timeframes

      // If the swing structure is valid (i.e., second low is lower than first)
      if(first_low > second_low && second_low > 0)
        {
         // Extend the trend line to the right
         ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true);

         // Show the trend line on all timeframes
         ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

         // Set visual properties: color and thickness
         ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue);
         ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, 3);
        }
     }

  }

//+------------------------------------------------------------------+
//| FUNCTION FOR LOWS                                                |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index, int lookback)
  {

   for(int i = 1; i <= lookback; i++)
     {

      if(low[index] > low[index - i] || low[index] > low[index + i])
         return false;
     }
   return true;
  }

Saída:

Figura 7. Identificação da linha de tendência de alta

Explicação:

A função IsSwingLow tem como objetivo encontrar fundos de swing na série de preços. Ela recebe três argumentos: o array de mínimas (low[]), um índice específico dentro desse array (index) e o valor do intervalo de verificação lookback, que indica quantas barras à esquerda e à direita devem ser verificadas. O loop for dentro do método é executado de 1 até lookback. Em cada iteração, ele compara a mínima no index atual com as mínimas em index + i, à direita, e index - i, à esquerda.

Se a mínima atual estiver acima de qualquer uma das mínimas vizinhas, a função retorna false, indicando que o ponto não é um fundo de swing. Se o valor atual da mínima estiver abaixo de todas as mínimas vizinhas no intervalo de verificação, a função retorna true, indicando que um fundo de swing foi detectado. Em essência, isso garante que a mínima no índice especificado seja uma mínima local, ou seja, a mínima dentro de sua vizinhança.

A lógica da linha de tendência é controlada por duas variáveis de entrada principais. A primeira variável, allow_uptrend, representa um valor booleano que permite aos usuários escolher se a linha de tendência será traçada como uma linha de tendência de alta. Se essa variável estiver definida como true, o código tentará encontrar dois fundos de swing e traçar uma linha de tendência conectando-os. A segunda variável de entrada, LookbackBars, indica quantos candles devem ser considerados à esquerda e à direita do ponto para determinar se ele é um fundo de swing. A função IsSwingLow usa o mesmo valor de intervalo de verificação. O critério para um fundo de swing fica mais sensível com um valor menor e mais rigoroso com um valor maior, exigindo um fundo mais pronunciado.

Os preços dos dois fundos de swing mais recentes mais recentes encontrados nos dados dos candles serão armazenados nas variáveis first_low e second_low. Os horários em que esses fundos de swing ocorreram serão armazenados nas variáveis first_low_time e second_low_time, respectivamente. Em seguida, com base nesses pares "tempo-preço", será traçada uma linha de tendência de alta. O ID do gráfico atual será armazenado na variável chart_id, e o nome do objeto da linha de tendência, um rótulo simples "Up Trend", será armazenado na variável up_trend. Ao criar ou modificar objetos gráficos, como linhas de tendência, é necessário usar o identificador do gráfico para definir o contexto correspondente.

O EA começa procurando dois fundos de swing válidos quando allow_uptrend está definido como true. Para evitar o acesso a elementos do array fora dos limites, o primeiro loop for percorre os dados de preço de LookbackBars até bars_check - LookbackBars. A função IsSwingLow verifica se cada ponto é um fundo de swing. Depois de identificar o fundo de swing válido mais recente, ela registra seu preço e o horário correspondente nas variáveis first_low e first_low_time antes de encerrar o loop.

Após identificar o primeiro fundo, o segundo loop for continua a varredura na mesma direção, também com IsSwingLow, mas desta vez adiciona dois requisitos: o segundo fundo deve ser mais baixo que o primeiro e deve ter ocorrido antes do primeiro fundo. Quando esse ponto for encontrado, o loop será interrompido depois de armazenar o preço e o tempo nas variáveis second_low e second_low_time. Para traçar uma linha de tendência de alta, o EA deve primeiro detectar um fundo anterior e mais baixo e depois um fundo posterior e mais alto, o que é garantido por essa busca em duas etapas.

Em seguida, o código usa a função ObjectCreate para criar a linha de tendência de alta depois que ambos os fundos de swing são identificados. Usando seus respectivos timestamps, essa função conecta second_low e first_low no gráfico. Para evitar que a linha seja traçada cedo demais, ela é inicialmente ocultada em todos os timeframes com OBJ_NO_PERIODS. Depois de confirmar que a estrutura está correta (first_low > second_low e second_low > 0), OBJPROP_RAY_RIGHT é usado para estender a linha para a direita, projetando-a para o futuro. OBJ_ALL_PERIODS também é usado para exibir a linha em todos os timeframes.

A cor da linha de tendência é alterada para azul (clrBlue), e sua largura recebe o valor de três pixels para aumentar a clareza visual. Essas melhorias ajudam a linha de tendência a se destacar em relação aos demais elementos do gráfico e facilitam sua identificação visual.

3.3. Identificação da linha de tendência de baixa

Agora que aplicamos com sucesso a teoria para traçar uma linha de tendência de alta usando fundos de swing, o próximo passo será criar um procedimento semelhante para encontrar e traçar uma linha de tendência de baixa. Desta vez, porém, vamos focar nos topos de swing, e não aos fundos. Um topo de swing é uma máxima local que ocorre quando um ponto de preço fica acima das máximas de um número predefinido de barras vizinhas, à esquerda e à direita.

Desenvolveremos uma função para identificar topos de swing, percorreremos os candles históricos em loop para identificar dois topos válidos, em que o segundo topo seja mais baixo que o primeiro, e então traçaremos uma linha de tendência de baixa conectando esses dois pontos, assim como fizemos para a linha de tendência de alta. Devido à sua inclinação descendente, essa linha indicará uma tendência de baixa e servirá como possível nível de resistência, no qual os traders podem considerar vendas ou aguardar uma reação do preço.

Exemplo:
// Timeframe to use for retrieving candlestick data (default is the current chart timeframe)
input ENUM_TIMEFRAMES time_frame = PERIOD_CURRENT;
// Input to enable or disable drawing of the ascending trend line (true = allow drawing)
input bool allow_uptrend = true;
// Number of candles to look back when identifying swing lows for drawing the trend line
input int LookbackBars = 5;
// Input to enable or disable drawing of the descebding trend line (true = allow drawing)
input bool allow_downtrend = true;

// Number of past bars (candlesticks) to check
int bars_check = 500;

// Arrays to store candlestick data
double close_price[];   // Stores close prices
double open_price[];    // Stores open prices
double low_price[];     // Stores low prices
double high_price[];    // Stores high prices
datetime time_price[];  // Stores time data for each candle

double first_low;           // Price value of the first identified swing low
datetime first_low_time;    // Time when the first swing low occurred
double second_low;          // Price value of the second identified swing low
datetime second_low_time;   // Time when the second swing low occurred
string up_trend = "Up Trend";  // Label used to name the ascending trend line object on the chart
long chart_id = ChartID();     // Stores the current chart ID for referencing during object creation or manipulation

double first_high;          // Price value of the first identified swing high (latest high)
datetime first_high_time;   // Time when the first swing high occurred
double second_high;         // Price value of the second identified swing high (older high)
datetime second_high_time;  // Time when the second swing high occurred

string down_trend = "Down Trend";  // Label used to name the descending trend line object on the chart

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Set arrays as series so the newest bar is index 0 ( start from the latest bar)
   ArraySetAsSeries(close_price, true);
   ArraySetAsSeries(open_price, true);
   ArraySetAsSeries(low_price, true);
   ArraySetAsSeries(high_price, true);
   ArraySetAsSeries(time_price, true);

   return(INIT_SUCCEEDED);  // Signal that the EA initialized successfully
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

   ObjectsDeleteAll(chart_id);

  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy the latest candlestick data into the arrays
   CopyOpen(_Symbol, time_frame, 1, bars_check, open_price);     // Open prices
   CopyClose(_Symbol, time_frame, 1, bars_check, close_price);   // Close prices
   CopyLow(_Symbol, time_frame, 1, bars_check, low_price);       // Low prices
   CopyHigh(_Symbol, time_frame, 1, bars_check, high_price);     // High prices
   CopyTime(_Symbol, time_frame, 1, bars_check, time_price);     // Candle times

// If the user allows drawing of ascending trend line
   if(allow_uptrend)
     {
      // First loop: Find the most recent swing low (first low)
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check if current point is a swing low
         if(IsSwingLow(low_price, i, LookbackBars))
           {
            // Store price and time of the first (latest) swing low
            first_low = low_price[i];
            first_low_time = time_price[i];
            break;  // Exit loop after finding the first swing low
           }
        }

      // Second loop: Find an earlier swing low that is lower than the first low
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check for earlier swing low that is lower and occurs before the first low
         if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time)
           {
            // Store price and time of the second (older) swing low
            second_low = low_price[i];
            second_low_time = time_price[i];
            break;  // Exit loop after finding the second swing low
           }
        }

      // Create an ascending trend line from the second low to the first low
      ObjectCreate(chart_id, up_trend, OBJ_TREND, 0, second_low_time, second_low, first_low_time, first_low);
      ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);  // Temporarily hide line on all timeframes

      // If the swing structure is valid (i.e., second low is lower than first)
      if(first_low > second_low && second_low > 0)
        {
         // Extend the trend line to the right
         ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true);

         // Show the trend line on all timeframes
         ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

         // Set visual properties: color and thickness
         ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue);
         ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, 3);
        }
     }

//
// Only proceed if drawing descending trend lines is enabled
   if(allow_downtrend)
     {
      // First loop: Find the most recent swing high (first high)
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check if the current bar is a swing high
         if(IsSwingHigh(high_price, i, LookbackBars))
           {
            // Store the price and time of this latest swing high
            first_high = high_price[i];
            first_high_time = time_price[i];

            break;  // Exit loop once the first swing high is found
           }
        }

      // Second loop: Find an earlier swing high that is higher than the first high and occurred before it
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check for earlier swing high that is higher and happened before the first one
         if(IsSwingHigh(high_price, i, LookbackBars) && high_price[i] > first_high && time_price[i] < first_high_time)
           {
            // Store the price and time of this older swing high
            second_high = high_price[i];
            second_high_time = time_price[i];

            break;  // Exit loop once the second swing high is found
           }
        }

      // Create a trend line object from the second swing high to the first swing high
      ObjectCreate(chart_id, down_trend, OBJ_TREND, 0, second_high_time, second_high, first_high_time, first_high);

      // Initially hide the trend line across all timeframes to avoid partial drawing
      ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);

      // Validate the swing structure:
      // The older swing high should be higher than the later swing high to confirm a descending trend line
      if(first_high < second_high && second_high > 0)
        {
         // Extend the trend line indefinitely to the right for better visual guidance
         ObjectSetInteger(chart_id, down_trend, OBJPROP_RAY_RIGHT, true);

         // Make the trend line visible on all chart timeframes
         ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

         // Set the trend line color to dark green for clear distinction
         ObjectSetInteger(chart_id, down_trend, OBJPROP_COLOR, clrDarkGreen);

         // Set the thickness of the trend line to 3 pixels for better visibility
         ObjectSetInteger(chart_id, down_trend, OBJPROP_WIDTH, 3);
        }
     }
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR LOWS                                                |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index, int lookback)
  {

   for(int i = 1; i <= lookback; i++)
     {

      if(low[index] > low[index - i] || low[index] > low[index + i])
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR HIGHS                                               |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high[], int index, int lookback)
  {

   for(int i = 1; i <= lookback; i++)
     {
      if(high[index] < high[index - i] || high[index] < high[index + i])
         return false;
     }
   return true;
  }

Saída:

Figura 8. Identificação da linha de tendência de baixa

Explicação:

Ao comparar a máxima da barra com as máximas das barras vizinhas dentro de um período predefinido, a função IsSwingHigh determina se a barra atual é um topo de swing. A função retorna true, confirmando um pico local, se sua máxima for maior que a máxima de todas essas barras vizinhas; caso contrário, retorna false. O traçado da linha de tendência de baixa no gráfico pode ser ativada ou desativada por meio da variável de entrada allow_downtrend.

Inicialmente, o código procura o topo de swing mais recente (first_high) e registra seu preço e o horário correspondente para definir a linha de tendência de baixa. Em seguida, ele procura um topo de swing anterior (second_high), que tenha ocorrido antes do primeiro e seja mais alto. A linha de tendência que conecta esses dois pontos recebe o nome "Down Trend." A linha de tendência de baixa se torna evidente quando o topo de swing recente fica abaixo do topo anterior, confirmando uma tendência de baixa. Ela conecta dois ou mais topos cada vez mais baixos, é estilizada para melhorar a visibilidade e é projetada para o futuro no gráfico. Essa lógica é, em essência, o oposto da linha de tendência de alta, que conecta fundos cada vez mais altos para representar uma tendência de alta.


4. Execução de operações com base em rompimentos de linhas de tendência e reversões

No capítulo anterior, discutimos como usar pontos de swing importantes no gráfico de preços para traçar programaticamente linhas de tendência de alta e de baixa. Com base nisso, neste capítulo discutiremos como executar operações com base em rompimentos de linhas de tendência e reações a partir delas. Em particular, aprenderemos como reconhecer quando o preço rompe determinadas linhas de tendência ou reverte, bem como usar esses sinais para entrar em operações ou sair delas com eficiência.

4.1. Linhas de tendência de alta

Até aqui, nosso foco esteve na identificação do primeiro e do segundo fundo de swing para traçar a linha de tendência de alta. Mas como podemos distinguir quando o mercado forma um rompimento com possível reteste da linha de tendência e quando o preço toca essa linha e reverte? Essa etapa é necessária para tomar decisões de trading com base na forma como o preço interage com a linha de tendência. A linguagem MQL5 simplifica essa etapa por meio de funções integradas que nos permitem obter o nível de preço da linha de tendência em qualquer momento específico ou pelo índice da barra.

Para tomar decisões de trading mais bem fundamentadas, nesta seção veremos como reconhecer quando o preço se aproxima da linha de tendência de alta e quando a rompe. Vamos analisar as ferramentas que a linguagem MQL5 oferece, seja para tentar capturar uma reação no nível de suporte, seja para buscar uma entrada em rompimento com reteste. Vamos nos concentrar na análise das quatro últimas barras do gráfico, porque rompimentos e reversões normalmente ocorrem nos candles mais recentes. Na maioria dos casos, essas barras recentes mostrarão indícios de que a linha de tendência foi rompida ou, ao contrário, serviu como ponto de reação.

Em seguida, analisaremos atentamente se todas as quatro últimas barras atendem aos requisitos de rompimento ou reversão, em vez de percorrer todo o histórico do gráfico. Com essa abordagem, poderemos reagir a novos movimentos do mercado e tomar decisões de trading fundamentadas. Nas próximas partes do artigo, usaremos a linguagem MQL5 para implementar essa ideia de forma programática.

Exemplo:
double t_line_value;   // Ascending trend line price level at the time of the most recent bar (not the ticking bar)
double t1_line_value;  // Ascending trend line price level at the time of the second most recent bar
double t2_line_value;  // Ascending trend line price level at the time of the third most recent bar
double t3_line_value;  // Ascending trend line price level at the time of the fourth most recent bar
if(allow_uptrend)
  {
// First loop: Find the most recent swing low (first low)
   for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
     {
      // Check if current point is a swing low
      if(IsSwingLow(low_price, i, LookbackBars))
        {
         // Store price and time of the first (latest) swing low
         first_low = low_price[i];
         first_low_time = time_price[i];
         break;  // Exit loop after finding the first swing low
        }
     }

// Second loop: Find an earlier swing low that is lower than the first low
   for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
     {
      // Check for earlier swing low that is lower and occurs before the first low
      if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time)
        {
         // Store price and time of the second (older) swing low
         second_low = low_price[i];
         second_low_time = time_price[i];
         break;  // Exit loop after finding the second swing low
        }
     }

// Create an ascending trend line from the second low to the first low
   ObjectCreate(chart_id, up_trend, OBJ_TREND, 0, second_low_time, second_low, first_low_time, first_low);
   ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);  // Temporarily hide line on all timeframes

// If the swing structure is valid (i.e., second low is lower than first)
   if(first_low > second_low && second_low > 0)
     {
      // Extend the trend line to the right
      ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true);

      // Show the trend line on all timeframes
      ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

      // Set visual properties: color and thickness
      ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue);
      ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, 3);


      // Get the price values of the trend line at the corresponding times of the four most recent bars
      t_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[0], 0);   // Current bar
      t1_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[1], 0);  // One bar ago
      t2_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[2], 0);  // Two bars ago
      t3_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[3], 0);  // Three bars ago

      Comment("Ascending trend tine value for the last 4 Bars",
              "\nBar 0: ",  DoubleToString(t_line_value, _Digits),
              "\nBar 1: ", DoubleToString(t1_line_value, _Digits),
              "\nBar 2: ",  DoubleToString(t2_line_value, _Digits),
              "\nBar 3: ",  DoubleToString(t3_line_value, _Digits));

     }
  }

Saída:

Figura 9. Valores da linha de tendência

Explicação:

Para obter o valor da linha de tendência, usamos a função ObjectGetValueByTime(), que retorna o nível de preço de um objeto gráfico especificado, neste caso, a linha de tendência, em um determinado momento. Isso nos permitirá determinar o nível da linha de tendência no momento do fechamento de cada um dos candles mais recentes.

Para isso, usamos quatro variáveis separadas. O valor da linha de tendência na barra 0, que é a barra fechada mais recente, mas não a barra atual, que ainda está em formação, geralmente chamada de "barra de tick", isto é, a barra ainda em formação, é armazenado na variável t_line_value. O nível da linha de tendência na barra 1, imediatamente anterior à última barra, é armazenado na variável t1_line_value. Os valores da linha de tendência para as barras 2 e 3, ou seja, as barras que fecharam dois e três períodos atrás, respectivamente, são armazenados de forma semelhante em t2_line_value e t3_line_value.

Vale observar que, embora chamemos time_price[0] de "bar 0", no gráfico essa barra na verdade aparece como a segunda barra a partir da direita. Isso acontece porque não usamos na análise a barra atual, a mais à direita, pois ela ainda está em formação e não fechou. A forma como usamos a função CopyTime() anteriormente no código, iniciando a cópia pelo índice 1 e pulando o candle atual, ainda não fechado, para obter dados mais confiáveis, também segue essa lógica. Para fins de verificação ou depuração, o código exibe esses valores da linha de tendência diretamente no gráfico usando a função Comment(). Essa saída permite observar como a linha de tendência se comporta em relação às barras mais recentes e é exibida no canto superior esquerdo da janela do gráfico. Isso é especialmente útil para verificar se a linha de tendência está atuando como ponto de rompimento ou de reação.

4.1.1. Execução de operação com base na linha de tendência de alta

Agora que sabemos como extrair com precisão os valores da linha de tendência no momento das barras fechadas mais recentes, o próximo passo é criar a lógica que determina quando executar uma operação, seja em um rompimento, seja em uma reversão. Agora podemos determinar o que está acontecendo na linha de tendência comparando os valores da linha de tendência dos candles mais recentes com os movimentos reais do preço, como os fechamentos dos candles ou suas mínimas e máximas.

4.1.1.1. Reversão

Agora precisamos definir a lógica de execução de operações com base em sinais de reversão, pois já sabemos como obter os valores de preço da linha de tendência nos horários correspondentes a diferentes barras. Para determinar se o preço respeitou a linha de tendência ou reagiu a partir dela, os preços das barras atuais são comparados com os valores correspondentes da linha de tendência. Devemos procurar uma reação na linha de tendência, ainda que esse conceito pareça simples. Implementar isso com precisão pode ser um pouco mais complexo. As reversões podem ter muitas variações e, muitas vezes, são bastante sutis. Às vezes, todo o corpo do candle pode estar sobre a linha de tendência, enquanto em outros casos o candle pode apenas tocar a linha e fechar acima dela. Às vezes, o preço até rompe determinado nível e depois reverte bruscamente: esse fenômeno é chamado de falso rompimento.

Por causa dessas variações, pode ser difícil definir os parâmetros necessários para identificar com precisão uma reversão real. Além da posição do candle atual, também é preciso considerar como vários candles interagem com a linha de tendência. Isso pode incluir confirmações adicionais, como padrões de candles de alta, para garantir que não houve fechamento significativo abaixo da linha de tendência e observar se as mínimas dos candles recentes tocam a linha ou reagem a partir dela. Para minimizar sinais falsos, essas condições em várias camadas exigem uma lógica de programação cuidadosamente elaborada e tornam mais complexa a detecção de reversões com linhas de tendência.

Abaixo estão algumas condições válidas de reversão:

Rejeição com pavio e reversão de alta na linha de tendência de alta:

Figura 10. Rejeição por pavio e reversão de alta

Como você pode ver, o candle reverteu imediatamente para cima assim que o pavio tocou a linha de tendência de alta. Isso mostra que o preço foi rapidamente rejeitado quando testou a linha de tendência como suporte. A reversão é reforçada pelo fato de que o mesmo candle fechou posteriormente em alta. Como isso mostra uma forte reação dos compradores em um nível crítico de suporte, esse price action, no qual o pavio interage com a linha de tendência, mas o corpo fecha de forma acentuada na direção oposta, é um critério válido para abrir uma compra.

Toque com pavio de um candle de baixa seguido de confirmação de alta:

Figura 11. Toque com pavio de candle de baixa seguido de confirmação de alta

No gráfico acima, o candle de baixa tocou a linha de tendência de alta com o pavio e fechou acima dela. No entanto, o candle não atende imediatamente aos critérios de entrada por reversão, porque é um candle de baixa. A confirmação vem do candle de alta seguinte. Esse candle de alta é uma condição adequada para abrir uma compra, pois indica que os compradores entraram em ação após o contato com a linha de tendência. Para confirmar a reversão, deve haver um candle de alta.

Vários fechamentos de baixa antes da confirmação da reversão de alta:

Figura 12. Vários fechamentos de baixa antes da confirmação da reversão de alta

O candle de baixa fechou acima da linha de tendência de alta quando seu pavio a tocou. Embora ele também fosse de baixa, o candle seguinte fechou acima da linha de tendência. O candle de alta que atendia aos requisitos de entrada por reversão só se formou no terceiro candle. O mercado continuou respeitando a linha de tendência, mas, como mostra esse setup, foram necessários vários candles para que os compradores assumissem o controle. O último candle de alta confirma uma possível operação de compra.

Reversão após falso rompimento com confirmação imediata de alta:

Figura 13. Falso rompimento

O fato de o primeiro candle que tocou a linha de tendência de alta ter fechado abaixo dela pode indicar um possível rompimento. No entanto, o candle seguinte foi um forte candle de alta, que fechou acima da linha de tendência. Essa reação rápida é crucial, pois o candle de alta formado logo após o toque favorece uma reversão. O mercado ainda trata a linha de tendência como suporte, como demonstra a reação imediata de alta, apesar do fechamento temporário abaixo dela. Portanto, este é um setup válido de compra.

Existem determinadas condições que invalidam uma reversão na linha de tendência, assim como há muitas condições que a confirmam. Com esses sinais de invalidação, podemos evitar operações de baixa qualidade e alertas falsos. Eles também são necessários para impedir a execução de várias operações em um curto período de tempo, o que pode ser causado por sinais iguais ou repetidos. Isso torna nossa abordagem disciplinada e evita o overtrading.

Reversão inválida após rompimento confirmado:

Figura 14. Reversão inválida após rompimento confirmado

A linha de tendência de alta já havia sido rompida por dois candles de baixa. O candle de alta deixa de ser considerado um sinal válido de reversão, mesmo que acabe fechando acima da linha de tendência. O rompimento é considerado confirmado porque a estrutura já foi violada. Qualquer movimento de alta posterior é tratado como ruído ou possível reteste e não gera um ponto de entrada válido.

Prevenção de sinais de trading repetitivos em múltiplas reversões:

Figura 15. Sinais de trading repetidos

Cada um dos três candles de alta na imagem acima tem um pavio que toca a linha de tendência de alta antes de reverter para cima. O algoritmo pode executar várias operações a cada toque se não houver condições adequadas para filtrar esses padrões, mesmo que todos façam parte de um movimento de reversão mais amplo. Nessas situações, é fundamental criar uma lógica que limite a execução de operações ao primeiro sinal válido.

A lógica deve ser cuidadosamente testada e ajustada e ajustada considerando esta e várias outras situações comparáveis. Elas podem causar atraso na entrada, operações repetidas ou sinais falsos se não forem tratadas corretamente. Para evitar execuções desnecessárias ou duplicadas durante sinais muito próximos entre si, bem como para verificar situações válidas de reversão, são necessários critérios rigorosos de confirmação.

Exemplo:

#include <Trade/Trade.mqh>
CTrade trade;
int MagicNumber = 532127;


// Timeframe to use for retrieving candlestick data (default is the current chart timeframe)
input ENUM_TIMEFRAMES time_frame = PERIOD_CURRENT;
// Input to enable or disable drawing of the ascending trend line (true = allow drawing)
input bool allow_uptrend = true;
// Number of candles to look back when identifying swing lows for drawing the trend line
input int LookbackBars = 5;

// Input to enable or disable drawing of the descebding trend line (true = allow drawing)
input bool allow_downtrend = true;
input bool allow_break_out = true;    // Enable or disable trade execution on trend line breakout (true = allow)
input bool allow_reversal = true;     // Enable or disable trade execution on trend line reversal (true = allow)
input double lot_size = 0.6;          // Lot size for each trade
input double sl_points = 10;          // Stop Loss in points from entry price
input double tp_points = 50;          // Take Profit in points from entry price

// Number of past bars (candlesticks) to check
int bars_check = 500;

// Arrays to store candlestick data
double close_price[];   // Stores close prices
double open_price[];    // Stores open prices
double low_price[];     // Stores low prices
double high_price[];    // Stores high prices
datetime time_price[];  // Stores time data for each candle

double first_low;           // Price value of the first identified swing low
datetime first_low_time;    // Time when the first swing low occurred

double second_low;          // Price value of the second identified swing low
datetime second_low_time;   // Time when the second swing low occurred
string up_trend = "Up Trend";  // Label used to name the ascending trend line object on the chart
long chart_id = ChartID();     // Stores the current chart ID for referencing during object creation or manipulation
double first_high;          // Price value of the first identified swing high (latest high)
datetime first_high_time;   // Time when the first swing high occurred
double second_high;         // Price value of the second identified swing high (older high)
datetime second_high_time;  // Time when the second swing high occurred
string down_trend = "Down Trend";  // Label used to name the descending trend line object on the chart
double t_line_value;   // Ascending trend line price level at the time of the most recent bar (not the ticking bar)
double t1_line_value;  // Ascending trend line price level at the time of the second most recent bar
double t2_line_value;  // Ascending trend line price level at the time of the third most recent bar
double t3_line_value;  // Ascending trend line price level at the time of the fourth most recent bar

// Time boundary used to limit lookback for valid reversal setups
datetime lookbackf_time;

datetime lastTradeBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Set arrays as series so the newest bar is index 0 ( start from the latest bar)
   ArraySetAsSeries(close_price, true);
   ArraySetAsSeries(open_price, true);
   ArraySetAsSeries(low_price, true);
   ArraySetAsSeries(high_price, true);
   ArraySetAsSeries(time_price, true);

   trade.SetExpertMagicNumber(MagicNumber);

   return(INIT_SUCCEEDED);  // Signal that the EA initialized successfully
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

   ObjectsDeleteAll(chart_id);

  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy the latest candlestick data into the arrays
   CopyOpen(_Symbol, time_frame, 1, bars_check, open_price);     // Open prices
   CopyClose(_Symbol, time_frame, 1, bars_check, close_price);   // Close prices
   CopyLow(_Symbol, time_frame, 1, bars_check, low_price);       // Low prices
   CopyHigh(_Symbol, time_frame, 1, bars_check, high_price);     // High prices
   CopyTime(_Symbol, time_frame, 1, bars_check, time_price);     // Candle times

   double ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   datetime currentBarTime = iTime(_Symbol, time_frame, 0);

// If the user allows drawing of ascending trend line
   if(allow_uptrend)
     {
      // First loop: Find the most recent swing low (first low)
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check if current point is a swing low
         if(IsSwingLow(low_price, i, LookbackBars))
           {
            // Store price and time of the first (latest) swing low
            first_low = low_price[i];
            first_low_time = time_price[i];
            lookbackf_time = time_price[i - 3];

            break;  // Exit loop after finding the first swing low
           }
        }

      // Second loop: Find an earlier swing low that is lower than the first low
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check for earlier swing low that is lower and occurs before the first low
         if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time)
           {
            // Store price and time of the second (older) swing low
            second_low = low_price[i];
            second_low_time = time_price[i];
            break;  // Exit loop after finding the second swing low
           }
        }

      // Create an ascending trend line from the second low to the first low
      ObjectCreate(chart_id, up_trend, OBJ_TREND, 0, second_low_time, second_low, first_low_time, first_low);
      ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);  // Temporarily hide line on all timeframes

      // If the swing structure is valid (i.e., second low is lower than first)
      if(first_low > second_low && second_low > 0)
        {
         // Extend the trend line to the right
         ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true);

         // Show the trend line on all timeframes
         ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

         // Set visual properties: color and thickness
         ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue);
         ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, 3);

         // Get the price values of the trend line at the corresponding times of the four most recent bars
         t_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[0], 0);   // Current bar
         t1_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[1], 0);  // One bar ago
         t2_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[2], 0);  // Two bars ago
         t3_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[3], 0);  // Three bars ago

         // Number of bars between the valid bullish confirmation candle and current time
         int no_bars = 0;

         // Loop through the last 4 bars to check for reversal wick touch on the trend line
         for(int i = 0; i <= 3; i++)
           {
            // Condition: Wick of the candle touches below the trend line but opens above it (indicating a potential reversal zone)
            if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0) &&
               open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0))
              {
               // Check if there's a bullish confirmation candle after the wick touch (within or immediately after)
               for(int j = i; j >= 0; j--)
                 {
                  // Bullish candle that closed above the trend line
                  if(close_price[j] > open_price[j] &&
                     close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], 0))
                    {
                     // Count how many bars ago this confirmation occurred
                     no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent());
                     break;
                    }
                 }
               break; // Exit after first valid reversal zone is found
              }
           }

         // Check whether a similar wick touch (reversal) happened recently to avoid repeated signals
         bool prev_touch = false;

         if((low_price[1] < t1_line_value && close_price[1] > open_price[1]) ||  // Bar 1 had reversal wick and bullish body
            (low_price[2] < t2_line_value && close_price[2] > open_price[2]))    // Bar 2 had reversal wick and bullish body
           {
            prev_touch = true;  // Flag that a recent touch already occurred
           }

         // Final condition for executing a BUY trade on a reversal setup
         if(
            // One of the recent 4 bars touched and rejected the trend line (wick below, open above), AND
            ((low_price[0] < t_line_value && open_price[0] > t_line_value) ||
             (low_price[1] < t1_line_value && open_price[1] > t1_line_value) ||
             (low_price[2] < t2_line_value && open_price[2] > t2_line_value) ||
             (low_price[3] < t3_line_value && open_price[3] > t3_line_value))
            &&
            // Current candle must be bullish and close above the trend line
            (close_price[0] > open_price[0]) && close_price[0] > t_line_value
            &&
            // The bullish confirmation must occur within 3 bars
            (no_bars < 3)
            &&
            // No recent wick reversal signal already processed
            prev_touch == false
            &&
            // The signal must be more recent than the lookback time threshold
            (time_price[3] > lookbackf_time)
            &&
            // Reversal signals are allowed and this signal is not duplicated from the same bar
            (allow_reversal == true && currentBarTime != lastTradeBarTime)
         )
           {
            // Execute BUY trade with defined lot size, SL and TP
            trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points);
            lastTradeBarTime = currentBarTime; // Update last trade bar time to avoid duplicate signals
           }
        }
     }

//
// Only proceed if drawing descending trend lines is enabled
   if(allow_downtrend)
     {
      // First loop: Find the most recent swing high (first high)
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check if the current bar is a swing high
         if(IsSwingHigh(high_price, i, LookbackBars))
           {
            // Store the price and time of this latest swing high
            first_high = high_price[i];
            first_high_time = time_price[i];

            break;  // Exit loop once the first swing high is found
           }
        }

      // Second loop: Find an earlier swing high that is higher than the first high and occurred before it
      for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
        {
         // Check for earlier swing high that is higher and happened before the first one
         if(IsSwingHigh(high_price, i, LookbackBars) && high_price[i] > first_high && time_price[i] < first_high_time)
           {
            // Store the price and time of this older swing high
            second_high = high_price[i];
            second_high_time = time_price[i];

            break;  // Exit loop once the second swing high is found
           }
        }

      // Create a trend line object from the second swing high to the first swing high
      ObjectCreate(chart_id, down_trend, OBJ_TREND, 0, second_high_time, second_high, first_high_time, first_high);

      // Initially hide the trend line across all timeframes to avoid partial drawing
      ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);

      // Validate the swing structure:
      // The older swing high should be higher than the later swing high to confirm a descending trend line
      if(first_high < second_high && second_high > 0)
        {
         // Extend the trend line indefinitely to the right for better visual guidance
         ObjectSetInteger(chart_id, down_trend, OBJPROP_RAY_RIGHT, true);

         // Make the trend line visible on all chart timeframes
         ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

         // Set the trend line color to dark green for clear distinction
         ObjectSetInteger(chart_id, down_trend, OBJPROP_COLOR, clrDarkGreen);

         // Set the thickness of the trend line to 3 pixels for better visibility
         ObjectSetInteger(chart_id, down_trend, OBJPROP_WIDTH, 3);

        }
     }
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR LOWS                                                |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index, int lookback)
  {

   for(int i = 1; i <= lookback; i++)
     {

      if(low[index] > low[index - i] || low[index] > low[index + i])
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR HIGHS                                               |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high[], int index, int lookback)
  {

   for(int i = 1; i <= lookback; i++)
     {
      if(high[index] < high[index - i] || high[index] < high[index + i])
         return false;
     }
   return true;
  }

Saída:

Figura 16. Reversão por pavio

Figura 17. Falso rompimento

Figura 18. Rejeição por pavio e reversão de alta

Explicação:

Para acessar a classe CTrade, usada na linguagem MQL5 para abrir operações e gerenciá-las, o código primeiro inclui a biblioteca de trading Trade.mqh. Em seguida, é criado um objeto da classe CTrade chamado trade, por meio do qual funções como Buy() e Sell() executam operações de trading. Para distinguir as transações executadas por este EA específico das demais, é atribuído ao EA um MagicNumber único. A função SetExpertMagicNumber() é usada para definir esse valor.

Em seguida, são especificados vários parâmetros de entrada. Isso permite que o usuário altere o comportamento do EA sem modificar o código principal. Por exemplo, para controlar se as operações são permitidas em situações de tendência de baixa, seja em padrões de rompimento ou em setups de reversão, são usadas flags de ativação/desativação: allow_downtrend, allow_break_out e allow_reversal. Os parâmetros sl_points e tp_points indicam, respectivamente, a distância em pontos entre o preço de entrada e o stop-loss e o take-profit, enquanto o parâmetro lot_size define o tamanho de cada posição.

Para controlar o tempo, são usadas duas variáveis: lookbackf_time e lastTradeBarTime. A variável lookbackf_time define até que ponto no passado o programa deve procurar setups válidos de reversão. Nesta etapa, quaisquer setups anteriores são ignorados. Para eliminar sinais excessivos ou duplicados, a variável lastTradeBarTime é usada para impedir a execução de várias operações no mesmo candle.

int no_bars = 0;
for(int i = 0; i <= 3; i++)
  {
   if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0) &&
      open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0))
     {
      for(int j = i; j >= 0; j--)
        {
         if(close_price[j] > open_price[j] &&
            close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], 0))
           {
            no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent());
            break;
           }
        }
      break;
     }
  }

Nos últimos quatro candles (barras 0-3), verifica-se se algum deles apresentou comportamento de pavio que pudesse indicar um sinal de reversão: a mínima rompeu a linha de tendência, mas o preço de abertura permaneceu acima dela. Isso indica que ocorreu uma reação a partir da linha de tendência. Assim que esse candle é identificado, o loop interno começa a procurar um candle de confirmação de alta que tenha fechado acima do preço de abertura e também da linha de tendência. Se esse candle for encontrado, a função Bars() é chamada para determinar quantas barras atrás essa confirmação ocorreu, e o resultado é armazenado na variável no_bars.

bool prev_touch = false;
if((low_price[1] < t1_line_value && close_price[1] > open_price[1]) ||
   (low_price[2] < t2_line_value && close_price[2] > open_price[2])) {
    prev_touch = true;
}

Este bloco determina se os candles 1 ou 2 já tocaram a linha de tendência e fecharam em alta, para evitar alertas recorrentes ou prematuros. A variável prev_touch recebe o valor true se pelo menos uma das duas condições for verdadeira, o que sugere que a reversão pode ter ocorrido recentemente e que outro sinal na barra atual deve ser ignorado.

if(
   ((low_price[0] < t_line_value && open_price[0] > t_line_value) ||
    (low_price[1] < t1_line_value && open_price[1] > t1_line_value) ||
    (low_price[2] < t2_line_value && open_price[2] > t2_line_value) ||
    (low_price[3] < t3_line_value && open_price[3] > t3_line_value))
   &&
   (close_price[0] > open_price[0]) && close_price[0] > t_line_value
   &&
   (no_bars < 3)
   &&
   prev_touch == false
   &&
   (time_price[3] > lookbackf_time)
   &&
   (allow_reversal == true && currentBarTime != lastTradeBarTime)
)
  {
   trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points);
   lastTradeBarTime = currentBarTime;
  }

Primeiro, verifica-se se pelo menos um dos quatro últimos candles apresentou comportamento do pavio, o que significa que o preço tocou a linha de tendência, mas abriu acima dela, indicando uma possível zona de reação a partir da linha; em seguida, exige-se que o candle atual seja de alta e tenha fechado acima da linha de tendência, reforçando o sinal; por fim, exige-se que o candle de confirmação de alta identificado anteriormente tenha ocorrido dentro das três últimas barras, para garantir que o setup ainda seja válido. Esse bloco condicional verifica se uma entrada de compra por reversão deve ser executada, avaliando uma série de critérios importantes.

O algoritmo verifica se nenhum sinal comparável de toque por pavio foi detectado recentemente e, portanto, o valor de prev_touch deve ser false para evitar entradas repetidas. Além disso, ele garante que o setup seja mais recente do que o limite temporal definido por lookbackf_time, o que limita o intervalo de sinais que o programa aceita como válidos.

Por fim, ao verificar que currentBarTime não é igual a lastTradeBarTime, o algoritmo evita duplicação e confirma que as operações de reversão estão permitidas (allow_reversal está definido como true) e que nenhuma operação ainda foi executada no mesmo candle. Uma ordem de compra com o tamanho de lote especificado e os valores de stop-loss e take-profit é executada se todos esses requisitos forem atendidos. Em seguida, para evitar a execução de operações adicionais no mesmo candle, lastTradeBarTime é atualizado para o horário do candle atual.

4.1.1.2. Rompimento e reteste

Os setups de rompimento e reteste próximos à linha de tendência de alta seguem os mesmos critérios de envio de ordens que as reversões. Após um rompimento abaixo da linha de tendência, o preço pode recuar para testar o suporte rompido, que agora atua como resistência. Um candle de corpo longo ou simplesmente um pavio tocando a linha de tendência pode acionar esse reteste. O rompimento pode ser confirmado com o envio de uma ordem de venda se o reteste for bem-sucedido e o preço reagir para baixo.

Figura 19. Rompimento e reteste

Exemplo:

// If the user allows drawing of ascending trend line
if(allow_uptrend)
  {
// First loop: Find the most recent swing low (first low)
   for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
     {
      // Check if current point is a swing low
      if(IsSwingLow(low_price, i, LookbackBars))
        {
         // Store price and time of the first (latest) swing low
         first_low = low_price[i];
         first_low_time = time_price[i];
         lookbackf_time = time_price[i - 3];

         break;  // Exit loop after finding the first swing low
        }
     }

// Second loop: Find an earlier swing low that is lower than the first low
   for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
     {
      // Check for earlier swing low that is lower and occurs before the first low
      if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time)
        {
         // Store price and time of the second (older) swing low
         second_low = low_price[i];
         second_low_time = time_price[i];
         break;  // Exit loop after finding the second swing low
        }
     }

// Create an ascending trend line from the second low to the first low
   ObjectCreate(chart_id, up_trend, OBJ_TREND, 0, second_low_time, second_low, first_low_time, first_low);
   ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);  // Temporarily hide line on all timeframes

// If the swing structure is valid (i.e., second low is lower than first)
   if(first_low > second_low && second_low > 0)
     {
      // Extend the trend line to the right
      ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true);

      // Show the trend line on all timeframes
      ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

      // Set visual properties: color and thickness
      ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue);
      ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, 3);


      // Get the price values of the trend line at the corresponding times of the four most recent bars
      t_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[0], 0);   // Current bar
      t1_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[1], 0);  // One bar ago
      t2_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[2], 0);  // Two bars ago
      t3_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[3], 0);  // Three bars ago

      // Number of bars between the valid bullish confirmation candle and current time
      int no_bars = 0;

      // Loop through the last 4 bars to check for reversal wick touch on the trend line
      for(int i = 0; i <= 3; i++)
        {
         // Condition: Wick of the candle touches below the trend line but opens above it (indicating a potential reversal zone)
         if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0) &&
            open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0))
           {
            // Check if there's a bullish confirmation candle after the wick touch (within or immediately after)
            for(int j = i; j >= 0; j--)
              {
               // Bullish candle that closed above the trend line
               if(close_price[j] > open_price[j] &&
                  close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], 0))
                 {
                  // Count how many bars ago this confirmation occurred
                  no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent());
                  break;
                 }
              }
            break; // Exit after first valid reversal zone is found
           }
        }

      // Check whether a similar wick touch (reversal) happened recently to avoid repeated signals
      bool prev_touch = false;

      if((low_price[1] < t1_line_value && close_price[1] > open_price[1]) ||  // Bar 1 had reversal wick and bullish body
         (low_price[2] < t2_line_value && close_price[2] > open_price[2]))    // Bar 2 had reversal wick and bullish body
        {
         prev_touch = true;  // Flag that a recent touch already occurred
        }

      // Final condition for executing a BUY trade on a reversal setup
      if(
         // One of the recent 4 bars touched and rejected the trend line (wick below, open above), AND
         ((low_price[0] < t_line_value && open_price[0] > t_line_value) ||
          (low_price[1] < t1_line_value && open_price[1] > t1_line_value) ||
          (low_price[2] < t2_line_value && open_price[2] > t2_line_value) ||
          (low_price[3] < t3_line_value && open_price[3] > t3_line_value))
         &&
         // Current candle must be bullish and close above the trend line
         (close_price[0] > open_price[0]) && close_price[0] > t_line_value
         &&
         // The bullish confirmation must occur within 3 bars
         (no_bars < 3)
         &&
         // No recent wick reversal signal already processed
         prev_touch == false
         &&
         // The signal must be more recent than the lookback time threshold
         (time_price[3] > lookbackf_time)
         &&
         // Reversal signals are allowed and this signal is not duplicated from the same bar
         (allow_reversal == true && currentBarTime != lastTradeBarTime)
      )
        {
         // Execute BUY trade with defined lot size, SL and TP
         trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points);
         lastTradeBarTime = currentBarTime; // Update last trade bar time to avoid duplicate signals
        }

      //BREAKOUT AND RETEST

      // Flag to track whether a recent bearish wick rejection (touch) already occurred
      bool prev_touch2 = false;

      // Check the last 2 bars to see if a candle had its high wick above the trend line,
      // but closed bearishly below the open - indicating a possible rejection
      if((high_price[1] > t1_line_value && close_price[1] < open_price[1]) ||
         (high_price[2] > t2_line_value && close_price[2] < open_price[2] && open_price[2] < t2_line_value))
        {
         prev_touch2 = true; // Set flag to avoid duplicate signals
        }

      // Variable to store how many bars ago the bearish confirmation candle appeared
      int no_bars2 = 0;

      // Loop through the last 4 candles to detect a wick rejection of the trend line (retest)
      for(int i = 0; i <= 3; i++)
        {
         // Condition: Candle wick (high) goes above the trend line, but the open is below it
         if(high_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0) &&
            open_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], 0))
           {
            // Search backward from that bar for a bearish confirmation candle
            for(int j = i; j >= 0; j--)
              {
               // Bearish candle that also closed below the trend line
               if(close_price[j] < open_price[j] &&
                  close_price[j] < ObjectGetValueByTime(chart_id, up_trend, time_price[j], 0))
                 {
                  // Count bars between that confirmation and now
                  no_bars2 = Bars(_Symbol, time_frame, time_price[j], TimeCurrent());
                  break; // Exit inner loop
                 }
              }
            break; // Exit outer loop after first valid retest
           }
        }

      // Final conditions to confirm a breakout and retest sell setup:
      // 1. One of the last 4 candles had a wick above the trend line but opened below it
      // 2. Current candle is bearish and closed below the trend line
      // 3. There was no recent similar signal (prev_touch2 == false)
      // 4. The bearish confirmation occurred within the last 3 bars
      // 5. Breakout trades are allowed and this signal is not from the same bar as the last trade
      if(((high_price[1] >= t1_line_value && open_price[1] < t1_line_value) ||
          (high_price[2] >= t2_line_value && open_price[2] < t2_line_value) ||
          (high_price[3] >= t3_line_value && open_price[3] < t3_line_value) ||
          (high_price[0] >= t_line_value)) &&
         (close_price[0] < t_line_value && close_price[0] < open_price[0] && open_price[1] < t1_line_value) &&
         prev_touch2 == false &&
         (no_bars2 < 3) &&
         (allow_break_out == true && currentBarTime != lastTradeBarTime))
        {
         // All conditions met - place SELL trade with defined SL and TP
         trade.Sell(lot_size, _Symbol, ask_price, ask_price + sl_points, ask_price - tp_points);

         // Update timestamp to prevent duplicate signals from the same bar
         lastTradeBarTime = currentBarTime;
        }
     }
  }

Saída:

Figura 20. Reteste imediato

Figura 21. Rompimento e reteste

Explicação:

O código começa declarando uma variável booleana chamada prev_touch2, que serve como flag para determinar se ocorreu recentemente um repique de baixa (falso rompimento). O objetivo dessa flag é impedir que o algoritmo dispare múltiplos sinais de trading a partir do mesmo setup que já foi processado. Isso ajuda a reduzir a quantidade de sinais falsos e garante que o EA reaja apenas a setups novos e válidos.

Em seguida, o algoritmo procura sinais de rejeição baixista na linha de tendência analisando os dois candles anteriores (high_price[1] e high_price[2]). Especificamente, ele verifica se o candle acabou fechando abaixo de sua abertura, o que indica pressão vendedora e incapacidade de sustentar a tentativa de rompimento, ou se ultrapassou brevemente a linha de tendência, sugerindo uma possível tentativa de rompimento. Para confirmar a rejeição na linha, é adicionada uma condição para o segundo candle ([2]), a fim de garantir que o preço de abertura também esteja abaixo da linha de tendência. A variável prev_touch2 recebe o valor true se qualquer uma dessas condições for atendida.

Depois que o pavio toca a linha de tendência, a variável no_bars2 é inicializada para indicar quantos candles atrás surgiu o candle de confirmação de baixa. Esses dados são fundamentais para confirmar que o sinal ainda é válido e que o reteste ocorreu recentemente.

Depois disso, o código entra em um loop que percorre os quatro candles anteriores. O objetivo é encontrar um candle cujo pavio (máxima) tenha ultrapassado a linha de tendência, mas que tenha aberto abaixo dela; isso indica que o preço tentou romper a linha de tendência, mas não conseguiu romper de forma limpa. Depois que esse candle é encontrado, um loop aninhado faz uma varredura para trás a partir dele em busca de uma confirmação de baixa, ou seja, um candle que tenha fechado abaixo tanto da abertura quanto da linha de tendência no momento correspondente. Se essa confirmação for encontrada, o método Bars() é aplicado para determinar quantas barras atrás esse candle ocorreu, e o resultado é armazenado em no_bars2. Para economizar recursos computacionais, os loops são encerrados antecipadamente assim que sinais válidos são detectados.

Por fim, o código avalia várias condições combinadas para determinar se uma operação de venda deve ser aberta. Essas condições verificam que:

  • um dos quatro últimos candles teve um pavio que subiu acima da linha de tendência, mas abriu abaixo dela;
  • o candle atual é de baixa e fechou abaixo tanto da linha de tendência quanto de seu preço de abertura;
  • não houve sinal semelhante registrado anteriormente (prev_touch2 == false);
  • um candle de confirmação de baixa se formou recentemente (dentro das últimas 3 barras);
  • a operação está habilitada (allow_break_out == true) e não duplica uma operação no mesmo candle (currentBarTime != lastTradeBarTime).

Uma venda com o tamanho de lote especificado e os valores de stop-loss e take-profit é executada se todos esses requisitos forem atendidos. Para evitar a abertura de outra operação com base no mesmo sinal, o horário do candle atual é armazenado em lastTradeBarTime.

4.1.2. Execução de operação com base na linha de tendência de baixa

Nesta seção, procuraremos oportunidades de rompimento e reversão na linha de tendência de baixa. Toda a lógica é apenas uma inversão do que fizemos para a linha de tendência de alta. Para evitar duplicação desnecessária, não vamos nos aprofundar nos detalhes, pois a metodologia e a estrutura são, em geral, idênticas. Nosso foco estará nos setups de baixa durante reversões e nos setups de alta durante rompimentos com reteste, já que a linha de tendência de baixa atua como resistência. Essa é a diferença principal.

Exemplo:

// Only proceed if drawing descending trend lines is enabled
if(allow_downtrend)
  {
// First loop: Find the most recent swing high (first high)
   for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
     {
      // Check if the current bar is a swing high
      if(IsSwingHigh(high_price, i, LookbackBars))
        {
         // Store the price and time of this latest swing high
         first_high = high_price[i];
         first_high_time = time_price[i];

         break;  // Exit loop once the first swing high is found
        }
     }

// Second loop: Find an earlier swing high that is higher than the first high and occurred before it
   for(int i = LookbackBars; i < bars_check - LookbackBars; i++)
     {
      // Check for earlier swing high that is higher and happened before the first one
      if(IsSwingHigh(high_price, i, LookbackBars) && high_price[i] > first_high && time_price[i] < first_high_time)
        {
         // Store the price and time of this older swing high
         second_high = high_price[i];
         second_high_time = time_price[i];

         break;  // Exit loop once the second swing high is found
        }
     }

// Create a trend line object from the second swing high to the first swing high
   ObjectCreate(chart_id, down_trend, OBJ_TREND, 0, second_high_time, second_high, first_high_time, first_high);

// Initially hide the trend line across all timeframes to avoid partial drawing
   ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);

// Validate the swing structure:
// The older swing high should be higher than the later swing high to confirm a descending trend line
   if(first_high < second_high && second_high > 0)
     {
      // Extend the trend line indefinitely to the right for better visual guidance
      ObjectSetInteger(chart_id, down_trend, OBJPROP_RAY_RIGHT, true);

      // Make the trend line visible on all chart timeframes
      ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

      // Set the trend line color to dark green for clear distinction
      ObjectSetInteger(chart_id, down_trend, OBJPROP_COLOR, clrDarkGreen);

      // Set the thickness of the trend line to 3 pixels for better visibility
      ObjectSetInteger(chart_id, down_trend, OBJPROP_WIDTH, 3);

      //REVERSAL

      td_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[0],0);
      td1_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[1],0);
      td2_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[2],0);
      td3_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[3],0);

      int no_bars = 0;

      for(int i = 0; i <= 3; i++)
        {

         if(high_price[i] > ObjectGetValueByTime(chart_id,down_trend,time_price[i],0) && open_price[i] < ObjectGetValueByTime(chart_id,down_trend,time_price[i],0)
           )
           {

            for(int j = i; j >= 0; j--)
              {

               if(close_price[j] < open_price[j] && close_price[j] < ObjectGetValueByTime(chart_id,down_trend,time_price[j],0))
                 {

                  no_bars = Bars(_Symbol,time_frame,time_price[j],TimeCurrent());

                  break;

                 }

              }
            break;

           }

        }

      bool prev_touch = false;

      if((high_price[1] > td1_line_value && close_price[1] < open_price[1])
         ||
         (high_price[2] > td2_line_value && close_price[2] < open_price[2])
        )
        {

         prev_touch = true;

        }

      if(((high_price[1] >= td1_line_value && open_price[1] < td1_line_value) || (high_price[2] >= td2_line_value && open_price[2] < td2_line_value)
          || (high_price[3] >= td3_line_value && open_price[3] < td3_line_value) || (high_price[0] >= td_line_value))
         && (close_price[0] < td_line_value && close_price[0] < open_price[0] && open_price[1] < td1_line_value)
         && (no_bars < 3)
         && prev_touch == false
         && (allow_reversal == true  && currentBarTime != lastTradeBarTime)
        )
        {

         trade.Sell(lot_size,_Symbol,ask_price,ask_price + sl_points, ask_price - tp_points);
         lastTradeBarTime = currentBarTime;

        }

      //BREAKOUT AMD RETEST

      // Flag to track whether a recent bullish wick rejection (touch) already occurred
      bool prev_touch2 = false;

      // Check the last 2 candles for bullish rejection from below the descending trend line
      // A bullish rejection occurs when the low goes below the trend line but closes above the open (bullish candle)
      if((low_price[1] < td1_line_value && close_price[1] > open_price[1]) ||
         (low_price[2] < td2_line_value && close_price[2] > open_price[2] && open_price[2] > td2_line_value))
        {
         prev_touch2 = true; // Set flag to prevent duplicate signals from the same type of setup
        }

      // Variable to hold how many bars ago a bullish confirmation candle occurred after wick rejection
      int no_bars2 = 0;

      // Loop through the last 4 candles to detect a wick rejection of the descending trend line
      for(int i = 0; i <= 3; i++)
        {
         // Condition: Candle wick (low) goes below the trend line, but the open is above it
         if(low_price[i] < ObjectGetValueByTime(chart_id, down_trend, time_price[i], 0) &&
            open_price[i] > ObjectGetValueByTime(chart_id, down_trend, time_price[i], 0))
           {
            // Look backward for a bullish confirmation candle that closes above the trend line
            for(int j = i; j >= 0; j--)
              {
               if(close_price[j] > open_price[j] &&
                  close_price[j] > ObjectGetValueByTime(chart_id, down_trend, time_price[j], 0))
                 {
                  // Count how many bars ago that bullish confirmation happened
                  no_bars2 = Bars(_Symbol, time_frame, time_price[j], TimeCurrent());
                  break; // Exit inner loop once confirmation is found
                 }
              }
            break; // Exit outer loop after the first valid retest is processed
           }
        }

      // Final conditions to confirm a breakout or retest for a BUY setup on descending trend line:
      // 1. One of the last 4 candles had a wick below the trend line but opened above it
      // 2. Current candle is bullish and closed above the trend line
      // 3. A valid bullish confirmation occurred within the last 3 bars
      // 4. No recent similar touch detected (prev_touch2 == false)
      // 5. Candle timestamps are valid (not too far back)
      // 6. Breakout trading is allowed, and this bar is not the same as the last trade bar
      if(
         ((low_price[0] < td_line_value && open_price[0] > td_line_value) ||
          (low_price[1] < td1_line_value && open_price[1] > td1_line_value) ||
          (low_price[2] < td2_line_value && open_price[2] > td2_line_value) ||
          (low_price[3] < td3_line_value && open_price[3] > td3_line_value)) &&
         (close_price[0] > open_price[0]) && close_price[0] > td_line_value &&
         (no_bars2 < 3) &&
         prev_touch2 == false &&
         (time_price[3] > lookbackfd_time) &&
         (allow_break_out == true && currentBarTime != lastTradeBarTime)
      )
        {
         // All conditions met - place a BUY trade with defined SL and TP
         trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points);

         // Update the last trade time to avoid repeated trades from the same bar
         lastTradeBarTime = currentBarTime;
        }
     }
  }

Saída:

Figura 22. Rompimento da linha de tendência de baixa

Figura 23. Reversão na linha de tendência de baixa

Explicação:

Nesta seção, foi implementada a lógica de execução de operações de compra com base em um rompimento ou repique a partir da linha de tendência de baixa. Para determinar se ocorreu recentemente uma reação de alta com pavio, primeiro criamos uma variável booleana chamada prev_touch2. Dessa forma, você pode evitar a duplicação de operações com base no mesmo sinal. Em seguida, observamos os dois últimos candles para determinar se eles tiveram pavios que, no fim, fecharam em alta, ou seja, acima da abertura, apesar de terem ultrapassado brevemente a linha de tendência para baixo. Se esse cenário for detectado, prev_touch2 recebe o valor true.

Em seguida, o número de candles desde o surgimento de um candle válido de confirmação de alta é armazenada na variável no_bars2. Para determinar se um reteste é provável, percorremos os quatro últimos candles e verificamos se eles tiveram pavios que desceram abaixo da linha de tendência de baixa, com a abertura do candle acima dela. Depois, se essa rejeição por pavio for detectada, retrocedemos no histórico a partir desse candle para encontrar um candle de confirmação de alta que tenha fechado acima da linha de tendência de baixa e de seu preço de abertura. Quando encontramos esse candle de confirmação, armazenamos esse número em no_bars2 e determinamos quantas barras atrás ele apareceu.

Por fim, para verificar a validade do setup de compra, adicionamos várias condições. Verificamos se algum dos quatro candles anteriores abriu acima da linha de tendência, mas teve o pavio abaixo dela. Também confirmamos que o candle atual fechou acima da linha de tendência, é de alta, com fechamento acima da abertura, e que a confirmação de alta ocorreu dentro das três barras anteriores.

Da mesma forma, também verificamos se o setup ainda é válido em termos temporais, se as operações por rompimento estão permitidas, se o sinal não vem do mesmo candle da operação anterior e se não foram registrados repiques recentes (prev_touch2). O EA usa o tamanho de lote especificado e os níveis de stop-loss e take-profit para executar uma operação de compra se todos esses critérios forem atendidos. Ele também atualiza lastTradeBarTime para evitar a duplicação de operações com base no mesmo candle.


Conclusão

Considerando tudo o que vimos neste artigo, agora você tem o conhecimento básico para trabalhar com qualquer padrão gráfico que envolva linhas de tendência. Desde canais até linhas de tendência de alta e de baixa, você aprendeu como extrair os valores das linhas de tendência e compará-los com o price action, além de executar operações com base em condições claras de rompimento ou reversão. Aplicamos esse conhecimento na prática, usando-o para criar um EA capaz de detectar automaticamente interações com a linha de tendência e reagir a elas em tempo real. Essa lógica é bastante adaptável, ou seja, você pode aplicá-la a outros padrões gráficos, como triângulos, cunhas e até padrões de topo duplo ou fundo duplo.

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

Arquivos anexados |
Últimos Comentários | Ir para discussão (5)
[Excluído] | 5 jun. 2025 em 08:17
Obrigado por este artigo! Só gostaria de informá-lo de que há um pequeno erro no cálculo do stoploss e do takeprofit :-) Ele não é baseado em pontos. Experimente em qualquer par de moedas (EURUSD, GBPUSD, etc.)
ALGOYIN LTD
Israel Pelumi Abioye | 5 jun. 2025 em 13:16
Dominic Michael Frehner GBPUSD, etc.)
Olá, Dominic.
Obrigado por suas palavras gentis, mas não se trata de um bug. Por exemplo, você pode decidir usar 0,0010 para 10 pips, isso depende do instrumento
Yaovi Inoussa Atchou
Yaovi Inoussa Atchou | 11 jul. 2025 em 13:30

Olá, amigo

Seu Ea pode funcionar com derivativos sobre volatilidade?

Celestine Nwakaeze
Celestine Nwakaeze | 28 out. 2025 em 15:48
Obrigado por esse curso, é muito educativo. Deus o abençoe.
ALGOYIN LTD
Israel Pelumi Abioye | 28 out. 2025 em 17:42
Celestine Nwakaeze #:
Obrigado por esse artigo, ele é muito instrutivo. Que Deus o abençoe.

De nada, obrigado por suas palavras gentis.


Ciência de dados e aprendizado de máquina (Parte 44): Previsão de séries OHLC no Forex pelo método de autorregressão vetorial (VAR) Ciência de dados e aprendizado de máquina (Parte 44): Previsão de séries OHLC no Forex pelo método de autorregressão vetorial (VAR)
Neste material, veremos como os modelos de autorregressão vetorial (VAR) podem prever séries temporais de valores OHLC (preço de abertura, máxima, mínima e preço de fechamento) no Forex. Falaremos sobre como implementar modelos VAR, treiná-los e gerar previsões em tempo real no MetaTrader 5, analisando movimentos interdependentes das taxas de câmbio para obter melhores resultados no trading.
Automatização de estratégias de negociação em MQL5 (Parte 20): estratégia multissímbolo usando CCI e AO Automatização de estratégias de negociação em MQL5 (Parte 20): estratégia multissímbolo usando CCI e AO
Neste artigo, desenvolveremos uma estratégia de negociação multissímbolo usando os indicadores CCI e AO para identificar reversões de tendência. Veremos o projeto, a implementação em MQL5 e os testes da estratégia em dados históricos. Na conclusão, são apresentadas recomendações para melhorar o desempenho.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Envio de mensagens de MQL5 para o Discord, criação de um bot Discord-MetaTrader 5 Envio de mensagens de MQL5 para o Discord, criação de um bot Discord-MetaTrader 5
Assim como o Telegram, o Discord é capaz de receber informações e mensagens em formato JSON usando suas APIs de comunicação. Neste artigo, veremos como usar a API do Discord para enviar sinais de trading e atualizações do MetaTrader 5 para sua comunidade de trading no Discord.