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