Smart Money y FVG

Hoy dejamos un indicador por aquí para MetaTrader 5, que te dibuja los FVG (fair value gap), en el contexto de ICT y smart money, que se dan entre tres velas, entre el cierre de la primera vela y el cierre de la tercera. Bueno para entrenar el «ojo», y com siempre, gratuito. El GAP esta puesto por defecto en 10 puntos, y te muestra las 200 últimas velas. Muestra colores diferentes para FVG alcistas o bajistas. Ya que es gratuito y de edición propia, lo puedes ajustar a tu gusto.

Espero que te sirva.

//+------------------------------------------------------------------+
//|                                   FVG_With_50_Percent_Line.mq5   |
//|                                  Copyright 2026, Raulito073      |
//+------------------------------------------------------------------+
#property copyright "Raulito073"
#property version   "1.06"
#property indicator_chart_window

// Parámetros configurables
input color  InpColorBullish = clrLightSkyBlue;   // Color FVG Alcista
input color  InpColorBearish = clrCornflowerBlue;  // Color FVG Bajista
input int    InpMaxBars      = 200;              // Historial de velas
input int    InpMinPuntos    = 10;                // Tamaño mínimo del FVG en puntos enteros

int OnInit() { return(INIT_SUCCEEDED); }
void OnDeinit(const int reason) { ObjectsDeleteAll(0, "FVG_"); ObjectsDeleteAll(0, "FVG50_"); }

int OnCalculate(const int rates_total, const int prev_calculated,
                const datetime &time[], const double &open[], const double &high[],
                const double &low[], const double &close[], const long &tick_volume[],
                const long &volume[], const int &spread[])
{
   int limit = rates_total - prev_calculated;
   if(limit <= 0) limit = 1;
   if(limit > InpMaxBars) limit = InpMaxBars;

   ArraySetAsSeries(time, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);

   // Evitar errores si _Point no está inicializado
   if(_Point <= 0) return(rates_total);

   // 1. Detectar nuevos FVG y trazar zonas e hilos del 50%
   for(int i = limit; i >= 1; i--)
   {
      if(i + 2 >= rates_total) continue;

      double v1_high = high[i+2];
      double v1_low  = low[i+2];
      double v3_high = high[i];
      double v3_low  = low[i];

      string nameSuffix = TimeToString(time[i+1], TIME_DATE|TIME_MINUTES);
      string objName    = "FVG_" + nameSuffix;
      string midLineName = "FVG50_" + nameSuffix;

      // --- FVG ALCISTA ---
      if(v3_low > v1_high)
      {
         // Calcular tamaño matemático convertido a puntos enteros
         double diferenciaPrecio = v3_low - v1_high;
         int puntosFVG = (int)MathRound(diferenciaPrecio / _Point);

         // Filtrar: Si es más pequeño que los puntos exigidos, lo ignoramos
         if(puntosFVG < InpMinPuntos) continue;

         // Borrar si ya existían para refrescar coordenadas
         ObjectDelete(0, objName);
         ObjectDelete(0, midLineName);

         // Rectángulo del canal completo
         if(ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time[i+2], v1_high, time[i], v3_low))
         {
            ObjectSetInteger(0, objName, OBJPROP_COLOR, InpColorBullish);
            ObjectSetInteger(0, objName, OBJPROP_FILL, true);
            ObjectSetInteger(0, objName, OBJPROP_BACK, true);
         }

         // Línea discontinua en el 50%
         double midPrice = v1_high + (diferenciaPrecio / 2.0);
         if(ObjectCreate(0, midLineName, OBJ_TREND, 0, time[i+2], midPrice, time[i], midPrice))
         {
            ObjectSetInteger(0, midLineName, OBJPROP_COLOR, InpColorBullish);
            ObjectSetInteger(0, midLineName, OBJPROP_STYLE, STYLE_DASH); // Estilo discontinua
            ObjectSetInteger(0, midLineName, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, midLineName, OBJPROP_RAY_RIGHT, false);
            ObjectSetInteger(0, midLineName, OBJPROP_BACK, true);
         }
      }
      
      // --- FVG BAJISTA ---
      else if(v3_high < v1_low)
      {
         // Calcular tamaño matemático convertido a puntos enteros
         double diferenciaPrecio = v1_low - v3_high;
         int puntosFVG = (int)MathRound(diferenciaPrecio / _Point);

         // Filtrar: Si es más pequeño que los puntos exigidos, lo ignoramos
         if(puntosFVG < InpMinPuntos) continue;

         ObjectDelete(0, objName);
         ObjectDelete(0, midLineName);

         // Rectángulo del canal completo
         if(ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time[i+2], v1_low, time[i], v3_high))
         {
            ObjectSetInteger(0, objName, OBJPROP_COLOR, InpColorBearish);
            ObjectSetInteger(0, objName, OBJPROP_FILL, true);
            ObjectSetInteger(0, objName, OBJPROP_BACK, true);
         }

         // Línea discontinua en el 50%
         double midPrice = v3_high + (diferenciaPrecio / 2.0);
         if(ObjectCreate(0, midLineName, OBJ_TREND, 0, time[i+2], midPrice, time[i], midPrice))
         {
            ObjectSetInteger(0, midLineName, OBJPROP_COLOR, InpColorBearish);
            ObjectSetInteger(0, midLineName, OBJPROP_STYLE, STYLE_DASH); // Estilo discontinua
            ObjectSetInteger(0, midLineName, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, midLineName, OBJPROP_RAY_RIGHT, false);
            ObjectSetInteger(0, midLineName, OBJPROP_BACK, true);
         }
      }
   }

   // 2. Controlar Extensión Dinámica y Mitigación en tiempo real
   for(int k = ObjectsTotal(0, 0, OBJ_RECTANGLE) - 1; k >= 0; k--)
   {
      string name = ObjectGetString(0, "", OBJPROP_NAME, k);
      if(StringFind(name, "FVG_") == 0)
      {
         string suffix = StringSubstr(name, 4);
         string companionLine = "FVG50_" + suffix;

         double price_p1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
         double price_p2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1);
         
         double price_top    = (price_p1 > price_p2) ? price_p1 : price_p2;
         double price_bottom = (price_p1 > price_p2) ? price_p2 : price_p1;

         // Detectar dirección analizando la posición del cierre actual respecto a la zona
         bool isBullish = (price_top < close[0]); 

         // --- EVALUAR MITIGACIÓN ---
         if(!isBullish && high[0] >= price_bottom) // Tocó la base del FVG bajista
         {
            ObjectDelete(0, name);
            ObjectDelete(0, companionLine);
            continue;
         }
         if(isBullish && low[0] <= price_top) // Tocó el techo del FVG alcista
         {
            ObjectDelete(0, name);
            ObjectDelete(0, companionLine);
            continue;
         }

         // Si sigue vivo, arrastramos el extremo derecho del recuadro y de la línea al tiempo actual
         ObjectSetInteger(0, name, OBJPROP_TIME, 1, time[0]);
         if(ObjectFind(0, companionLine) >= 0)
         {
            ObjectSetInteger(0, companionLine, OBJPROP_TIME, 1, time[0]);
         }
      }
   }

   return(rates_total);
}

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *