//+------------------------------------------------------------------+ //| FVG_Detector.mq5 | //| Expert Advisor para MT5 | //| Detección y Visualización de Fair Value Gaps| //+------------------------------------------------------------------+ #property copyright "FVG Visual Detector" #property link "" #property version "1.00" #property description "Expert Advisor exclusivo para detectar y dibujar Fair Value Gaps" #property description "NO ejecuta trades - Solo análisis visual" #property strict //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\Utils\\MetricSaver.mqh" /* Codigo hecho por ia Claude Sonnet 4.5 Promt: Desarrolla un BOT (Expert Advisor) para MetaTrader 5 (MT5) que EXCLUSIVAMENTE detecte y dibuje Fair Value Gaps (FVG) en el gráfico. ⚠️ Este BOT NO debe ejecutar operaciones, NO debe abrir/cerrar trades y NO debe funcionar como indicador. Su única función es análisis visual mediante dibujo en el gráfico . Requisitos técnicos obligatorios: Implementado como Expert Advisor (EA) en MQL5. Detección y dibujo de FVG alcistas y bajistas. Funcionamiento rápido, eficiente y altamente optimizado. Capaz de trabajar con datos históricos y en tiempo real. Configuración personalizable desde el panel del BOT: Colores de los FVG. Opacidad / transparencia. Grosor y estilo de los rectángulos. Activar o desactivar FVG alcistas o bajistas. Timeframe de análisis. Aspectos visuales: Los FVG deben dibujarse como zonas (rectángulos) claras y limpias. Evitar redibujados innecesarios. El BOT debe actualizar los FVG solo cuando sea necesario. Buenas prácticas de desarrollo: Código limpio, modular y bien comentado. Uso eficiente de OnInit, OnTick y manejo de objetos gráficos. Optimización de loops y cálculos para mínimo consumo de CPU. Usa todo tu conocimiento avanzado en MQL5, optimización y Smart Money Concepts para crear un BOT profesional, estable y rápido, enfocado únicamente en dibujar Fair Value Gaps. */ //+------------------------------------------------------------------+ //| INPUTS - Configuración Personalizable | //+------------------------------------------------------------------+ input group "===== CONFIGURACIÓN GENERAL =====" input bool InpEnableBullishFVG = true; // Activar FVG Alcistas input bool InpEnableBearishFVG = true; // Activar FVG Bajistas input int InpMaxBarsToAnalyze = 500; // Máximo de barras a analizar input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Timeframe de análisis input group "===== CONFIGURACIÓN VISUAL FVG ALCISTAS =====" input color InpBullishColor = clrLimeGreen; // Color FVG Alcistas input int InpBullishTransparency = 85; // Transparencia (0-100) input ENUM_LINE_STYLE InpBullishBorderStyle = STYLE_SOLID; // Estilo de borde input int InpBullishBorderWidth = 1; // Grosor de borde input group "===== CONFIGURACIÓN VISUAL FVG BAJISTAS =====" input color InpBearishColor = clrTomato; // Color FVG Bajistas input int InpBearishTransparency = 85; // Transparencia (0-100) input ENUM_LINE_STYLE InpBearishBorderStyle = STYLE_SOLID; // Estilo de borde input int InpBearishBorderWidth = 1; // Grosor de borde input group "===== CONFIGURACIÓN AVANZADA =====" input bool InpDeleteFilledFVG = false; // Eliminar FVG cuando precio los llena input double InpMinFVGSize = 5.0; // Tamaño mínimo FVG en puntos input bool InpExtendToRight = true; // Extender rectángulos a la derecha input int InpMaxFVGsToDisplay = 50; // Máximo de FVGs a mostrar //+------------------------------------------------------------------+ //| Estructura para almacenar datos de FVG | //+------------------------------------------------------------------+ struct FVGData { datetime time; // Tiempo de creación del FVG double topPrice; // Precio superior del gap double bottomPrice; // Precio inferior del gap bool isBullish; // true = Alcista, false = Bajista string objectName; // Nombre del objeto gráfico bool isFilled; // Si el precio ha llenado el FVG }; //+------------------------------------------------------------------+ //| Variables Globales | //+------------------------------------------------------------------+ FVGData g_FVGList[]; // Array dinámico para almacenar FVGs int g_FVGCount = 0; // Contador de FVGs activos string g_prefix = "FVG_"; // Prefijo para objetos gráficos int g_lastAnalyzedBar = -1; // Última barra analizada double g_point; // Valor del punto del símbolo int g_digits; // Dígitos del símbolo //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { CMetricsSave::Start(FILE_CODE4); // Inicializar variables del símbolo g_point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); g_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); // Validar inputs if(!ValidateInputs()) { Print("❌ Error: Configuración inválida"); return(INIT_PARAMETERS_INCORRECT); } // Limpiar objetos gráficos anteriores CleanupOldObjects(); // Redimensionar array de FVGs ArrayResize(g_FVGList, InpMaxFVGsToDisplay); // Análisis inicial de barras históricas AnalyzeHistoricalBars(); Print("✅ FVG Detector iniciado correctamente"); Print("📊 Símbolo: ", _Symbol); Print("⏱️ Timeframe: ", EnumToString(InpTimeframe)); Print("🔍 Analizando hasta ", InpMaxBarsToAnalyze, " barras"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Limpiar todos los objetos gráficos creados CleanupAllFVGObjects(); Print("🔚 FVG Detector detenido. Razón: ", GetDeinitReasonText(reason)); CMetricsSave::Destroy(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Obtener información de la última barra int bars = iBars(_Symbol, InpTimeframe); if(bars < 3) return; // Necesitamos al menos 3 barras // Verificar si hay una nueva barra if(g_lastAnalyzedBar != bars) { // Analizar nuevos FVGs DetectNewFVGs(); // Actualizar estado de FVGs existentes (si están llenos) if(InpDeleteFilledFVG) UpdateFVGStatus(); g_lastAnalyzedBar = bars; } } //+------------------------------------------------------------------+ //| Validar parámetros de entrada | //+------------------------------------------------------------------+ bool ValidateInputs() { if(InpMaxBarsToAnalyze < 10 || InpMaxBarsToAnalyze > 5000) { Print("⚠️ Advertencia: InpMaxBarsToAnalyze fuera de rango (10-5000)"); return false; } if(InpBullishTransparency < 0 || InpBullishTransparency > 100) { Print("⚠️ Advertencia: Transparencia alcista fuera de rango (0-100)"); return false; } if(InpBearishTransparency < 0 || InpBearishTransparency > 100) { Print("⚠️ Advertencia: Transparencia bajista fuera de rango (0-100)"); return false; } if(!InpEnableBullishFVG && !InpEnableBearishFVG) { Print("⚠️ Advertencia: Debe activar al menos un tipo de FVG"); return false; } return true; } //+------------------------------------------------------------------+ //| Analizar barras históricas | //+------------------------------------------------------------------+ void AnalyzeHistoricalBars() { int barsToAnalyze = MathMin(InpMaxBarsToAnalyze, iBars(_Symbol, InpTimeframe) - 3); Print("🔄 Analizando ", barsToAnalyze, " barras históricas..."); for(int i = barsToAnalyze; i >= 2; i--) { CheckForFVG(i); } Print("✅ Análisis histórico completado. FVGs detectados: ", g_FVGCount); } //+------------------------------------------------------------------+ //| Detectar nuevos FVGs en tiempo real | //+------------------------------------------------------------------+ void DetectNewFVGs() { // Analizar las últimas 3 barras para nuevos FVGs CheckForFVG(2); CheckForFVG(1); } //+------------------------------------------------------------------+ //| Verificar si existe un FVG en la barra especificada | //+------------------------------------------------------------------+ void CheckForFVG(int barIndex) { // Obtener datos de 3 barras consecutivas double high1 = iHigh(_Symbol, InpTimeframe, barIndex + 1); double low1 = iLow(_Symbol, InpTimeframe, barIndex + 1); double high2 = iHigh(_Symbol, InpTimeframe, barIndex); double low2 = iLow(_Symbol, InpTimeframe, barIndex); double high3 = iHigh(_Symbol, InpTimeframe, barIndex - 1); double low3 = iLow(_Symbol, InpTimeframe, barIndex - 1); datetime time2 = iTime(_Symbol, InpTimeframe, barIndex); // Detectar FVG ALCISTA (Bullish FVG) // Condición: El low de la barra 3 está por encima del high de la barra 1 if(InpEnableBullishFVG && low3 > high1) { double gapSize = (low3 - high1) / g_point; if(gapSize >= InpMinFVGSize) { // Verificar si ya existe este FVG if(!FVGExists(time2, high1, low3)) { CreateFVG(time2, high1, low3, true); } } } // Detectar FVG BAJISTA (Bearish FVG) // Condición: El high de la barra 3 está por debajo del low de la barra 1 if(InpEnableBearishFVG && high3 < low1) { double gapSize = (low1 - high3) / g_point; if(gapSize >= InpMinFVGSize) { // Verificar si ya existe este FVG if(!FVGExists(time2, high3, low1)) { CreateFVG(time2, high3, low1, false); } } } } //+------------------------------------------------------------------+ //| Verificar si un FVG ya existe | //+------------------------------------------------------------------+ bool FVGExists(datetime time, double bottomPrice, double topPrice) { for(int i = 0; i < g_FVGCount; i++) { if(g_FVGList[i].time == time && MathAbs(g_FVGList[i].bottomPrice - bottomPrice) < g_point && MathAbs(g_FVGList[i].topPrice - topPrice) < g_point) { return true; } } return false; } //+------------------------------------------------------------------+ //| Crear y dibujar un nuevo FVG | //+------------------------------------------------------------------+ void CreateFVG(datetime time, double bottomPrice, double topPrice, bool isBullish) { // Verificar límite de FVGs if(g_FVGCount >= InpMaxFVGsToDisplay) { // Eliminar el FVG más antiguo RemoveOldestFVG(); } // Crear nombre único para el objeto string objName = g_prefix + (isBullish ? "Bull_" : "Bear_") + TimeToString(time, TIME_DATE | TIME_SECONDS); // Crear rectángulo if(!ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time, bottomPrice, 0, topPrice)) { Print("❌ Error al crear objeto: ", objName, " - Error: ", GetLastError()); return; } // Configurar propiedades del rectángulo datetime endTime = InpExtendToRight ? 0 : TimeCurrent() + PeriodSeconds(InpTimeframe) * 100; ObjectSetInteger(0, objName, OBJPROP_TIME, 1, endTime); // Colores y estilos color fillColor = isBullish ? InpBullishColor : InpBearishColor; int transparency = isBullish ? InpBullishTransparency : InpBearishTransparency; ENUM_LINE_STYLE borderStyle = isBullish ? InpBullishBorderStyle : InpBearishBorderStyle; int borderWidth = isBullish ? InpBullishBorderWidth : InpBearishBorderWidth; // Aplicar transparencia al color color finalColor = ColorWithTransparency(fillColor, transparency); ObjectSetInteger(0, objName, OBJPROP_COLOR, finalColor); ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth); ObjectSetInteger(0, objName, OBJPROP_FILL, true); ObjectSetInteger(0, objName, OBJPROP_BACK, true); ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true); // Tooltip string tooltip = (isBullish ? "🟢 FVG Alcista" : "🔴 FVG Bajista") + "\nTiempo: " + TimeToString(time) + "\nRango: " + DoubleToString(bottomPrice, g_digits) + " - " + DoubleToString(topPrice, g_digits); ObjectSetString(0, objName, OBJPROP_TOOLTIP, tooltip); // Guardar en el array g_FVGList[g_FVGCount].time = time; g_FVGList[g_FVGCount].topPrice = topPrice; g_FVGList[g_FVGCount].bottomPrice = bottomPrice; g_FVGList[g_FVGCount].isBullish = isBullish; g_FVGList[g_FVGCount].objectName = objName; g_FVGList[g_FVGCount].isFilled = false; g_FVGCount++; } //+------------------------------------------------------------------+ //| Aplicar transparencia a un color | //+------------------------------------------------------------------+ color ColorWithTransparency(color baseColor, int transparency) { // Convertir transparencia de 0-100 a alpha 0-255 int alpha = (int)((100 - transparency) * 255 / 100); // Extraer componentes RGB int r = (int)((baseColor) & 0xFF); int g = (int)((baseColor >> 8) & 0xFF); int b = (int)((baseColor >> 16) & 0xFF); // Aplicar alpha al color // En MT5, la transparencia se maneja con la saturación del color double factor = alpha / 255.0; r = (int)(r * factor + 255 * (1 - factor)); g = (int)(g * factor + 255 * (1 - factor)); b = (int)(b * factor + 255 * (1 - factor)); return (color)(r | (g << 8) | (b << 16)); } //+------------------------------------------------------------------+ //| Actualizar estado de los FVGs (verificar si están llenos) | //+------------------------------------------------------------------+ void UpdateFVGStatus() { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); for(int i = g_FVGCount - 1; i >= 0; i--) { if(g_FVGList[i].isFilled) continue; bool isFilled = false; // FVG Alcista se llena cuando el precio baja y toca el nivel inferior if(g_FVGList[i].isBullish && currentPrice <= g_FVGList[i].bottomPrice) { isFilled = true; } // FVG Bajista se llena cuando el precio sube y toca el nivel superior if(!g_FVGList[i].isBullish && currentPrice >= g_FVGList[i].topPrice) { isFilled = true; } if(isFilled) { g_FVGList[i].isFilled = true; // Eliminar el objeto gráfico ObjectDelete(0, g_FVGList[i].objectName); // Remover del array RemoveFVGFromArray(i); } } } //+------------------------------------------------------------------+ //| Remover FVG del array | //+------------------------------------------------------------------+ void RemoveFVGFromArray(int index) { if(index < 0 || index >= g_FVGCount) return; // Mover elementos for(int i = index; i < g_FVGCount - 1; i++) { g_FVGList[i] = g_FVGList[i + 1]; } g_FVGCount--; } //+------------------------------------------------------------------+ //| Eliminar el FVG más antiguo | //+------------------------------------------------------------------+ void RemoveOldestFVG() { if(g_FVGCount == 0) return; // El FVG más antiguo está en el índice 0 ObjectDelete(0, g_FVGList[0].objectName); RemoveFVGFromArray(0); } //+------------------------------------------------------------------+ //| Limpiar objetos antiguos al inicio | //+------------------------------------------------------------------+ void CleanupOldObjects() { int totalObjects = ObjectsTotal(0, 0, OBJ_RECTANGLE); for(int i = totalObjects - 1; i >= 0; i--) { string objName = ObjectName(0, i, 0, OBJ_RECTANGLE); if(StringFind(objName, g_prefix) == 0) { ObjectDelete(0, objName); } } ChartRedraw(0); } //+------------------------------------------------------------------+ //| Limpiar todos los objetos FVG | //+------------------------------------------------------------------+ void CleanupAllFVGObjects() { for(int i = 0; i < g_FVGCount; i++) { ObjectDelete(0, g_FVGList[i].objectName); } g_FVGCount = 0; ChartRedraw(0); } //+------------------------------------------------------------------+ //| Obtener texto de la razón de desinicialización | //+------------------------------------------------------------------+ string GetDeinitReasonText(int reason) { switch(reason) { case REASON_PROGRAM: return "Programa cerrado"; case REASON_REMOVE: return "EA removido del gráfico"; case REASON_RECOMPILE: return "EA recompilado"; case REASON_CHARTCHANGE: return "Cambio de símbolo o timeframe"; case REASON_CHARTCLOSE: return "Gráfico cerrado"; case REASON_PARAMETERS: return "Parámetros modificados"; case REASON_ACCOUNT: return "Cambio de cuenta"; default: return "Razón desconocida"; } } //+------------------------------------------------------------------+