English Русский 中文 Español Deutsch 日本語
preview
Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 21): Detector de Inversão da Estrutura de Mercado

Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 21): Detector de Inversão da Estrutura de Mercado

MetaTrader 5 — Sistemas de negociação |
149 1
Christian Benjamin
Christian Benjamin

Conteúdo


Introdução

Este EA aborda um dos desafios mais difíceis do trading: os falsos sinais de reversão. Uma price action lateral e errática podem comprometer rotinas simples de identificação de pivôs, expondo os traders a whipsaws frequentes. O Market Structure Flip Detector soluciona esse problema convertendo o ATR em um filtro flexível baseado em quantidade de barras. Ele ignora pequenas oscilações, identifica apenas máximas e mínimas válidas e, em seguida, sinaliza um flip de baixa quando uma higher high (máxima mais alta) passa a ser seguida por uma lower high (máxima mais baixa), ou um flip de alta quando uma lower low passa a ser seguida por uma higher low. Ao longo do artigo, você verá como:

  • Converter o ATR em uma medida de profundidade que aumenta em mercados mais voláteis e diminui em mercados mais calmos.
  • Confirmar pivôs analisando uma quantidade exata de barras em cada lado de uma máxima ou mínima candidata.
  • Manter um viés de tendência, de modo que os flips sejam acionados somente após o rompimento da estrutura anterior.
  • Sinalizar os eventos com setas no gráfico, rótulos nos pivôs e um painel de estatísticas em tempo real que acompanha a quantidade de flips e os momentos em que ocorrem.

Ao final, você terá um EA robusto, capaz de filtrar o ruído do mercado e apresentar apenas os sinais de reversão mais claros e baseados em regras, incluindo alertas sonoros e notificações push quando flips efetivos ocorrerem.


Por que esta ferramenta é importante

Falsos sinais de reversão podem representar mais da metade dos sinais gerados por pivôs em mercados com movimentos irregulares, provocando whipsaws frequentes e expectativa matemática negativa. Ao vincular nosso filtro de swing points ao Average True Range (ATR), indicador introduzido por Wilder em 1978 para medir a volatilidade de forma mais precisa do que simples intervalos entre máxima e mínima, ajustamos em tempo real a "profundidade" necessária para confirmar um pivô. Quando o ATR aumenta durante períodos de forte volatilidade, nosso filtro amplia a janela para ignorar oscilações pequenas e erráticas. Quando o mercado se acalma, essa janela diminui, permitindo identificar movimentos de reversão relevantes mais rapidamente.

O True Range (TR) de cada barra é definido como:

Fórmula do TR


O ATR é a média móvel simples do TR ao longo de n períodos (por padrão, n = 14).

Convertemos o ATR em uma profundidade d, expressa em número de barras, por meio de:

ATR

Onde Point representa o incremento mínimo de preço, m é o multiplicador do ATR e f é o fator de redução do filtro. 

Um flip de baixa ocorre quando primeiro registramos uma higher high e, posteriormente, a próxima swing high fica abaixo dessa máxima. Um flip de alta ocorre de forma inversa. Se assumirmos que os retornos seguem uma distribuição normal com média zero e variância que chamaremos de "volatilidade ao quadrado", então a probabilidade de qualquer barra individual representar a máxima dentro de uma janela de "duas vezes a profundidade mais um" barras corresponde simplesmente a um dividido por esse mesmo valor, "duas vezes a profundidade mais um". Como vinculamos diretamente a profundidade à volatilidade do mercado — estimando a volatilidade como o ATR dividido pela raiz quadrada de metade de pi —, conseguimos controlar diretamente a taxa de falsos sinais. Na prática, isso nos permite selecionar parâmetros capazes de manter o nível de ruído em torno de 5%.

Uma pesquisa da TradeStation demonstrou que janelas de pivô baseadas no ATR reduziram em aproximadamente 40% as operações provocadas por ruído e aumentaram o lucro líquido em cerca de 22% ao longo de cinco anos de dados do S&P 500. O QuantifiedStrategies.com relata que pivôs filtrados pelo ATR elevaram a taxa de acerto de aproximadamente 35% para 58% e aumentaram a relação média entre retorno e risco de cerca de 1,1 para 1,8 em backtests realizados com EURUSD e futuros do ES. Relatos da comunidade no TradingView indicam que ferramentas de pivô com janelas baseadas no ATR apresentam boa correspondência com rompimentos relacionados ao fluxo de ordens institucionais, especialmente nos gráficos de 1 hora e 4 horas.


Visão Geral do Plano de Ação

Este EA filtra o ruído do mercado transformando a leitura atual do ATR em uma janela flexível de "profundidade": mais ampla em condições de alta volatilidade e mais estreita em períodos de baixa volatilidade. Em seguida, ele valida a máxima ou mínima de cada barra fechada comparando-a com as barras vizinhas dentro dessa janela. O EA mantém na memória as duas últimas swing highs e swing lows confirmadas e acompanha uma flag simples de viés, que muda para "up" quando uma nova máxima supera a anterior ou para "down" quando uma nova mínima fica abaixo de sua antecessora. Quando ocorre uma reversão na estrutura do mercado — isto é, quando, em um estado "up", a máxima mais recente fica abaixo da máxima anterior (flip de baixa), ou quando, em um estado "down", a mínima mais recente fica acima da mínima anterior (flip de alta) —, o EA insere uma seta colorida no gráfico, rotula ambos os pivôs, atualiza o painel de estatísticas em tempo real e, opcionalmente, aciona alertas sonoros ou notificações push. Essa abordagem permite visualizar apenas reversões efetivas de "higher high para lower high" ou de "lower low para higher low".

Flip de Baixa

Uma tendência de alta é identificada quando o preço registra duas higher highs consecutivas. Em seguida, o Expert Advisor (EA) procura uma swing high que esteja abaixo da swing high anterior. Uma swing high é definida como o ponto mais alto dentro de uma janela determinada com base no Average True Range (ATR). Quando o EA está no estado "up" e identifica essa lower high, ele marca a barra correspondente com uma seta vermelha e o rótulo "LH". Além dessa indicação visual, o EA gera um alerta e registra o flip de baixa, sinalizando que os vendedores podem estar começando a assumir o controle.

Flip de Baixa

Flip de Alta

Uma tendência de baixa é identificada quando o preço registra duas lower lows consecutivas. Em seguida, o Expert Advisor (EA) identifica uma swing low acima da swing low anterior. Uma swing low é definida como o ponto mais baixo dentro de uma janela determinada com base no Average True Range (ATR). Quando o EA está no estado "down" e identifica essa higher low, ele insere uma seta verde com o rótulo "HL" na barra correspondente. Além disso, o EA gera um alerta e registra o flip de alta, indicando um possível retorno da pressão compradora.

Flip de Alta


Estrutura do Expert Advisor

Ao iniciar qualquer arquivo MQL5, adicionamos linhas #property para definir as propriedades básicas do programa. Aqui, habilitamos a compilação estrita para que o compilador identifique conversões de tipo potencialmente inseguras ou chamadas obsoletas. Também incluímos as propriedades de copyright, link e versão para que qualquer pessoa que consulte o código saiba quem o desenvolveu, onde encontrar mais informações e qual versão está sendo utilizada. Essas linhas não interferem na lógica do programa; elas servem para identificar o arquivo.

#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

Em seguida, disponibilizamos todos os parâmetros que o trader pode querer ajustar. Os três primeiros inputs determinam como identificamos os pivôs com base no ATR.

  • InpAtrPeriod define quantas barras são utilizadas no cálculo do ATR. Um período curto (por exemplo, 7) reage rapidamente, mas pode captar mais ruído. Um período mais longo (por exemplo, 21) suaviza os picos, porém introduz maior atraso na resposta.
  • InpAtrMultiplier converte a amplitude do ATR em uma largura mínima necessária para identificar um swing. Com o valor 1,0, é necessário um movimento equivalente a um ATR para marcar um pivô. Aumentar esse valor para 1,5 ou 2,0 torna o filtro mais seletivo.
  • InpAtrLoosenFactor reduz proporcionalmente essa largura utilizando um fator entre 0 e 1. Um fator de 0,5 reduz o requisito pela metade, fazendo com que os pivôs sejam identificados mais cedo, o que pode ser útil em períodos de baixa volatilidade.

Em seguida, configuramos a disposição dos elementos no gráfico.

  • InpAutoShift reserva automaticamente barras vazias à direita à medida que novas barras são formadas.
  • InpShiftBars define quantas barras vazias serão reservadas (cinco, por padrão).

Esse espaçamento simples evita que as setas, os rótulos e o painel de estatísticas fiquem sobrepostos à movimentação dos preços.

Por fim, disponibilizamos dois métodos de alerta:

  • InpEnableSound permite que o EA reproduza um arquivo WAV sempre que ocorrer um flip.
  • InpSoundFile define o nome desse arquivo, localizado na pasta Sounds do MetaTrader 5.
  • InpEnablePush envia uma notificação push para o aplicativo móvel do MetaTrader 5.

Com essas opções, você pode escolher entre receber um alerta sonoro no computador, uma notificação no celular ou ambos.

input int    InpAtrPeriod       = 14;      // How many bars for ATR
input double InpAtrMultiplier   = 1.0;     // Scale ATR into bar‑depth
input double InpAtrLoosenFactor = 0.5;     // Optional: loosen the swing filter
input bool   InpAutoShift       = true;    // Push bars left for visibility
input int    InpShiftBars       = 5;       // Number of bars for right margin
input bool   InpEnableSound     = true;    // Play a sound on flip
input string InpSoundFile       = "alert.wav";
input bool   InpEnablePush      = false;   // Send push notifications

Quando o EA é iniciado, OnInit primeiro verifica InpAutoShift e, se estiver habilitado, chama ChartSetInteger com CHART_SHIFT e InpShiftBars para deslocar as novas barras para a esquerda e reservar um espaço livre para as anotações. Em seguida, solicita um handle para o indicador ATR integrado ao MetaTrader por meio de iATR, armazenando-o em atrHandle e interrompendo imediatamente a inicialização com INIT_FAILED caso o handle seja inválido. Por fim, cria um rótulo no canto do gráfico (um OBJ_LABEL identificado por panelName), fixa-o no canto superior esquerdo, aplica um deslocamento horizontal e vertical de 10 pixels, define o tamanho da fonte como 10 e a cor como amarela e retorna INIT_SUCCEEDED para confirmar que o acesso aos dados do ATR e o painel de estatísticas estão prontos para serem utilizados por OnTick.

int OnInit()
{
   if(InpAutoShift)
      ChartSetInteger(0, CHART_SHIFT, InpShiftBars);

   atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
   if(atrHandle == INVALID_HANDLE)
      return INIT_FAILED;

   ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, panelName, OBJPROP_CORNER,    CORNER_LEFT_UPPER);
   ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE,  10);
   ObjectSetInteger(0, panelName, OBJPROP_COLOR,     clrYellow);

   return INIT_SUCCEEDED;
}

Sempre que removemos o EA ou encerramos o MetaTrader 5, OnDeinit exclui todas as setas e objetos de texto que criamos, remove o rótulo do painel de estatísticas e libera o handle do ATR. Isso evita o acúmulo de objetos no gráfico e libera os recursos utilizados.

void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, -1, OBJ_ARROW);
   ObjectsDeleteAll(0, -1, OBJ_TEXT);
   ObjectDelete(0, panelName);
   if(atrHandle != INVALID_HANDLE)
      IndicatorRelease(atrHandle);
}

Na rotina OnTick, evitamos executar a lógica de identificação de pivôs a cada atualização de preço utilizando uma variável datetime estática lastBar. Obtemos thisBar com iTime(...,1), que retorna o horário de abertura da barra fechada mais recente, e o comparamos com lastBar. Se o valor não tiver mudado, sabemos que ainda estamos na mesma barra e simplesmente encerramos a execução com return. Quando thisBar passa a ser diferente, atualizamos lastBar e prosseguimos com o processamento.

Em seguida, chamamos CopyBuffer utilizando o handle do ATR para obter apenas o valor mais recente do indicador. Se essa chamada falhar, interrompemos o processamento para evitar trabalhar com dados inválidos. Com um valor válido do ATR disponível, calculamos a "profundidade" do swing convertendo as unidades de preço do ATR em uma quantidade de barras. Dividimos o valor pelo menor incremento de preço (SYMBOL_POINT) e multiplicamos o resultado por InpAtrMultiplier e InpAtrLoosenFactor. Em seguida, utilizamos MathMax para garantir uma profundidade mínima de uma barra. Dessa forma, obtemos uma profundidade dinâmica que aumenta quando a volatilidade se intensifica, exigindo swings maiores para confirmar os pivôs, e diminui quando o mercado se acalma, permitindo que movimentos menores sejam considerados. Depois disso, passamos esse valor para as funções responsáveis pela identificação efetiva dos pivôs.

void OnTick()
{
   static datetime lastBar=0;
   datetime thisBar = iTime(_Symbol,_Period,1);
   if(thisBar == lastBar) return;
   lastBar = thisBar;

   double atrBuf[];
   if(CopyBuffer(atrHandle, 0, 1, 1, atrBuf) <= 0) return;
   double atr = atrBuf[0];

   int depth = MathMax(1,
      int(atr / SymbolInfoDouble(_Symbol, SYMBOL_POINT)
          * InpAtrMultiplier * InpAtrLoosenFactor));
   // … pivot checks follow …
}

Com a profundidade definida, executamos dois loops simples para validar os pivôs. IsSwingHigh(1, depth) verifica se nenhuma barra dentro do intervalo de depth barras em cada lado supera a máxima candidata; IsSwingLow realiza a verificação inversa para as mínimas. Quando identificamos uma nova máxima ou mínima, transferimos lastHigh para prevHigh — e fazemos o mesmo para as mínimas — e registramos o timestamp correspondente. Manter tanto o pivô anterior quanto o atual é o que nos permite compará-los na etapa seguinte.

bool newHigh = IsSwingHigh(1, depth);
bool newLow  = IsSwingLow (1, depth);
double h = iHigh(_Symbol,_Period,1), l = iLow(_Symbol,_Period,1);

if(newHigh)
{
   prevHigh     = lastHigh;
   prevHighTime = lastHighTime;
   lastHigh     = h;
   lastHighTime = thisBar;
}
if(newLow)
{
   prevLow      = lastLow;
   prevLowTime  = lastLowTime;
   lastLow      = l;
   lastLowTime  = thisBar;
}

Depois de identificarmos os pivôs, atualizamos structState para representar o viés da estrutura: uma higher high define o estado como 1 (possibilidade de um flip de baixa a seguir), enquanto uma lower low define o estado como 2 (possibilidade de um flip de alta a seguir). Em seguida, verificamos se o flip efetivamente ocorreu: no estado 1, se a nova máxima estiver abaixo da máxima anterior, temos um flip de baixa; no estado 2, se a nova mínima estiver acima da mínima anterior, temos um flip de alta. Quando um flip é identificado, acionamos as funções de representação gráfica e de notificação e incrementamos os respectivos contadores.

// Update bias
if(newHigh && prevHigh>0 && lastHigh > prevHigh) structState = 1;
if(newLow  && prevLow>0  && lastLow  < prevLow) structState = 2;

// Bearish flip
if(newHigh && structState==1 && lastHigh < prevHigh)
{
   PlotArrow(...);  PlotLabel(...);  Notify(...);
   if(countBear>0) sumBearInterval += (lastHighTime - prevLowTime)/60.0;
   countBear++;
}

// Bullish flip
if(newLow && structState==2 && lastLow > prevLow)
{
   PlotArrow(...);  PlotLabel(...);  Notify(...);
   if(countBull>0) sumBullInterval += (lastLowTime - prevHighTime)/60.0;
   countBull++;
}

Concentramos todos os elementos gráficos exibidos no gráfico em duas funções auxiliares — PlotArrow e PlotLabel — para garantir eficiência e clareza. Dentro de cada função, primeiro chamamos ObjectFind(0, name), que procura um objeto existente no gráfico por seu nome exclusivo. Essa operação possui complexidade O(n) em relação à quantidade de objetos, mas é suficientemente rápida em computadores atuais para verificações ocasionais realizadas a cada barra. Se o objeto ainda não existir (ObjectFind retorna –1), nós o criamos uma única vez com ObjectCreate, escolhendo o tipo de objeto adequado: uma seta para PlotArrow ou um rótulo de texto para PlotLabel.

Em seguida, personalizamos suas propriedades: para as setas, definimos OBJPROP_ARROWCODE para selecionar o símbolo desejado (por exemplo, o código Wingdings 234 para uma seta vermelha apontando para baixo) e OBJPROP_COLOR para determinar sua cor; para os rótulos, configuramos OBJPROP_TEXT com o texto correspondente, como "LH" ou "HL", além dos deslocamentos e do tamanho da fonte. Ao evitar chamadas repetidas a ObjectCreate, prevenimos a perda de desempenho e o consumo desnecessário de memória que poderiam ocorrer caso centenas ou milhares de objetos idênticos fossem acumulados ao longo do tempo. Essa abordagem também garante que cada marcador de pivô tenha um identificador estável e previsível. Assim, caso você queira posteriormente ajustar seu OBJPROP_ZORDER (prioridade de exibição) ou excluí-lo sob determinadas condições, poderá referenciá-lo pelo nome com segurança, sem afetar acidentalmente outros elementos do gráfico.

void PlotArrow(string name, datetime t, double price, int code, color c)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_ARROW, 0, t, price);
      ObjectSetInteger(0, name, OBJPROP_ARROWCODE, code);
      ObjectSetInteger(0, name, OBJPROP_COLOR, c);
   }
}
// PlotLabel is identical, but creates OBJ_TEXT and sets OBJPROP_TEXT.

Após cada barra, atualizamos o rótulo panelName para exibir:

  • A profundidade atual dos pivôs
  • O total de flips de alta e de baixa
  • O intervalo médio, em minutos, entre os flips (quando houver pelo menos dois).

Isso permite visualizar imediatamente com que frequência ocorrem mudanças na estrutura sob as configurações de ATR escolhidas.

string txt = StringFormat("Depth: %d\nBull Flips: %d\nBear Flips: %d",
                          depth, countBull, countBear);
if(countBull>1)
   txt += "\nAvg HL Int: " + DoubleToString(sumBullInterval/(countBull-1),1) + "m";
if(countBear>1)
   txt += "\nAvg LH Int: " + DoubleToString(sumBearInterval/(countBear-1),1) + "m";

ObjectSetString(0, panelName, OBJPROP_TEXT, txt);

Por fim, nossa função Notify(msg) reúne todos os métodos de alerta em um único local. Sempre chamamos Alert(msg) para exibir uma notificação pop-up no MetaTrader 5 e, de acordo com os inputs configurados, também podemos reproduzir um som com PlaySound ou enviar uma notificação push com SendNotification. Centralizar essas funções torna simples adicionar posteriormente alertas por e-mail ou webhook.

void Notify(string msg)
{
   Alert(msg);
   if(InpEnableSound) PlaySound(InpSoundFile);
   if(InpEnablePush)  SendNotification(msg);
}


Listagem do Código-Fonte

//+------------------------------------------------------------------+
//|                                 Market Structure Flip Detector EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//--- user inputs
input int    InpAtrPeriod       = 14;      // ATR lookback
input double InpAtrMultiplier   = 1.0;     // ATR to swing depth factor (lower = looser)
input double InpAtrLoosenFactor = 0.5;     // Loosen factor for ATR confirmation (0-1)
input bool   InpAutoShift       = true;    // Auto-enable chart right shift
input int    InpShiftBars       = 5;       // Bars for right margin
input bool   InpEnableSound     = true;
input string InpSoundFile       = "alert.wav";
input bool   InpEnablePush      = false;

//--- global vars
string   panelName = "FlipPanel";
int      atrHandle;
int      structState = 0;
double   prevHigh=0, lastHigh=0;
datetime prevHighTime=0, lastHighTime=0;
double   prevLow=0, lastLow=0;
datetime prevLowTime=0, lastLowTime=0;
int      countBull=0, countBear=0;
double   sumBullInterval=0, sumBearInterval=0;

//+------------------------------------------------------------------+
int OnInit()
  {
   if(InpAutoShift)
      ChartSetInteger(0, CHART_SHIFT, InpShiftBars);

   atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
   if(atrHandle == INVALID_HANDLE)
      return(INIT_FAILED);

   ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, panelName, OBJPROP_CORNER,    CORNER_LEFT_UPPER);
   ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE,  10);
   ObjectSetInteger(0, panelName, OBJPROP_COLOR,     clrYellow);
   ObjectSetInteger(0, panelName, OBJPROP_BACK,      false);
   ObjectSetInteger(0, panelName, OBJPROP_ZORDER,    1);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, -1, OBJ_ARROW);
   ObjectsDeleteAll(0, -1, OBJ_TEXT);
   ObjectDelete(0, panelName);
   if(atrHandle != INVALID_HANDLE)
      IndicatorRelease(atrHandle);
  }

//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime lastBar=0;
   datetime thisBar = iTime(_Symbol,_Period,1);
   if(thisBar==lastBar)
      return;
   lastBar=thisBar;

   double atrBuf[];
   if(CopyBuffer(atrHandle,0,1,1,atrBuf)<=0)
      return;
   double atr = atrBuf[0];

// loosen ATR confirmation by InpAtrLoosenFactor (0-1)
   double rawDepth = atr/SymbolInfoDouble(_Symbol,SYMBOL_POINT)*InpAtrMultiplier;
   int depth = MathMax(1, (int)(rawDepth * InpAtrLoosenFactor));

   bool newHigh=false,newLow=false;
   double h=iHigh(_Symbol,_Period,1), l=iLow(_Symbol,_Period,1);

   if(IsSwingHigh(1,depth))
     {
      prevHigh = lastHigh;
      prevHighTime = lastHighTime;
      lastHigh = h;
      lastHighTime = thisBar;
      newHigh = true;
     }
   if(IsSwingLow(1,depth))
     {
      prevLow = lastLow;
      prevLowTime = lastLowTime;
      lastLow = l;
      lastLowTime = thisBar;
      newLow = true;
     }

   double off = SymbolInfoDouble(_Symbol,SYMBOL_POINT)*10;

// Bearish Flip: Lower High after a Higher High
   if(newHigh && structState==1 && prevHigh>0 && lastHigh<prevHigh)
     {
      // signal arrow and label at current LH
      PlotArrow("Bear_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh, 234, clrRed);
      PlotLabel("LH_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh+off, "LH", clrRed);
      // label the previous LH used for comparison
      PlotLabel("PrevLH_"+IntegerToString((int)prevHighTime), prevHighTime, prevHigh+off, "LH_prev", clrRed);
      Notify("Bearish Flip (LH) at "+TimeToString(lastHighTime,TIME_DATE|TIME_MINUTES));
      if(countBear>0)
         sumBearInterval += (lastHighTime-prevLowTime)/60.0;
      countBear++;
     }

// Bullish Flip: Higher Low after a Lower Low
   if(newLow && structState==2 && prevLow>0 && lastLow>prevLow)
     {
      // signal arrow and label at current HL
      PlotArrow("Bull_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow, 233, clrLime);
      PlotLabel("HL_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow-off, "HL", clrLime);
      // label the previous HL used for comparison
      PlotLabel("PrevHL_"+IntegerToString((int)prevLowTime), prevLowTime, prevLow-off, "HL_prev", clrLime);
      Notify("Bullish Flip (HL) at "+TimeToString(lastLowTime,TIME_DATE|TIME_MINUTES));
      if(countBull>0)
         sumBullInterval += (lastLowTime-prevHighTime)/60.0;
      countBull++;
     }

// update structure state
   if(newHigh && prevHigh>0 && lastHigh>prevHigh)
      structState = 1;
   if(newLow  && prevLow>0  && lastLow <prevLow)
      structState = 2;

// update panel stats
   string txt = "Depth: "+IntegerToString(depth)+"\n";
   txt += "Bull Flips: "+IntegerToString(countBull)+"\n";
   txt += "Bear Flips: "+IntegerToString(countBear);
   if(countBull>1)
      txt += "\nAvg HL Int: "+DoubleToString(sumBullInterval/(countBull-1),1)+"m";
   if(countBear>1)
      txt += "\nAvg LH Int: "+DoubleToString(sumBearInterval/(countBear-1),1)+"m";
   ObjectSetString(0, panelName, OBJPROP_TEXT, txt);
  }

//+------------------------------------------------------------------+
bool IsSwingHigh(int shift,int depth)
  {
   double p = iHigh(_Symbol,_Period,shift);
   for(int i=shift-depth; i<=shift+depth; i++)
      if(i>=0 && iHigh(_Symbol,_Period,i) > p)
         return false;
   return true;
  }

//+------------------------------------------------------------------+
bool IsSwingLow(int shift,int depth)
  {
   double p = iLow(_Symbol,_Period,shift);
   for(int i=shift-depth; i<=shift+depth; i++)
      if(i>=0 && iLow(_Symbol,_Period,i) < p)
         return false;
   return true;
  }

//+------------------------------------------------------------------+
void PlotArrow(string nm,datetime t,double price,int code,color c)
  {
   if(ObjectFind(0,nm) < 0)
     {
      ObjectCreate(0, nm, OBJ_ARROW, 0, t, price);
      ObjectSetInteger(0, nm, OBJPROP_ARROWCODE, code);
      ObjectSetInteger(0, nm, OBJPROP_COLOR, c);
      ObjectSetInteger(0, nm, OBJPROP_WIDTH, 2);
     }
  }

//+------------------------------------------------------------------+
void PlotLabel(string nm,datetime t,double price,string txt,color c)
  {
   if(ObjectFind(0,nm) < 0)
     {
      ObjectCreate(0, nm, OBJ_TEXT, 0, t, price);
      ObjectSetString(0, nm, OBJPROP_TEXT, txt);
      ObjectSetInteger(0, nm, OBJPROP_COLOR, c);
      ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 10);
     }
  }

//+------------------------------------------------------------------+
void Notify(string msg)
  {
   Alert(msg);
   if(InpEnableSound)
      PlaySound(InpSoundFile);
   if(InpEnablePush)
      SendNotification(msg);
  }
//+------------------------------------------------------------------+


Resultados de Desempenho

A seguir, apresentaremos os resultados dos testes realizados tanto em condições de mercado em tempo real quanto em backtesting.

Mercado em Tempo Real

Resultados em Mercado em Tempo Real

No gráfico acima, o EA identifica primeiro uma swing high marcada como "LH_prev", que representa duas higher highs consecutivas e estabelece uma estrutura de alta. Algumas barras depois, ele identifica outra swing high que não supera a máxima anterior. Essa lower high dentro de uma tendência de alta faz com que o EA insira uma seta vermelha e o rótulo "LH" na barra correspondente. Esse sinal de flip de baixa indica a perda de força da estrutura de alta e alerta para a possibilidade de início de um movimento de queda.

GIF do Mercado em Tempo Real

Abaixo, apresentamos um GIF demonstrando o desempenho do EA no EURUSD. À medida que cada barra de um minuto é fechada, o EA acompanha as mínimas sucessivas até identificar uma swing low acima da mínima anterior. Quando essa higher low é identificada, o EA insere uma seta verde com o rótulo "HL" para marcar o flip de alta. Ao mesmo tempo, painel de cabeçalho é atualizado — neste exemplo, exibindo 12 flips de alta, 1 flip de baixa e um intervalo médio entre HLs de 108,0 minutos — para refletir os novos valores dos contadores. Esse exemplo ilustra claramente a transição de uma estrutura de baixa para um possível movimento de alta.

Resultados em Mercado em Tempo Real

Backtesting

Abaixo, apresentamos uma tabela com os resultados da análise do Step Index em diferentes timeframes. "Sinais positivos" são aqueles após os quais o mercado se movimentou na direção indicada durante um período prolongado.

Timeframe de 5 Minutos

Tipo de Sinal Total de Sinais Sinais Positivos Taxa de Acerto
Venda 56 39 70%
Compra 53 44 83%

Timeframe de 15 Minutos

Tipo de Sinal Total de Sinais Sinais Positivos Taxa de Acerto
Venda 7 5 71%
Compra 14 9 64%

O resumo da análise indica que o Market Structure Flip Detector gerou sinais lucrativos de forma consistente nos testes apresentados, especialmente nos timeframes mais curtos. Os setups de venda apresentaram taxa de acerto de 70% ou mais, reforçando os resultados obtidos pela ferramenta nesses testes. Esse resultado representa um avanço importante na automação da análise de Price Action e nos aproxima de um conjunto de ferramentas de trading totalmente sistemático e de baixa latência.



Conclusão

Após desenvolver e testar esta ferramenta tanto em condições de mercado em tempo real quanto por meio de backtesting, nossa análise mostra que ela apresenta um desempenho consistente, especialmente em operações de scalping em timeframes mais curtos, nos quais apresentou desempenho forte. No entanto, é fundamental ter cautela e validar seus sinais utilizando métodos adicionais de confirmação antes de executar qualquer operação. Além disso, testar a ferramenta em diferentes pares de moedas é essencial para identificar aqueles nos quais ela apresenta os melhores resultados. Durante os testes, você também pode ajustar os parâmetros de input para otimizar ainda mais o desempenho.

Data Nome da Ferramenta  Descrição Versão  Atualizações  Notas
01/10/24 Projetor de Gráfico Script para sobrepor o Price Action do dia anterior ao gráfico atual utilizando um efeito fantasma. 1.0 Lançamento Inicial Ferramenta número 1
18/11/24 Comentário Analítico Apresenta as informações do dia anterior em formato tabular e também antecipa a possível direção futura do mercado. 1.0 Lançamento Inicial Ferramenta número 2
27/11/24 Mestre Analítico Atualização periódica das métricas do mercado a cada duas horas  1.01 Segundo Lançamento Ferramenta número 3
02/12/24 Previsor Analítico  Atualização periódica das métricas do mercado a cada duas horas, com integração ao Telegram 1.1 Terceira Edição Ferramenta número 4
09/12/24 Navegador de Volatilidade O EA analisa as condições do mercado utilizando os indicadores Bollinger Bands, RSI e ATR 1.0 Lançamento Inicial Ferramenta número 5
19/12/24 Coletor de Sinais de Reversão à Média  Analisa o mercado utilizando uma estratégia de reversão à média e fornece sinais  1.0  Lançamento Inicial  Ferramenta número 6 
9/01/25  Pulso de Sinais  Analisador de múltiplos timeframes 1.0  Lançamento Inicial  Ferramenta número 7 
17/01/25  Painel de Métricas  Painel com botão para análise  1.0  Lançamento Inicial Ferramenta número 8 
21/01/25 Fluxo Externo Análises por meio de bibliotecas externas 1.0  Lançamento Inicial Ferramenta número 9 
27/01/25 VWAP Preço Médio Ponderado por Volume   1.3  Lançamento Inicial  Ferramenta número 10 
02/02/25  Heikin Ashi  Suavização de tendência e identificação de sinais de reversão  1.0  Lançamento Inicial  Ferramenta número 11
04/02/25  FibVWAP  Geração de sinais por meio de análise em Python  1.0  Lançamento Inicial  Ferramenta número 12
14/02/25  DIVERGÊNCIA RSI  Divergências entre a ação do preço e o RSI  1.0  Lançamento Inicial  Ferramenta número 13 
17/02/25  Parada e Reversão Parabólicas (PSAR)  Automatização da estratégia PSAR 1.0 Lançamento Inicial  Ferramenta número 14
20/02/25  Script Quarters Drawer  Desenho de níveis de quarters no gráfico  1.0  Lançamento Inicial  Ferramenta número 15 
27/02/25  Detector de Intrusão Detecta e alerta quando o preço atinge níveis de quarters 1.0   Lançamento Inicial Ferramenta número 16 
27/02/25  Ferramenta TrendLoom  Painel de análise em múltiplos timeframes 1.0 Lançamento Inicial Ferramenta número 17
11/03/25  Painel de Quarters  Painel com botões para ativar ou desativar níveis de quarters  1.0  Lançamento Inicial Ferramenta número 18
26/03/25  Analisador ZigZag  Desenho de linhas de tendência com o indicador ZigZag.  1.0  Lançamento Inicial  Ferramenta número 19 
10/04/25  Explorador de Correlações Representação das correlações entre moedas utilizando bibliotecas Python. 1.0 Lançamento Inicial  Ferramenta número 20 
 23/04/25  Ferramenta de Detecção de Inversão de Estrutura de Mercado Detecção de inversão da estrutura de mercado 1.0  Lançamento Inicial  Ferramenta número 21

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

Arquivos anexados |
Flip_Detector.mq5 (14.5 KB)
Últimos Comentários | Ir para discussão (1)
Alexander P.
Alexander P. | 7 mai. 2025 em 11:08
bool IsSwingHigh( int shift, int depth)
  {
   double p = iHigh ( _Symbol , _Period ,shift);
   for ( int i= shift-depth; i<=shift+depth; i++)
       if (i>= 0 && iHigh ( _Symbol , _Period ,i) > p)
         return false ;
   return true ;
  }

Olá, não entendo por que você escreve ` int i = shift-depth `. Não daria para simplesmente usar ` int i = 0` ?

Você poderia explicar isso, por favor? Obrigado.

Técnicas do MQL5 Wizard que você deve conhecer (Parte 61): Uso de padrões do ADX e CCI com aprendizado supervisionado Técnicas do MQL5 Wizard que você deve conhecer (Parte 61): Uso de padrões do ADX e CCI com aprendizado supervisionado
Os osciladores ADX e CCI são indicadores de acompanhamento de tendência e momentum que podem ser combinados no desenvolvimento de um Expert Advisor. Veremos como essa abordagem pode ser sistematizada utilizando os três principais métodos de treinamento de Machine Learning. Os Expert Advisors montados com o MQL5 Wizard nos permitem avaliar os padrões apresentados por esses dois indicadores, e começaremos analisando como o aprendizado supervisionado pode ser aplicado a esses padrões.
Automatizando Estratégias de Trading em MQL5 (Parte 16): Rompimento da Faixa da Meia-Noite com Break of Structure (BoS) e Price Action Automatizando Estratégias de Trading em MQL5 (Parte 16): Rompimento da Faixa da Meia-Noite com Break of Structure (BoS) e Price Action
Neste artigo, automatizamos em MQL5 a estratégia de rompimento da faixa da meia-noite com Break of Structure, detalhando o código utilizado para detectar rompimentos e executar operações. Definimos parâmetros precisos de risco para entradas, Stop Loss e Take Profit. Também abordamos o backtesting e a otimização da estratégia para sua aplicação prática no trading.
Técnicas do Assistente MQL5 que você deve conhecer (Parte 62): Utilizando padrões de ADX e CCI com aprendizado por reforço via TRPO Técnicas do Assistente MQL5 que você deve conhecer (Parte 62): Utilizando padrões de ADX e CCI com aprendizado por reforço via TRPO
Os osciladores ADX e CCI são indicadores de acompanhamento de tendência e momentum que podem ser combinados no desenvolvimento de um Expert Advisor. Damos continuidade ao ponto em que paramos no último artigo, analisando como o treinamento durante o uso e a atualização do modelo desenvolvido podem ser realizados por meio do aprendizado por reforço. Utilizaremos um algoritmo que ainda não abordamos nesta série, conhecido como Trust Region Policy Optimization (TRPO). E, como sempre, a montagem de Expert Advisors por meio do Assistente MQL5 nos permite preparar nossos modelos para testes de forma muito mais rápida, além de possibilitar sua distribuição e avaliação com diferentes tipos de sinais.
Data Science e ML (Parte 37): Utilizando padrões de Candlestick e IA para superar o mercado Data Science e ML (Parte 37): Utilizando padrões de Candlestick e IA para superar o mercado
Os padrões de candlestick ajudam os traders a compreender a psicologia do mercado e a identificar tendências nos mercados financeiros, permitindo decisões de negociação mais bem fundamentadas, que podem levar a melhores resultados. Neste artigo, exploraremos como utilizar padrões de candlestick em conjunto com modelos de IA para buscar um desempenho de negociação otimizado.