English Русский 中文 Español Deutsch 日本語
preview
Aprenda a operar a Fair Value Gap (FVG)/Imbalances passo a passo: Uma abordagem do conceito de Smart Money

Aprenda a operar a Fair Value Gap (FVG)/Imbalances passo a passo: Uma abordagem do conceito de Smart Money

MetaTrader 5Negociação |
751 21
Allan Munene Mutiiria
Allan Munene Mutiiria

Introdução

Neste artigo, discutiremos as etapas básicas para a criação e desenvolvimento de um expert advisor (EA), com base na estratégia de Fair Value Gap (FVG)/imbalance e na abordagem do conceito de Smart Money. Essencialmente, a jornada para criar um expert advisor baseado na estratégia Fair Value Gap representa uma convergência de arte e ciência, normalmente exigindo que o trader seja capaz não apenas de analisar os candles, mas também de visualizar os níveis conceituais. Junte-se a nós enquanto desvendamos os mistérios do Smart Money e embarcamos em uma jornada para explorar seu poder transformador no campo da negociação algorítmica. Desenvolveremos o Expert Advisor com Fair Value Gap através dos seguintes tópicos:

  1. Definição de Imbalance
  2. Descrição da estratégia de negociação
  3. Esboço da estratégia de negociação
  4. Sistema de negociação em MQL5
  5. Resultados do Strategy Tester
  6. Conclusão


Nesta jornada, utilizaremos amplamente o MetaQuotes Language 5 (MQL5) como nosso ambiente base de codificação em IDE, e executaremos os arquivos no terminal de negociação MetaTrader 5 (MT5). Portanto, será de grande importância possuir as versões mencionadas. Vamos começar então.


Definição de Fair Value Gap (FVG)/Imbalance

O Fair Value Gap descreve a diferença entre os desequilíbrios causados por pressões de compra e venda. Normalmente, eles ocorrem quando há alta volatilidade que afasta completamente os ursos ou os touros do mercado, e assim surgem pressões desiguais no mercado. Isso é tipicamente caracterizado por movimentos unidirecionais massivos no mercado. Geralmente, você notará candles longos no gráfico. Com esse conhecimento, podemos empregar várias abordagens para explorar o Fair Value Gap e potencialmente criar uma estratégia de negociação.

Descrição da estratégia de negociação de Fair Value Gap (FVG)

A estratégia de negociação de Fair Value Gap integra o conceito de avaliação de valor justo com desequilíbrios de candle para identificar possíveis oportunidades de negociação.

Aqui está uma descrição detalhada da estratégia de negociação de Fair Value Gap incorporando desequilíbrios de candles:

  • Os traders conduzem uma análise fundamental abrangente para determinar os Fair Value Gaps. Isso é alcançado analisando a ação do preço no mercado usando padrões de candle para identificar desequilíbrios entre as pressões de compra e venda. Padrões de candle, como engolfo de alta, engolfo de baixa e doji, oferecem insights sobre o sentimento do mercado e possíveis mudanças de momento. Os traders procuram instâncias em que o preço de mercado se desvia significativamente do valor justo estimado, enquanto também observam desequilíbrios notáveis nos candles. Uma grande lacuna entre o preço de fechamento da sessão anterior e o preço de abertura da sessão atual, juntamente com fortes padrões de candle de alta ou baixa, pode indicar um possível Fair Value Gap. A parte mais difícil da abordagem de Fair Value Gap, como outros padrões de gráfico, é identificar essa configuração especial em um gráfico de preços. FVGs requerem a aparição de um design de três candles com diretrizes específicas. Quando isso ocorre, o Fair Value Gap é a área ou distância entre os pavios do primeiro e terceiro candles.

A seguir está como identificar um FVG no gráfico:

  • Encontrando o Grande Candle: A primeira coisa a fazer ao tentar identificar um Fair Value Gap é procurar em seu gráfico de preços um grande candle. Deve haver uma relação perceptível entre o corpo e o pavio deste candle—idealmente, 70%.
  • Examinando os Candles Adjacentes: Depois de localizar o grande candle, examine os que estão imediatamente à frente e atrás dele. O candle importante não deve sobrepor completamente esses candles adjacentes. Em vez disso, pode haver leves sobreposições nas partes superior e inferior do candle significativo. A Fair Value Gap é então causada pelo espaço entre os pavios dos candles adjacentes.
  • Determinando a Diferença de Valor Justo: O último passo é definir e representar o Fair Value Gap no seu gráfico de preços. A faixa de preço entre a máxima e a mínima do candle anterior em uma tendência de baixa é conhecida como Fair Value Gap. É aqui que o desequilíbrio do mercado se manifesta e pode apresentar uma oportunidade de negociação. Da mesma forma, uma tendência de alta também se aplica, mas com condições opostas.



  • Sinais de Entrada e Saída: Ao identificar um Fair Value Gap acompanhado por desequilíbrios significativos de candle, os traders executam negociações de acordo. Por exemplo, se o preço de mercado estiver abaixo do valor justo estimado e surgir um padrão de engolfo de alta, os traders podem considerar entrar em uma posição comprada, antecipando uma correção de preço. Por outro lado, se o preço de mercado estiver acima do valor justo e aparecer um padrão de engolfo de baixa, os traders podem iniciar uma posição vendida.
  • Gestão de Risco: Os traders implementam técnicas de gestão de risco, como definir ordens de stop-loss e dimensionar posições para mitigar possíveis perdas, caso a negociação não ocorra conforme o esperado.
  • Monitoramento e Ajuste: Os traders monitoram continuamente a negociação, ajustando suas posições com base nas condições de mercado em evolução e na reavaliação das estimativas de valor justo.

Como você já deve ter notado, existem dois tipos diferentes de Fair Value Gaps, cada um com implicações únicas para os traders. Uma descrição detalhada está abaixo:

  • Fair Value Gap de baixa, ou Fair Value Gap Subvalorizado

Esse tipo de FVG indica que o preço de um par de moedas, ou de qualquer outro ativo financeiro, está atualmente abaixo de seu valor justo. Simplificando, os traders podem esperar um retrocesso de mercado para corrigir essa ineficiência. Se você vir um grande candle de baixa no seu gráfico, provavelmente indica que há um FVG subvalorizado.

  • Fair Value Gap de alta, ou Fair Value Gap Supervalorizado

Por outro lado, um FVG supervalorizado mostra que um ativo financeiro ou par de moedas está atualmente sendo negociado acima de seu valor justo. O mercado está superaquecido aqui, e uma correção é iminente. Antes de subir, os traders devem antecipar um retrocesso de preço enquanto o mercado se corrige.


Esboço da estratégia de trading Fair Value Gap (FVG)

Após definir e descrever a estratégia Fair Value Gap, vamos agora definir, passo a passo, as condições designadas que precisam ser consideradas para operar a estratégia FVG. Lembre-se de que dois tipos de FVG podem se formar no mercado.

  • FVG de alta: De acordo com a estratégia, precisamos encontrar uma vela de alta com grande movimento de preço e, em seguida, avaliar as velas vizinhas—à esquerda e à direita. Se isso for verdade, prosseguimos para obter a diferença entre a terceira vela e a primeira vela em uma série temporal e, se a diferença não estiver dentro dos pontos pré-definidos restritos, teremos um FVG de alta que iremos operar conforme o planejado. O FVG se torna nosso ponto de interesse e o documentamos no algoritmo. Desenhamos simultaneamente o FVG, colorido em verde/lima no gráfico, com um comprimento pré-definido para fins de visualização, sinalizando que encontramos uma configuração FVG de alta e que estamos prontos para operar essa configuração. Agora, se o preço voltar e tocar a área inferior do FVG, enviamos uma ordem de compra instantânea no mercado. O take profit será definido na parte superior do FVG, enquanto o stop loss será definido abaixo do preço de abertura da ordem, com uma relação risco-recompensa de 1:3. No entanto, se o preço não retornar à configuração FVG após o comprimento pré-definido, simplesmente a retiramos da nossa consideração.


O mesmo processo continua para todas as outras configurações FVG de alta.

  • FVG de baixa: Novamente, aqui, precisamos encontrar uma vela de baixa com grande movimento de preço e, em seguida, avaliar as velas vizinhas—à esquerda e à direita. Se isso for verdade, prosseguimos para obter a diferença entre a terceira vela e a primeira vela em uma série temporal e, se a diferença não estiver dentro dos pontos pré-definidos restritos, teremos um FVG de baixa que iremos operar conforme o planejado. O FVG se torna nosso ponto de interesse e o documentamos no algoritmo. Desenhamos simultaneamente o FVG, colorido em vermelho/tomate no gráfico, com um comprimento pré-definido para fins de visualização, sinalizando que encontramos uma configuração FVG de baixa e que estamos prontos para operar essa configuração. Agora, se o preço voltar e tocar a área superior do FVG, enviamos uma ordem de venda instantânea no mercado. O take profit será definido na parte inferior do FVG, enquanto o stop loss será definido acima do preço de abertura da ordem, com uma relação risco-recompensa de 1:10. No entanto, se o preço não retornar à configuração FVG após o comprimento pré-definido, simplesmente a retiramos da nossa consideração.


O mesmo processo continua para todas as outras configurações FVG de baixa.


Sistema de trading Fair Value Gap (FVG) em MQL5

Após aprender toda a teoria sobre a estratégia de trading FVG, vamos então automatizar a teoria e criar um Expert Advisor (EA) em MQL5 para MetaTrader 5. 

Para criar um EA, no seu terminal MetaTrader 5, clique na aba Ferramentas e selecione MetaQuotes Language Editor, ou simplesmente pressione F4 no seu teclado. Isso abrirá o ambiente MetaQuotes Language Editor, que permite escrever robôs de trading, indicadores técnicos, scripts e bibliotecas de funções.


Agora clique em Novo, selecione Expert Advisor (modelo) e clique em Próximo.


ABRIR NOVO ARQUIVO

Em seguida, forneça o nome desejado para o arquivo do expert advisor, clique em Próximo, clique em Próximo novamente e depois clique em Concluir. Depois de fazer tudo isso, estamos prontos para codificar e programar nossa estratégia FVG.

Primeiro, incluímos uma instância de trade usando #include no início do código-fonte. Isso nos dá acesso à classe CTrade, que usaremos para criar um objeto de trade.

#include <Trade/Trade.mqh>
CTrade obj_Trade;

Novamente, usamos #define para definir uma variável que atribuiremos como um prefixo a todos os retângulos FVG criados e suas cores.

#define FVG_Prefix "FVG REC "
#define CLR_UP clrLime
#define CLR_DOWN clrRed

Abaixo está a ilustração da importância dos parâmetros mencionados anteriormente.


Precisaremos pré-definir algumas outras variáveis para facilitar ainda mais a codificação do EA. Esses são os pontos mínimos para que uma barra de desequilíbrio seja considerada viável, e o comprimento do retângulo desenhado será, neste caso, a extensão das barras a partir da barra de desequilíbrio central. Definiremos essas variáveis como variáveis globais.

int minPts = 100;
int FVG_Rec_Ext_Bars = 10;

Abaixo está a ilustração. Definimos minPts para os pontos mínimos e FVG_Rec_Bars para o intervalo em barras no qual o comprimento do retângulo será representado.


Por fim, definimos quatro arrays, de variáveis do tipo string, integer, datetime e boolean, que armazenarão nossos dados usados na criação do EA. Essas também serão globais.

string totalFVGs[];
int barINDICES[];
datetime barTIMEs[];
bool signalFVGs[];

Na seção OnInit, buscamos as configurações FVG apenas para as barras visíveis no gráfico. Isso dará ao nosso EA um toque mais semelhante a um indicador. Assim, se o usuário iniciar o gráfico, ele ainda poderá visualizar as configurações FVG anteriores enquanto aguarda a criação de mais nas velas subsequentes. 

Primeiro, obtemos todas as barras visíveis no gráfico e informamos sua contagem.

   int visibleBars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   Print("Total visible bars on chart = ",visibleBars);

Se não houver objetos de retângulo no gráfico, liberamos e redimensionamos os arrays de armazenamento para zero, preparando-os para novos dados. Este é um passo muito importante, pois o usuário pode ter deletado todos os objetos no gráfico antes de inicializar o EA novamente.

   if (ObjectsTotal(0,0,OBJ_RECTANGLE)==0){
      Print("No FVGs Found, Resizing storage arrays to 0 now!!!");
      ArrayResize(totalFVGs,0);
      ArrayResize(barINDICES,0);
      ArrayResize(signalFVGs,0);
   }

Para evitar sobreposição de objetos, eliminamos novamente todas as configurações FVG anteriores e criamos novas com base nas propriedades atuais do ambiente do gráfico aplicado. Isso é feito utilizando o prefixo das nossas configurações FVG, o que garante que apenas excluímos os objetos de retângulo FVG criados pelo nosso EA, para que ele seja compatível com outros EAs.

   ObjectsDeleteAll(0,FVG_Prefix);

Em seguida, percorreremos todas as barras visíveis no gráfico, obteremos as propriedades das barras e verificaremos se elas atendem aos critérios para serem uma configuração FVG válida.

Aqui está uma descrição passo a passo:

Para buscar uma configuração de FVG de alta, obtemos o valor mínimo da primeira barra, no índice i, tecnicamente esta é nossa barra número 0, assumindo que já temos uma configuração de apenas 3 barras, obtemos o valor máximo da terceira barra, no índice i+2, e calculamos a diferença de gap entre elas em forma de pontos.

      double low0 = iLow(_Symbol,_Period,i);
      double high2 = iHigh(_Symbol,_Period,i+2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);

A mesma lógica se aplica ao procurar por configurações de baixa Para procurar uma configuração de FVG de baixa, obtemos a máxima da primeira barra, no índice i, tecnicamente esta é a nossa barra número 0, se assumíssemos que já temos uma configuração de apenas 3 barras, obtemos a mínima da terceira barra, no índice i+2, e obtemos a diferença de gap em forma de pontos.

      double high0 = iHigh(_Symbol,_Period,i);
      double low2 = iLow(_Symbol,_Period,i+2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);

Depois de obter a diferença de pontos, agora podemos usar os valores retornados para verificar se existe alguma das configurações FVG. Para fazer isso, usamos duas variáveis booleanas: 

  1. FVG_UP - Verifica se o valor mínimo da barra no índice i é maior que o valor máximo da barra no índice i+2 e, ao mesmo tempo, se os pontos de gap computados são maiores que o número mínimo de pontos permitidos para o retângulo.

  2. FVG_DOWN - Verifica se o valor máximo da barra no índice i é menor que o valor mínimo da barra no índice i+2 e, ao mesmo tempo, se os pontos de gap computados são maiores que o número mínimo de pontos permitidos para o retângulo.

A verificação dos pontos mínimos garante que temos apenas configurações FVG válidas e significativas, causadas por possíveis movimentos de preço espontâneos, e não poluímos o gráfico.

      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;

Uma vez que temos uma configuração FVG confirmada, prosseguimos para criar a respectiva configuração FVG, junto com a documentação dos dados nos arrays de armazenamento.

Definimos a variável time1 do tipo datetime, onde armazenamos o tempo da barra central, que é a barra do meio ou a segunda barra, no índice i+1, onde o retângulo começará.

Novamente, definimos a variável price1 do tipo double, onde usamos o operador ternário para retornar o valor máximo da terceira barra no caso de uma configuração FVG de alta, ou o valor máximo da primeira barra no caso de uma configuração FVG de baixa.

Tecnicamente, isso serve como as coordenadas do primeiro ponto do nosso objeto de retângulo a ser desenhado.

         datetime time1 = iTime(_Symbol,_Period,i+1);
         double price1 = FVG_UP ? high2 : high0;

Depois de definir as primeiras coordenadas, definimos as segundas coordenadas. 

Definimos a variável time2 do tipo datetime, onde armazenamos o tempo da barra final, que é a barra onde o retângulo desenhado termina. Isso é feito simplesmente somando ao time1, o tempo inicial, o número de barras para estender o retângulo.

Novamente, definimos a variável price2 do tipo double, onde usamos o operador ternário para retornar o valor mínimo da primeira barra no caso de uma configuração FVG de alta, ou o valor mínimo da terceira barra no caso de uma configuração FVG de baixa.

         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;

Abaixo está uma visualização das coordenadas necessárias para garantir a criação suave dos retângulos.


Após obter as coordenadas da configuração FVG, precisamos dar um nome ao objeto de retângulo FVG.

Usamos o prefixo pré-definido como prefixo do nosso FVG e adicionamos o horário de criação a ele. Isso garante, em primeiro lugar, que, uma vez que criamos um objeto FVG naquele horário de barra, não podemos criá-lo novamente, pois naquele momento específico da vela, um objeto de título semelhante já existe, e, em segundo lugar, há unicidade, pois com certeza não pode haver outro horário de barra semelhante.

         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";

Também precisamos atribuir cores diferentes às nossas configurações FVG para que possamos distinguir entre configurações de alta e de baixa. As configurações de alta são atribuídas à cor de alta pré-definida, ou seja, CLR_UP, e as configurações de baixa à cor de baixa pré-definida, ou seja, CLR_DOWN.

         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;

Até este ponto, temos tudo o que precisamos para desenhar a respectiva configuração FVG no gráfico. 

Para facilitar isso, prosseguimos e criamos uma função void no escopo global. Definimos a função como CreateRec, e passamos o nome do objeto, as coordenadas do ponto um e ponto dois respectivamente, e a cor, variáveis necessárias na criação das configurações FVG.

void CreateRec(string objName,datetime time1,double price1,
               datetime time2, double price2,color clr){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_RECTANGLE,0,time1,price1,time2,price2);
      
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FILL,true);
      ObjectSetInteger(0,objName,OBJPROP_BACK,false);
      
      ChartRedraw(0);
   }
}

Após criar a função, usaremos ela para criar o respectivo retângulo FVG no gráfico, passando os parâmetros previamente criados.

         CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);

Depois de criar a configuração FVG, precisamos armazenar os dados nos arrays de armazenamento. Como criamos arrays dinâmicos, primeiramente redimensionamos os arrays para incorporar um valor de dado extra, usando a função ArrayResize.

         ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
         ArrayResize(barINDICES,ArraySize(barINDICES)+1);

Após redimensionar os arrays de armazenamento, prosseguimos para adicionar os novos dados ao array, respectivamente.

         totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
         barINDICES[ArraySize(barINDICES)-1] = i+1;

Até este ponto, tivemos sucesso na criação de configurações FVG para todas as barras visíveis no gráfico. Abaixo está o resultado intermediário.


O usuário pode desejar que as configurações que foram confirmadas sejam encurtadas assim que forem confirmadas, como uma forma de indicar que houve uma configuração FVG bem-sucedida. Portanto, precisamos truncar o comprimento extra das configurações uma vez que o preço as confirma. Fazemos isso percorrendo todas as configurações FVG criadas e confirmadas, obtendo os respectivos detalhes, verificando-os contra a lógica de controle e, finalmente, atualizando as configurações de acordo.

Aqui está um procedimento passo a passo de como isso é realizado:

Percorra todas as configurações FVG criadas usando a função de loop for.

   for (int i=ArraySize(totalFVGs)-1; i>=0; i--){// ... }

Obtenha/recupere os dados da configuração.

      string objName = totalFVGs[i];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      int barIndex = barINDICES[i];
      datetime timeSTART = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,0);
      datetime timeEND = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,1);
      double fvgLOW = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHIGH = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);

Percorra todas as barras de extensão do retângulo, ou seja, o comprimento do retângulo selecionado.

      for (int k=barIndex-1; k>=(barIndex-FVG_Rec_Ext_Bars); k--){//... }

Obtenha os dados da barra.

         datetime barTime = iTime(_Symbol,_Period,k);
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);

No caso de o retângulo se estender além da primeira barra visível em uma série temporal, simplesmente trunque-o para o tempo da primeira barra, ou seja, a barra no índice 0, ou simplesmente a barra atual. Informe sobre a detecção de estouro, atualize a configuração FVG para o tempo da barra atual e interrompa o loop de operação.

         if (k==0){
            Print("OverFlow Detected @ fvg ",fvgNAME);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }

Para facilitar a atualização das configurações, criamos uma função void chamada UpdateRec, passando o nome do objeto e as coordenadas dos dois pontos vitais do objeto retângulo a serem atualizados.

void UpdateRec(string objName,datetime time1,double price1,
               datetime time2, double price2){
   if (ObjectFind(0,objName) >= 0){
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      
      ChartRedraw(0);
   }
}

No caso de a configuração FVG ser uma FVG de baixa, detectada pela cor, e o preço máximo da barra selecionada estar acima das coordenadas superiores do objeto FVG, isso sinaliza que a FVG foi um sucesso e, portanto, é truncada de acordo, ou seja, atualiza as coordenadas do segundo ponto para a vela onde ocorre a ruptura da FVG. Da mesma forma, vale para a configuração FVG de alta.

         if ((fvgColor == CLR_DOWN && barHigh > fvgHIGH) ||
            (fvgColor == CLR_UP && barLow < fvgLOW)
         ){
            Print("Cut Off @ bar no: ",k," of Time: ",barTime);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }


Uma vez que tudo está configurado, redimensionamos os arrays de armazenamento para zero, para nos prepararmos para a seção OnTick.

   ArrayResize(totalFVGs,0);
   ArrayResize(barINDICES,0);

O código completo OnInit responsável pela criação das configurações FVG nas barras visíveis no gráfico e pela sua atualização será o seguinte:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   int visibleBars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   Print("Total visible bars on chart = ",visibleBars);
   
   if (ObjectsTotal(0,0,OBJ_RECTANGLE)==0){
      Print("No FVGs Found, Resizing storage arrays to 0 now!!!");
      ArrayResize(totalFVGs,0);
      ArrayResize(barINDICES,0);
      ArrayResize(signalFVGs,0);
   }
   
   ObjectsDeleteAll(0,FVG_Prefix);
   
   for (int i=0; i<=visibleBars; i++){
      //Print("Bar Index = ",i);
      double low0 = iLow(_Symbol,_Period,i);
      double high2 = iHigh(_Symbol,_Period,i+2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);
      
      double high0 = iHigh(_Symbol,_Period,i);
      double low2 = iLow(_Symbol,_Period,i+2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);
      
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;
      
      if (FVG_UP || FVG_DOWN){
         Print("Bar Index with FVG = ",i+1);
         datetime time1 = iTime(_Symbol,_Period,i+1);
         double price1 = FVG_UP ? high2 : high0;
         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;
         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;
         CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);
         Print("Old ArraySize = ",ArraySize(totalFVGs));
         ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
         ArrayResize(barINDICES,ArraySize(barINDICES)+1);
         Print("New ArraySize = ",ArraySize(totalFVGs));
         totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
         barINDICES[ArraySize(barINDICES)-1] = i+1;
         ArrayPrint(totalFVGs);
         ArrayPrint(barINDICES);
      }
   }
   
   for (int i=ArraySize(totalFVGs)-1; i>=0; i--){
      string objName = totalFVGs[i];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      int barIndex = barINDICES[i];
      datetime timeSTART = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,0);
      datetime timeEND = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,1);
      double fvgLOW = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHIGH = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);
      
      Print("FVG NAME = ",fvgNAME," >No: ",barIndex," TS: ",timeSTART," TE: ",
            timeEND," LOW: ",fvgLOW," HIGH: ",fvgHIGH," CLR = ",fvgColor);
      for (int k=barIndex-1; k>=(barIndex-FVG_Rec_Ext_Bars); k--){
         datetime barTime = iTime(_Symbol,_Period,k);
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         //Print("Bar No: ",k," >Time: ",barTime," >H: ",barHigh," >L: ",barLow);
         
         if (k==0){
            Print("OverFlow Detected @ fvg ",fvgNAME);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }
         
         if ((fvgColor == CLR_DOWN && barHigh > fvgHIGH) ||
            (fvgColor == CLR_UP && barLow < fvgLOW)
         ){
            Print("Cut Off @ bar no: ",k," of Time: ",barTime);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }
      }
      
   }
   
   ArrayResize(totalFVGs,0);
   ArrayResize(barINDICES,0);

   return(INIT_SUCCEEDED);
}

Na seção OnTick, funções e lógica semelhantes são usadas. Percorremos todas as barras de extensão pré-definidas, ou seja, o comprimento da configuração FVG a partir da barra atual, procuramos por configurações potenciais e as criamos se alguma for encontrada e confirmada. 

Aqui estão as pequenas diferenças no loop de criação usado.

No loop de criação da configuração FVG no OnInit, consideramos todas as barras visíveis no gráfico. Aqui, ao contrário, consideramos apenas o último comprimento de extensão pré-definido.

   for (int i=0; i<=FVG_Rec_Ext_Bars; i++){//... }

Ao obter informações para as barras, começamos na barra anterior ao índice da barra atual, pois a barra ainda está no processo de formação. Portanto, adicionamos 1 ao índice selecionado.

      double low0 = iLow(_Symbol,_Period,i+1);
      double high2 = iHigh(_Symbol,_Period,i+2+1);

No loop de truncamento FVG do OnInit, truncamos as configurações FVG confirmadas e testadas. Na seção OnTick, não truncamos as configurações porque queremos visualizá-las. Em vez disso, enviamos ordens de mercado instantâneas, respectivamente. Além disso, uma vez que o preço está além do comprimento da barra, isso significa que não podemos operar essa configuração de forma alguma, então eliminamos os dados armazenados nos arrays. Aqui estão as diferenças no código:

Adicionamos uma nova variável booleana fvgExist e a inicializamos como false, para que possa sinalizar a disponibilidade de uma configuração dentro das barras escaneadas.

      bool fvgExist = false;

Novamente, percorremos as barras pré-definidas a partir da barra anterior à barra atual, ou seja, 0 + 1 = 1, obtendo os preços máximos e mínimos da barra selecionada e, se algum dos preços coincidir com as coordenadas do segundo ponto da configuração, ainda está dentro do intervalo, e portanto, pode ser negociado, e então definimos a variável fvgExist como true.

      for (int k=1; k<=FVG_Rec_Ext_Bars; k++){
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         
         if (barHigh == fvgLow || barLow == fvgLow){
            //Print("Found: ",fvgNAME," @ bar ",k);
            fvgExist = true;
            break;
         }
      }

Como queremos infiltrar o mercado, e isso exigirá os níveis de preço de entrada, definimos as cotações do símbolo atual logo no início.

      double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
      double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

Utilize declarações if posteriormente para verificar se a configuração FVG selecionada é uma configuração de baixa, se o preço Bid atual está acima das coordenadas superiores da configuração e, ao mesmo tempo, se este é o primeiro sinal de trade para a configuração. Quando todas as condições forem atendidas, abra uma ordem de venda, com o volume de trade de 0,01. O preço de abertura será o preço Bid atual do mercado, com o nível de take profit nas coordenadas inferiores da configuração FVG e stop loss com uma relação risco-recompensa de 1:10, acima do preço de entrada. Assim que a posição for iniciada, definimos os dados do sinal naquele índice específico como verdadeiro, para que não abramos nenhuma outra posição com base naquela configuração em particular no próximo tick.

      if (fvgColor == CLR_DOWN && Bid > fvgHigh && !signalFVGs[j]){
         Print("SELL SIGNAL For (",fvgNAME,") Now @ ",Bid);
         double SL_sell = Ask + NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);
         
         if (Check2_Margin(ORDER_TYPE_SELL,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_SELL,SL_sell,fvgLow)){
            obj_Trade.Sell(trade_lots,_Symbol,Bid,SL_sell,fvgLow);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }


Da mesma forma, uma confirmação de alta é válida, mas com condições opostas.

      else if (fvgColor == CLR_UP && Ask < fvgLow && !signalFVGs[j]){
         Print("BUY SIGNAL For (",fvgNAME,") Now @ ",Ask);
         double SL_buy = Bid - NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);

         if (Check2_Margin(ORDER_TYPE_BUY,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_BUY,SL_buy,fvgHigh)){
            obj_Trade.Buy(trade_lots,_Symbol,Ask,SL_buy,fvgHigh);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }


Finalmente, uma vez que a configuração FVG não existe mais, isso significa que não podemos mais negociá-la, e assim ela se torna irrelevante. Portanto, liberamos os arrays de armazenamento eliminando os dados correspondentes. Isso é realizado usando a função ArrayRemove, passando o array, a posição inicial, neste caso 0 para o primeiro dado, e o número total de elementos, neste caso 1, já que queremos remover apenas os dados daquela configuração FVG selecionada.

      if (fvgExist == false){
         bool removeName = ArrayRemove(totalFVGs,0,1);
         bool removeTime = ArrayRemove(barTIMEs,0,1);
         bool removeSignal = ArrayRemove(signalFVGs,0,1);
         if (removeName && removeTime && removeSignal){
            Print("Success removing the FVG DATA from the arrays. New Data as Below:");
            Print("FVGs: ",ArraySize(totalFVGs)," TIMEs: ",ArraySize(barTIMEs),
                     " SIGNALs: ",ArraySize(signalFVGs));
            ArrayPrint(totalFVGs);
            ArrayPrint(barTIMEs);
            ArrayPrint(signalFVGs);
         }
      }


O seguinte é o código OnTick necessário para criar as configurações FVG, negociar as configurações confirmadas e remover os dados fora dos limites dos arrays de armazenamento:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   for (int i=0; i<=FVG_Rec_Ext_Bars; i++){
      double low0 = iLow(_Symbol,_Period,i+1);
      double high2 = iHigh(_Symbol,_Period,i+2+1);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);
      
      double high0 = iHigh(_Symbol,_Period,i+1);
      double low2 = iLow(_Symbol,_Period,i+2+1);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);
      
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;
      
      if (FVG_UP || FVG_DOWN){
         datetime time1 = iTime(_Symbol,_Period,i+1+1);
         double price1 = FVG_UP ? high2 : high0;
         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;
         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;
         
         if (ObjectFind(0,fvgNAME) < 0){
            CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);
            Print("Old ArraySize = ",ArraySize(totalFVGs));
            ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
            ArrayResize(barTIMEs,ArraySize(barTIMEs)+1);
            ArrayResize(signalFVGs,ArraySize(signalFVGs)+1);
            Print("New ArraySize = ",ArraySize(totalFVGs));
            totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
            barTIMEs[ArraySize(barTIMEs)-1] = time1;
            signalFVGs[ArraySize(signalFVGs)-1] = false;
            ArrayPrint(totalFVGs);
            ArrayPrint(barTIMEs);
            ArrayPrint(signalFVGs);
         }
      }
   }
   
   for (int j=ArraySize(totalFVGs)-1; j>=0; j--){
      bool fvgExist = false;
      string objName = totalFVGs[j];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      double fvgLow = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHigh = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);
      
      for (int k=1; k<=FVG_Rec_Ext_Bars; k++){
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         
         if (barHigh == fvgLow || barLow == fvgLow){
            //Print("Found: ",fvgNAME," @ bar ",k);
            fvgExist = true;
            break;
         }
      }
      
      //Print("Existence of ",fvgNAME," = ",fvgExist);
      
      double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
      double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
      
      if (fvgColor == CLR_DOWN && Bid > fvgHigh && !signalFVGs[j]){
         Print("SELL SIGNAL For (",fvgNAME,") Now @ ",Bid);
         double SL_sell = Ask + NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);
         
         if (Check2_Margin(ORDER_TYPE_SELL,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_SELL,SL_sell,fvgLow)){
            obj_Trade.Sell(trade_lots,_Symbol,Bid,SL_sell,fvgLow);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }
      else if (fvgColor == CLR_UP && Ask < fvgLow && !signalFVGs[j]){
         Print("BUY SIGNAL For (",fvgNAME,") Now @ ",Ask);
         double SL_buy = Bid - NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);

         if (Check2_Margin(ORDER_TYPE_BUY,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_BUY,SL_buy,fvgHigh)){
            obj_Trade.Buy(trade_lots,_Symbol,Ask,SL_buy,fvgHigh);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }
      
      if (fvgExist == false){
         bool removeName = ArrayRemove(totalFVGs,0,1);
         bool removeTime = ArrayRemove(barTIMEs,0,1);
         bool removeSignal = ArrayRemove(signalFVGs,0,1);
         if (removeName && removeTime && removeSignal){
            Print("Success removing the FVG DATA from the arrays. New Data as Below:");
            Print("FVGs: ",ArraySize(totalFVGs)," TIMEs: ",ArraySize(barTIMEs),
                     " SIGNALs: ",ArraySize(signalFVGs));
            ArrayPrint(totalFVGs);
            ArrayPrint(barTIMEs);
            ArrayPrint(signalFVGs);
         }
      }      
   }
   
}

Saúde para nós! Agora criamos um sistema de trading baseado no conceito de Smart Money com a estratégia FVG/Imbalance para gerar sinais de negociação.


Resultados do testador de estratégia Fair Value Gap (FVG)

Após testar no testador de estratégia, aqui estão os resultados.

  • Gráfico de Saldo/Equidade:


  • Resultados do backtest:



Conclusão

Em conclusão, a exploração da codificação da estratégia Fair Value Gap ou Imbalance, por meio dos diversos tópicos descritos, oferece insights valiosos sobre metodologias de negociação quantitativa. Por meio deste artigo, aprofundamos os detalhes intrincados de como implementar tais estratégias, abordando componentes-chave, como análise de dados, modelagem estatística e técnicas de negociação algorítmica.

Primeiramente, entender o conceito de Fair Value Gap ou Imbalance é essencial, pois ele forma a base da estratégia. Envolve identificar discrepâncias entre o preço de mercado de um ativo e seu valor intrínseco, utilizando métodos estatísticos para medir essas diferenças com precisão.

Além disso, o artigo enfatiza a importância de técnicas robustas de análise de dados para extrair insights significativos de dados financeiros. Técnicas como análise de séries temporais, análise de candles e análise de sentimento desempenham um papel crucial na identificação de padrões e tendências que podem informar as decisões de negociação.

Ademais, a parte de codificação da estratégia destaca a importância da proficiência na linguagem de programação MQL5, bem como a familiaridade com as bibliotecas e funções do MQL5. Práticas eficientes de codificação permitem a automação dos processos de negociação, facilitando a execução mais rápida e a escalabilidade da estratégia.

Aviso legal: As informações ilustradas neste artigo são apenas para fins educacionais. É apenas destinado a mostrar insights sobre como criar um EA Fair Value Gap com base na abordagem Smart Money Concept e, portanto, deve ser usado como uma base para criar um expert advisor melhor, com mais otimização e extração de dados considerados. As informações apresentadas não garantem quaisquer resultados de negociação. 

No geral, o artigo destaca a natureza interdisciplinar do trading quantitativo, da estatística e da análise de barras para desenvolver estratégias eficazes para navegar nos mercados financeiros dinâmicos. Ao sintetizar conceitos teóricos com implementações práticas de codificação, o artigo equipa os leitores com as ferramentas necessárias para perseguir empreendimentos de negociação quantitativa com sucesso, especificamente a abordagem Smart Money Concept (SMC).


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

Arquivos anexados |
FVG_SMC_EA.mq5 (21.23 KB)
Últimos Comentários | Ir para discussão (21)
Taku001
Taku001 | 21 ago. 2024 em 10:07
Allan Munene Mutiiria #:
@Taku001, obrigado pelo feedback gentil e pelo reconhecimento. As verificações são para requisitos de margem, volume e níveis de negociação e garantem que não haja erros antes de colocar as negociações, respectivamente. Sua declaração é feita nos arquivos anexos que estão disponíveis na parte inferior do artigo como anexos, de preferência como arquivos ZIP. Espero que isso ajude. Muito obrigado.
Obrigado, Allan. Você é uma estrela. Continue com seu bom trabalho
Allan Munene Mutiiria
Allan Munene Mutiiria | 22 set. 2024 em 20:51
Taku001 # : Obrigado, Allan. Você é um astro. Continue com seu bom trabalho.

Fico feliz em ouvir isso. Obrigado, Allan.

Dragosh Zavadschi
Dragosh Zavadschi | 3 out. 2024 em 17:03
Instruções muito detalhadas e EA, obrigado, é incrível!
Allan Munene Mutiiria
Allan Munene Mutiiria | 3 out. 2024 em 18:39
Dragosh Zavadschi #:
Instruções e EA muito detalhados, obrigado, é fantástico!
@Dragosh Zavadschi, obrigado pelo feedback gentil e pelas boas-vindas. Ficamos felizes por você ter achado o artigo detalhado e fácil de entender. Muito obrigado. Boas negociações.
Xmh2265756468
Xmh2265756468 | 5 mai. 2025 em 07:05
艾伦·穆内内Mutiiria #

Obrigado, Juan Guirao. Ficamos felizes que você tenha achado o conteúdo valioso e bem informado. Seu feedback é muito apreciado.


Muito obrigado por compartilhar. Sou do outro lado do oceano. Posso adicioná-lo para saber mais sobre a EA?

Do básico ao intermediário: Array e Strings (I) Do básico ao intermediário: Array e Strings (I)
Neste artigo, começaremos a ver alguns tipos especiais de dados. Vamos começar definindo o que seria uma string e como usar alguns procedimentos básicos. Isto para que possamos começar a trabalhar com este tipo que é bem curioso. Apesar de em alguns momentos ser um tanto confuso para iniciantes. O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como sendo, uma aplicação cuja finalidade não venha a ser o aprendizado e estudo dos conceitos mostrados.
Do básico ao intermediário: Precedência de operadores Do básico ao intermediário: Precedência de operadores
Este é com toda a certeza, o assunto mais complicado de explicar somente utilizando a parte teórica do mesmo. Sendo assim, aconselho a você, meu caro leitor, procurar praticar o que será mostrado aqui. Mesmo quando tudo parece simples a principio, esta questão sobre operadores, de fato, somente será bem compreendida com a pratica aliada ao estudo constante. O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como sendo, uma aplicação cuja finalidade não venha a ser o aprendizado e estudo dos conceitos mostrados.
Introdução ao MQL5 (Parte 7): Guia para Iniciantes na Criação de Expert Advisors e Utilização de Código Gerado por IA no MQL5 Introdução ao MQL5 (Parte 7): Guia para Iniciantes na Criação de Expert Advisors e Utilização de Código Gerado por IA no MQL5
Descubra o guia definitivo para iniciantes na criação de Expert Advisors (EAs) com MQL5 em nosso artigo abrangente. Aprenda passo a passo como construir EAs utilizando pseudocódigo e aproveite o poder do código gerado por IA. Seja você novo no trading algorítmico ou esteja buscando aprimorar suas habilidades, este guia oferece um caminho claro para criar EAs eficazes.
Construindo um Modelo de Restrição de Tendência de Candlestick (Parte 2): Mesclando Indicadores Nativos Construindo um Modelo de Restrição de Tendência de Candlestick (Parte 2): Mesclando Indicadores Nativos
Este artigo foca em aproveitar os indicadores embutidos no MetaTrader 5 para filtrar sinais fora da tendência. Avançando a partir do artigo anterior, exploraremos como fazer isso usando o código MQL5 para comunicar nossa ideia ao programa final.