A Estratégia de Negociação do Inverse Fair Value Gap
Introdução
Um inverse fair value gap (IFVG) ocorre quando o preço retorna a um fair value gap previamente identificado e, em vez de apresentar a reação esperada de suporte ou resistência, falha em respeitá-lo. Essa falha pode sinalizar uma possível mudança na direção do mercado e oferecer uma vantagem contrária de negociação. Neste artigo, vou apresentar minha abordagem desenvolvida por mim para quantificar e utilizar o inverse fair value gap como uma estratégia para expert advisors do MetaTrader 5.
Motivação da Estratégia
Compreendendo Primeiro os Fair Value Gaps (FVGs)
Para compreender plenamente a intuição por trás de um “inverse fair value gap”, é útil começar pelo que um fair value gap (FVG) padrão representa. Um fair value gap é normalmente definido dentro de um padrão de preço de três velas.

Um FVG ocorre quando o corpo da Vela B (e frequentemente os pavios) impulsiona o preço do mercado abruptamente para cima ou para baixo, de forma que um “gap” fique para trás. De forma mais concreta, se a mínima da Vela C for maior que a máxima da Vela A em um forte movimento de alta, o espaço entre esses dois pontos de preço é considerado um fair value gap. Esse gap reflete uma zona de ineficiência ou desequilíbrio no mercado — uma área onde as negociações não tiveram participação adequada dos dois lados porque o preço se moveu muito rapidamente em uma direção. Os traders frequentemente assumem que o fluxo de ordens institucionais causou esse deslocamento, deixando “pegadas” da atividade de grandes players.
A lógica comum é que o preço, em algum momento, frequentemente retorna a esses gaps para “preenchê-los”. Preencher o gap pode ser visto como a forma do mercado equilibrar o fluxo de ordens que anteriormente ficou unilateral. Traders que seguem esse princípio geralmente aguardam o preço revisitar esse gap, buscando uma reação que confirme a continuação na direção original ou, em alguns casos, uma reversão.
O que é um Inverse Fair Value Gap?
O conceito de um “inverse fair value gap” se baseia nessa ideia, mas a aborda a partir de uma perspectiva contrária ou de engenharia reversa. Em vez de usar o fair value gap como uma zona para confirmar a continuação na direção original, uma estratégia de inverse FVG pode usar esse próprio gap para antecipar onde o mercado pode falhar em dar continuidade e possivelmente reverter.
Por exemplo, para distinguir um inverse fair value gap de baixa, é possível seguir estes passos:
- Identificar um FVG de alta.
- O preço retorna à zona do FVG.
- Em vez de respeitá-lo como suporte, observar como o preço se comporta. Se ele falhar em impulsionar para cima e, em vez disso, negociar através do gap como se ele não estivesse fornecendo suporte significativo, essa falha pode sinalizar uma mudança de momentum.
- Entrar vendido, antecipando que a incapacidade de usar o FVG como um degrau para preços mais altos significa que o mercado pode agora se mover para baixo.
A Intuição por Trás dos Inverse Fair Value Gaps
- Pegadas Institucionais e Pontos de Falha: A suposição subjacente aos fair value gaps é que grandes players sofisticados criaram o desequilíbrio inicial. Quando o preço retorna a essas zonas, geralmente ocorre um teste: se os grandes players ainda enxergam valor nesses preços, suas ordens remanescentes podem sustentar ou resistir ao preço, causando uma reação. Se, em vez disso, o preço atravessa o FVG diretamente, sem um forte repique ou continuação, isso sugere que essas grandes ordens podem ter sido executadas, canceladas ou não estão mais defendendo essa zona. Isso pode indicar uma mudança na intenção do mercado.
- Detectando Fraqueza ou Força Antecipadamente: Ao focar no que não acontece quando o preço retorna ao gap, os traders podem extrair pistas sutis sobre a força ou fraqueza subjacente. Se um mercado de alta não consegue obter impulso a partir de uma zona conhecida de ineficiência (o FVG de alta), isso pode estar fornecendo um alerta precoce de que a narrativa altista pode estar perdendo força.
- Complementa Estratégias Tradicionais de FVG: As estratégias tradicionais de FVG se baseiam na suposição de um rebalanceamento seguido de continuação na direção original. No entanto, os mercados são dinâmicos, e nem todo preenchimento de gap leva à retomada da tendência anterior. A abordagem de inverse FVG pode oferecer ao trader uma “vantagem” adicional ao identificar situações em que o roteiro normal falha e, assim, um movimento contrário pode ter maior probabilidade e melhor relação risco/retorno.
O conceito de inverse fair value gaps está fundamentado no reconhecimento de que os mercados estão constantemente testando e retestando áreas de desequilíbrio anterior. Enquanto a negociação tradicional de FVG se concentra no rebalanceamento bem-sucedido e na continuação, a abordagem inversa obtém vantagem ao identificar quando esse processo de rebalanceamento falha em produzir o resultado esperado. Essa mudança de perspectiva transforma o que poderia ter sido uma oportunidade perdida — ou até mesmo uma proposição perdedora — em uma configuração contrária com potencialmente alta vantagem. Em um ambiente de mercado onde antecipar o inesperado frequentemente é recompensado, o conceito de inverse FVG adiciona uma ferramenta extra ao arsenal de técnicas de análise técnica do trader.
Desenvolvimento da Estratégia
De forma semelhante a como traders discricionários utilizam fair value gaps, os inverse fair value gaps também são negociados ativamente devido aos critérios sofisticados necessários para identificar padrões válidos. Negociar cada inverse fair value gap sem discriminação provavelmente resultaria em um desempenho aleatório, já que a maioria dos gaps não se alinha com a intuição estratégica que discuti anteriormente. Com o objetivo de quantificar as configurações de características que traders discricionários consideram, realizei testes extensivos de características e estabeleci as seguintes regras:
-
Alinhamento com a Tendência Macro: O preço deve seguir a tendência macro predominante, que é determinada por sua posição em relação à média móvel de 400 períodos.
-
Seleção Adequada de Timeframe: Um timeframe baixo, como de 1 a 5 minutos, deve ser utilizado, pois o conceito de “preenchimento de ordens” ocorre em um curto espaço de tempo. Para os fins deste artigo, é utilizado um timeframe de 3 minutos.
-
Foco no Fair Value Gap Mais Recente: Apenas o fair value gap (FVG) mais recente é considerado, pois ele possui a maior relevância para refletir as condições atuais do mercado.
-
Validação do Tamanho do Fair Value Gap: O FVG não deve ser nem grande demais nem pequeno demais em comparação com as velas ao redor. Um gap muito pequeno carece de significância para atuar como um nível confiável de suporte ou resistência, enquanto um gap muito grande provavelmente é causado por um evento de notícia, o que pode atrasar o sinal de reversão. Para garantir que o FVG seja significativo, limites específicos são definidos para validar cada gap.
-
Tamanho Controlado da Vela de Rompimento: Da mesma forma, a vela de rompimento não deve ser excessivamente grande, já que as entradas são baseadas no fechamento das velas. Velas de rompimento grandes podem levar a sinais tardios, algo que a estratégia busca evitar.
-
Reversão de Preço e Rompimento em Tempo Oportuno: Dentro de um período especificado após a formação de um FVG, o preço deve reverter de volta ao gap e romper a partir da borda oposta com uma vela fechada. Isso é alcançado ao examinar apenas o FVG mais recente dentro de um curto período de look-back.
-
Confirmação da Força do Rompimento: O FVG deve estar alinhado com um nível de rejeição anterior, garantindo que um rompimento do FVG sinalize maior força na direção correspondente.
Agora, vamos percorrer o código.
Primeiramente, declaramos as variáveis globais necessárias. Essas variáveis globais armazenam dados-chave para o rastreamento de Fair Value Gaps (FVGs), operações abertas atuais e o estado do sistema. Variáveis como previousGapHigh, previousGapLow e lastGapIndex ajudam a rastrear o gap mais recente identificado. handleMa armazenará o handle da média móvel. As variáveis buypos e sellpos rastreiam os tickets de operações abertas, enquanto currentFVGstatus e newFVGformed rastreiam a condição do último FVG identificado.
string previousGapObjName = ""; double previousGapHigh = 0.0; double previousGapLow = 0.0; int LastGapIndex = 0; double gapHigh = 0.0; double gapLow = 0.0; double gap = 0.0; double lott= 0.1; ulong buypos = 0, sellpos = 0; double anyGap = 0.0; double anyGapHigh = 0.0; double anyGapLow = 0.0; int barsTotal = 0; int newFVGformed = 0; int currentFVGstatus = 0; int handleMa; #include <Trade/Trade.mqh> CTrade trade;
Em seguida, declaramos as seguintes funções para executar negociações com take profit e stop loss, e para rastrear o ticket da ordem de cada operação.
//+------------------------------------------------------------------+ //| Store order ticket number into buypos/sellpos variables | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if (trans.type == TRADE_TRANSACTION_ORDER_ADD) { COrderInfo order; if (order.Select(trans.order)) { if (order.Magic() == Magic) { if (order.OrderType() == ORDER_TYPE_BUY) { buypos = order.Ticket(); } else if (order.OrderType() == ORDER_TYPE_SELL) { sellpos = order.Ticket(); } } } } } //+------------------------------------------------------------------+ //| Execute sell trade function | //+------------------------------------------------------------------+ void executeSell() { if (IsWithinTradingHours()){ double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); bid = NormalizeDouble(bid,_Digits); double tp = bid - tpp * _Point; tp = NormalizeDouble(tp, _Digits); double sl = bid + slp * _Point; sl = NormalizeDouble(sl, _Digits); trade.Sell(lott,_Symbol,bid,sl,tp); sellpos = trade.ResultOrder(); } } //+------------------------------------------------------------------+ //| Execute buy trade function | //+------------------------------------------------------------------+ void executeBuy() { if (IsWithinTradingHours()){ double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); ask = NormalizeDouble(ask,_Digits); double tp = ask + tpp * _Point; tp = NormalizeDouble(tp, _Digits); double sl = ask - slp * _Point; sl = NormalizeDouble(sl, _Digits); trade.Buy(lott,_Symbol,ask,sl,tp); buypos= trade.ResultOrder(); } } //+------------------------------------------------------------------+ //| Check if is trading hours | //+------------------------------------------------------------------+ bool IsWithinTradingHours() { datetime currentTime = TimeTradeServer(); MqlDateTime timeStruct; TimeToStruct(currentTime, timeStruct); int currentHour = timeStruct.hour; if (currentHour >= startHour && currentHour < endHour) return true; else return false; }
Depois, utilizamos essas duas funções para validar um fair value gap. IsReacted() verifica se, dentro do período de look-back, existem pelo menos dois pavios de velas dentro da faixa de preço do FVG atual, o que interpretamos como um sinal de rejeição prévia do FVG. IsGapValid() então verifica se o tamanho do gap está dentro da faixa desejada, retornando verdadeiro ou falso.
//+------------------------------------------------------------------+ //| Function to validate the FVG gap | //+------------------------------------------------------------------+ bool IsGapValid(){ if (anyGap<=gapMaxPoint*_Point && anyGap>=gapMinPoint*_Point&&IsReacted()) return true; else return false; } //+------------------------------------------------------------------+ //| Check for gap reaction to validate its strength | //+------------------------------------------------------------------+ bool IsReacted(){ int count1 = 0; int count2 = 0; for (int i = 4; i < lookBack; i++){ double aLow = iLow(_Symbol,PERIOD_CURRENT,i); double aHigh = iHigh(_Symbol,PERIOD_CURRENT,i); if (aHigh<anyGapHigh&&aHigh>anyGapLow&&aLow<anyGapLow){ count1++; } else if (aLow<anyGapHigh&&aLow>anyGapLow&&aHigh>anyGapHigh){ count2++; } } if (count1>=2||count2>=2) return true; else return false; }
Após isso, usamos essas funções para verificar se há atualmente um rompimento no último FVG.
//+------------------------------------------------------------------+ //| Check if price broke out to the upside of the gap | //+------------------------------------------------------------------+ bool IsBrokenUp(){ int lastClosedIndex = 1; double lastOpen = iOpen(_Symbol, PERIOD_CURRENT, lastClosedIndex); double lastClose = iClose(_Symbol, PERIOD_CURRENT, lastClosedIndex); if (lastOpen < gapHigh && lastClose > gapHigh&&(lastClose-gapHigh)<maxBreakoutPoints*_Point) { if(currentFVGstatus==-1){ return true;} } return false; } //+------------------------------------------------------------------+ //| Check if price broke out to the downside of the gap | //+------------------------------------------------------------------+ bool IsBrokenLow(){ int lastClosedIndex = 1; double lastOpen = iOpen(_Symbol, PERIOD_CURRENT, lastClosedIndex); double lastClose = iClose(_Symbol, PERIOD_CURRENT, lastClosedIndex); if (lastOpen > gapLow && lastClose < gapLow&&(gapLow -lastClose)<maxBreakoutPoints*_Point) { if(currentFVGstatus==1){ return true;} } return false; }
Por fim, utilizamos essas duas funções para verificar a validade do gap com IsGapValid() e, se for válido, atualizar as variáveis globais, marcar o FVG como novo e desenhá-lo no gráfico. A função getFVG() é essencial para a codificação de toda a estratégia. Nós a chamamos a cada nova barra para verificar se existe um FVG válido. Se o FVG for válido, verificamos se ele é diferente do último que salvamos e, em caso afirmativo, salvamos nas variáveis globais para atualizar o estado.
//+------------------------------------------------------------------+ //| To get the most recent Fair Value Gap (FVG) | //+------------------------------------------------------------------+ void getFVG() { // Loop through the bars to find the most recent FVG for (int i = 1; i < 3; i++) { datetime currentTime = iTime(_Symbol,PERIOD_CURRENT, i); datetime previousTime = iTime(_Symbol,PERIOD_CURRENT, i + 2); // Get the high and low of the current and previous bars double currentLow = iLow(_Symbol,PERIOD_CURRENT, i); double previousHigh = iHigh(_Symbol,PERIOD_CURRENT, i+2); double currentHigh = iHigh(_Symbol,PERIOD_CURRENT, i); double previousLow = iLow(_Symbol,PERIOD_CURRENT, i+2); anyGap = MathAbs(previousLow - currentHigh); // Check for an upward gap if (currentLow > previousHigh) { anyGapHigh = currentLow; anyGapLow = previousHigh; //Check for singular if (LastGapIndex != i){ if (IsGapValid()){ gapHigh = currentLow; gapLow = previousHigh; gap = anyGap; currentFVGstatus = 1;//bullish FVG DrawGap(previousTime,currentTime,gapHigh,gapLow); LastGapIndex = i; newFVGformed =1; return; } } } // Check for a downward gap else if (currentHigh < previousLow) { anyGapHigh = previousLow; anyGapLow = currentHigh; if (LastGapIndex != i){ if(IsGapValid()){ gapHigh = previousLow; gapLow = currentHigh; gap = anyGap; currentFVGstatus = -1; DrawGap(previousTime,currentTime,gapHigh,gapLow); LastGapIndex = i; newFVGformed =1; return; } } } } } //+------------------------------------------------------------------+ //| Function to draw the FVG gap on the chart | //+------------------------------------------------------------------+ void DrawGap(datetime timeStart, datetime timeEnd, double gaphigh, double gaplow) { // Delete the previous gap object if it exists if (previousGapObjName != "") { ObjectDelete(0, previousGapObjName); } // Generate a new name for the gap object previousGapObjName = "FVG_" + IntegerToString(TimeCurrent()); // Create a rectangle object to highlight the gap ObjectCreate(0, previousGapObjName, OBJ_RECTANGLE, 0, timeStart, gaphigh, timeEnd, gaplow); // Set the properties of the rectangle ObjectSetInteger(0, previousGapObjName, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, previousGapObjName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, previousGapObjName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, previousGapObjName, OBJPROP_RAY, false); // Update the previous gap information previousGapHigh = gaphigh; previousGapLow = gaplow; }
E integramos todas as regras da estratégia juntas na função OnTick() desta forma, e finalizamos.
//+------------------------------------------------------------------+ //| OnTick function | //+------------------------------------------------------------------+ void OnTick() { int bars = iBars(_Symbol,PERIOD_CURRENT); if (barsTotal!= bars){ barsTotal = bars; double ma[]; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); CopyBuffer(handleMa,BASE_LINE,1,1,ma); if (IsBrokenLow()&&sellpos == buypos&&newFVGformed ==1&&bid<ma[0]){ executeSell(); newFVGformed =0; } else if (IsBrokenUp()&&sellpos == buypos&&newFVGformed ==1&&ask>ma[0]){ executeBuy(); newFVGformed =0; } getFVG(); if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; } } }
Algumas observações adicionais: chamamos isso no início da função OnTick() para que ela processe o restante das linhas somente após a formação de uma nova barra. Essa medida economiza poder computacional.
int bars = iBars(_Symbol,PERIOD_CURRENT); if (barsTotal!= bars){ barsTotal = bars;
Além disso, como queremos apenas uma operação por vez, só podemos entrar em uma negociação quando ambos os tickets estiverem definidos como 0 com esta lógica, que verifica se não há posições atualmente abertas por este EA específico.
if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; }
Resumo rápido:
- Declarações Globais & Entradas: Configuração do ambiente, variáveis e parâmetros configuráveis pelo usuário.
- Inicialização (OnInit): Preparação do filtro de média móvel e definição dos magic numbers.
- Lógica do OnTick: O fluxo principal — verifica novas barras, detecta FVGs, verifica rompimentos e executa operações se as condições forem atendidas.
- Detecção de FVG (getFVG, IsGapValid, IsReacted): Identifica e valida Fair Value Gaps e suas reações no mercado.
- Verificações de Rompimento (IsBrokenUp, IsBrokenLow): Confirma a direção do rompimento para entradas em operações.
- Gerenciamento de Operações (OnTradeTransaction, executeBuy, executeSell): Gerencia os tickets das ordens e garante que as operações sejam executadas corretamente.
- Gráficos (DrawGap): Visualiza os FVGs identificados.
- Filtro de Tempo (IsWithinTradingHours): Restringe a negociação a horários específicos.
Teste da Estratégia
A estratégia funciona melhor em índices de ações devido aos seus spreads relativamente baixos e alta volatilidade, que são benéficos para o trading intradiário de varejo. Testaremos esta estratégia negociando o índice Nasdaq 100 de 1º de janeiro de 2020 a 1º de dezembro de 2024, no timeframe de 3 minutos (M3). Aqui estão os parâmetros que escolhi para esta estratégia.

Aqui estão algumas recomendações para escolher os valores dos parâmetros da estratégia:
- Defina o horário de negociação durante períodos de alta volatilidade do mercado, normalmente quando o mercado de ações está aberto. Esse horário depende do tempo do servidor da sua corretora. Por exemplo, com o meu tempo de servidor (GMT+0), o mercado de ações fica aberto aproximadamente das 14:00 às 19:00.
- Recomenda-se uma relação de recompensa para risco maior que um, pois estamos seguindo a tendência macro em um mercado altamente volátil. Além disso, evite definir níveis de take profit e stop loss (TPSL) muito altos ou muito baixos. Se o TPSL for muito grande, ele não capturará efetivamente os sinais de padrões de curto prazo, e se for muito pequeno, os spreads podem impactar negativamente a operação.
- Não ajuste excessivamente os valores dos limites de gap, limites da vela de rompimento e o período de look-back. Mantenha esses parâmetros dentro de uma faixa razoável em relação à variação de preço do ativo negociado para evitar overfitting.
Agora, aqui está o resultado do backtest:



Podemos ver que a estratégia apresentou um desempenho muito consistente ao longo dos últimos cinco anos, indicando seu potencial de lucratividade.
Uma operação típica na parte de visualização do testador de estratégia seria assim:

Encorajo os leitores a construir sobre este framework de estratégia e adicionar sua criatividade para aprimorá-lo. Aqui estão algumas das minhas sugestões:
- A força do IFVG é determinada pelo número de velas de rejeição ao redor da área do FVG. Você pode usar a diferença nesses números como uma regra para avaliação.
- Neste artigo, focamos apenas nos pontos máximos de rompimento. No entanto, às vezes a vela de rompimento pode ser muito pequena, indicando força fraca de rompimento, o que pode afetar negativamente a continuação da tendência. Você pode considerar adicionar também um limite para os pontos mínimos de rompimento.
- A regra de saída é definida por take profit e stop loss. Alternativamente, você pode definir o nível de saída com base em níveis-chave relevantes para ambas as direções dentro de um determinado período de look-back, ou estabelecer um tempo fixo de saída.
Conclusão
Neste artigo, apresentei minha abordagem desenvolvida por mim para quantificar e utilizar inverse fair value gaps como uma estratégia para expert advisors do MetaTrader 5, cobrindo a motivação, o desenvolvimento e os testes da estratégia. Essa estratégia demonstra alto potencial de lucratividade, tendo apresentado desempenho consistente ao longo dos últimos cinco anos com mais de 400 operações. Outras modificações podem ser realizadas para adaptar essa estratégia a diferentes ativos e timeframes. O código completo está anexado abaixo, e você é bem-vindo a integrá-lo aos seus próprios desenvolvimentos de trading.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16659
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Mecanismos de gating em aprendizado por ensemble
Dominando Registros de Log (Parte 4): Salvando logs em arquivos
Integrar seu próprio LLM em EA (Parte 5): Desenvolver e testar estratégia de trading com LLMs (IV) — Testar estratégia de trading
Construindo Expert Advisors Auto-Otimizáveis em MQL5 (Parte 4): Dimensionamento Dinâmico de Posição
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso