| //+------------------------------------------------------------------+
|
| //| FVG_Detector_EA.mq5 |
|
| //| Fair Value Gap Detection Tool |
|
| //| Professional Smart Money Concepts Expert |
|
| //+------------------------------------------------------------------+
|
| #property copyright "FVG Detector - SMC Analysis Tool"
|
| #property link "https://www.smartmoneyconcepts.com"
|
| #property version "2.01"
|
| #property description "Expert Advisor para detección y visualización de Fair Value Gaps (FVG)"
|
| #property description "⚠️ SOLO ANÁLISIS VISUAL - NO EJECUTA OPERACIONES"
|
| #property strict
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| #include "..\\Utils\\MetricSaver.mqh"
|
|
|
| /* Codigo hecho por ia Claude Opus 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.
|
| */
|
|
|
| //+------------------------------------------------------------------+
|
| //| ENUMERACIONES PERSONALIZADAS |
|
| //+------------------------------------------------------------------+
|
|
|
| // Estilos de borde para los rectángulos FVG
|
| enum ENUM_FVG_BORDER_STYLE
|
| {
|
| FVG_BORDER_SOLID = STYLE_SOLID, // Línea Sólida
|
| FVG_BORDER_DASH = STYLE_DASH, // Línea Discontinua
|
| FVG_BORDER_DOT = STYLE_DOT, // Línea Punteada
|
| FVG_BORDER_DASHDOT = STYLE_DASHDOT, // Guión-Punto
|
| FVG_BORDER_DASHDOTDOT = STYLE_DASHDOTDOT // Guión-Punto-Punto
|
| };
|
|
|
| // Tipo de extensión del FVG
|
| enum ENUM_FVG_EXTENSION
|
| {
|
| FVG_EXTEND_FIXED = 0, // Extensión Fija (N velas)
|
| FVG_EXTEND_UNTIL_FILL = 1 // Hasta ser llenado
|
| };
|
|
|
| //+------------------------------------------------------------------+
|
| //| PARÁMETROS DE ENTRADA - CONFIGURACIÓN DEL USUARIO |
|
| //+------------------------------------------------------------------+
|
|
|
| // === CONFIGURACIÓN GENERAL ===
|
| input group "══════════ CONFIGURACIÓN GENERAL ══════════"
|
| input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Timeframe de análisis
|
| input int InpLookbackBars = 500; // Velas a analizar (historial)
|
| input int InpMinFVGSize = 5; // Tamaño mínimo FVG (puntos)
|
| input bool InpShowFilledFVG = false; // Mostrar FVG ya llenados
|
| input ENUM_FVG_EXTENSION InpExtensionType = FVG_EXTEND_UNTIL_FILL; // Tipo de extensión
|
|
|
| // === FVG ALCISTAS (BULLISH) ===
|
| input group "══════════ FVG ALCISTAS (BULLISH) ══════════"
|
| input bool InpShowBullishFVG = true; // Activar FVG Alcistas
|
| input color InpBullishColor = clrDodgerBlue; // Color FVG Alcista
|
| input uchar InpBullishOpacity = 50; // Opacidad (0-255)
|
| input int InpBullishWidth = 1; // Grosor del borde
|
| input ENUM_FVG_BORDER_STYLE InpBullishStyle = FVG_BORDER_SOLID; // Estilo del borde
|
| input bool InpBullishFill = true; // Rellenar rectángulo
|
|
|
| // === FVG BAJISTAS (BEARISH) ===
|
| input group "══════════ FVG BAJISTAS (BEARISH) ══════════"
|
| input bool InpShowBearishFVG = true; // Activar FVG Bajistas
|
| input color InpBearishColor = clrCrimson; // Color FVG Bajista
|
| input uchar InpBearishOpacity = 50; // Opacidad (0-255)
|
| input int InpBearishWidth = 1; // Grosor del borde
|
| input ENUM_FVG_BORDER_STYLE InpBearishStyle = FVG_BORDER_SOLID; // Estilo del borde
|
| input bool InpBearishFill = true; // Rellenar rectángulo
|
|
|
| // === CONFIGURACIÓN AVANZADA ===
|
| input group "══════════ CONFIGURACIÓN AVANZADA ══════════"
|
| input int InpFixedExtension = 50; // Extensión fija (velas)
|
| input bool InpDeleteOnFill = true; // Eliminar FVG al llenarse
|
| input int InpUpdateFrequency = 1; // Frecuencia actualización (ticks)
|
| input string InpObjectPrefix = "FVG_"; // Prefijo objetos gráficos
|
|
|
| //+------------------------------------------------------------------+
|
| //| ESTRUCTURA PARA ALMACENAR INFORMACIÓN DE FVG |
|
| //+------------------------------------------------------------------+
|
| struct FVG_Data
|
| {
|
| datetime timeStart; // Tiempo de inicio (vela del gap)
|
| datetime timeEnd; // Tiempo de fin (extensión)
|
| double priceHigh; // Precio superior del gap
|
| double priceLow; // Precio inferior del gap
|
| bool isBullish; // true = alcista, false = bajista
|
| bool isFilled; // true si el gap ha sido llenado
|
| int barIndex; // Índice de la vela donde se formó
|
| string objectName; // Nombre del objeto gráfico
|
| };
|
|
|
| //+------------------------------------------------------------------+
|
| //| VARIABLES GLOBALES |
|
| //+------------------------------------------------------------------+
|
| FVG_Data g_fvgArray[]; // Array dinámico de FVGs detectados
|
| int g_totalFVG = 0; // Contador total de FVGs
|
| int g_tickCounter = 0; // Contador de ticks para optimización
|
| datetime g_lastBarTime = 0; // Tiempo de la última vela procesada
|
| int g_lastBarsCount = 0; // Último conteo de barras
|
| string g_chartSymbol; // Símbolo del gráfico
|
| ENUM_TIMEFRAMES g_chartTimeframe; // Timeframe activo
|
| double g_pointValue; // Valor del punto
|
| int g_digits; // Dígitos del símbolo
|
|
|
| //+------------------------------------------------------------------+
|
| //| FUNCIÓN DE INICIALIZACIÓN DEL EXPERT ADVISOR |
|
| //+------------------------------------------------------------------+
|
| int OnInit()
|
| {
|
| CMetricsSave::Start(FILE_CODE6);
|
|
|
|
|
| // Validar parámetros de entrada
|
| if(!ValidateInputParameters())
|
| {
|
| Print("❌ Error: Parámetros de entrada inválidos");
|
| return(INIT_PARAMETERS_INCORRECT);
|
| }
|
|
|
| // Inicializar variables globales
|
| g_chartSymbol = _Symbol;
|
| g_chartTimeframe = (InpTimeframe == PERIOD_CURRENT) ? Period() : InpTimeframe;
|
| g_pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
| g_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
| g_lastBarTime = 0;
|
| g_lastBarsCount = 0;
|
| g_tickCounter = 0;
|
|
|
| // Limpiar objetos gráficos anteriores
|
| CleanupGraphicObjects();
|
|
|
| // Inicializar array de FVGs
|
| ArrayResize(g_fvgArray, 0);
|
| g_totalFVG = 0;
|
|
|
| // Detectar FVGs en el historial
|
| DetectHistoricalFVGs();
|
|
|
| // Dibujar FVGs detectados
|
| DrawAllFVGs();
|
|
|
| // Forzar actualización del gráfico
|
| ChartRedraw(0);
|
|
|
| // Mensaje de inicialización exitosa
|
| Print("═══════════════════════════════════════════════════════════");
|
| Print("✅ FVG Detector EA iniciado correctamente");
|
| Print("📊 Símbolo: ", g_chartSymbol, " | Timeframe: ", EnumToString(g_chartTimeframe));
|
| Print("📈 FVGs Alcistas: ", InpShowBullishFVG ? "Activados" : "Desactivados");
|
| Print("📉 FVGs Bajistas: ", InpShowBearishFVG ? "Activados" : "Desactivados");
|
| Print("📋 FVGs detectados: ", g_totalFVG);
|
| Print("═══════════════════════════════════════════════════════════");
|
|
|
| return(INIT_SUCCEEDED);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| FUNCIÓN DE DESINICIALIZACIÓN |
|
| //+------------------------------------------------------------------+
|
| void OnDeinit(const int reason)
|
| {
|
| // Limpiar todos los objetos gráficos creados
|
| CleanupGraphicObjects();
|
|
|
| // Liberar memoria del array
|
| ArrayFree(g_fvgArray);
|
| g_totalFVG = 0;
|
|
|
| // Mensaje de cierre
|
| string reasonText = "";
|
| switch(reason)
|
| {
|
| case REASON_PROGRAM: reasonText = "Programa terminado"; break;
|
| case REASON_REMOVE: reasonText = "EA eliminado del gráfico"; break;
|
| case REASON_RECOMPILE: reasonText = "EA recompilado"; break;
|
| case REASON_CHARTCHANGE: reasonText = "Símbolo o timeframe cambiado"; break;
|
| case REASON_CHARTCLOSE: reasonText = "Gráfico cerrado"; break;
|
| case REASON_PARAMETERS: reasonText = "Parámetros modificados"; break;
|
| case REASON_ACCOUNT: reasonText = "Cuenta cambiada"; break;
|
| case REASON_TEMPLATE: reasonText = "Nueva plantilla aplicada"; break;
|
| default: reasonText = "Razón desconocida"; break;
|
| }
|
|
|
| Print("══════════════════════════════════════════════════════════");
|
| Print("⏹️ FVG Detector EA detenido - ", reasonText);
|
| Print("══════════════════════════════════════════════════════════");
|
| CMetricsSave::Destroy();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| FUNCIÓN PRINCIPAL OnTick - PROCESAMIENTO EN TIEMPO REAL |
|
| //+------------------------------------------------------------------+
|
| void OnTick()
|
| {
|
| // Control de frecuencia de actualización para optimización
|
| g_tickCounter++;
|
| if(g_tickCounter < InpUpdateFrequency)
|
| return;
|
| g_tickCounter = 0;
|
|
|
| // Obtener tiempo de la vela actual
|
| datetime currentBarTime = iTime(g_chartSymbol, g_chartTimeframe, 0);
|
| int currentBarsCount = iBars(g_chartSymbol, g_chartTimeframe);
|
|
|
| // Verificar si hay una nueva vela
|
| bool isNewBar = (currentBarTime != g_lastBarTime);
|
| bool barsChanged = (currentBarsCount != g_lastBarsCount);
|
|
|
| if(isNewBar || barsChanged)
|
| {
|
| g_lastBarTime = currentBarTime;
|
| g_lastBarsCount = currentBarsCount;
|
|
|
| // Detectar nuevo FVG potencial (solo en la vela más reciente completa)
|
| CheckForNewFVG(1);
|
|
|
| // Actualizar estado de FVGs existentes (verificar si fueron llenados)
|
| UpdateFVGStatus();
|
|
|
| // Redibujar FVGs si es necesario
|
| RedrawModifiedFVGs();
|
| }
|
| else
|
| {
|
| // Solo actualizar extensiones si no hay nueva vela
|
| UpdateFVGExtensions();
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| VALIDACIÓN DE PARÁMETROS DE ENTRADA |
|
| //+------------------------------------------------------------------+
|
| bool ValidateInputParameters()
|
| {
|
| bool isValid = true;
|
|
|
| // Validar lookback
|
| if(InpLookbackBars < 10 || InpLookbackBars > 10000)
|
| {
|
| Print("⚠️ Advertencia: LookbackBars debe estar entre 10 y 10000. Usando 500.");
|
| // Continuar con valor por defecto
|
| }
|
|
|
| // Validar tamaño mínimo
|
| if(InpMinFVGSize < 0)
|
| {
|
| Print("⚠️ Advertencia: MinFVGSize no puede ser negativo.");
|
| }
|
|
|
| // Validar que al menos un tipo de FVG esté activo
|
| if(!InpShowBullishFVG && !InpShowBearishFVG)
|
| {
|
| Print("⚠️ Advertencia: Ambos tipos de FVG están desactivados. El EA no mostrará nada.");
|
| }
|
|
|
| return isValid;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| LIMPIEZA DE OBJETOS GRÁFICOS |
|
| //+------------------------------------------------------------------+
|
| void CleanupGraphicObjects()
|
| {
|
| int totalObjects = ObjectsTotal(0, 0, OBJ_RECTANGLE);
|
|
|
| // Iterar en orden inverso para eliminar objetos con nuestro prefijo
|
| for(int i = totalObjects - 1; i >= 0; i--)
|
| {
|
| string objName = ObjectName(0, i, 0, OBJ_RECTANGLE);
|
| if(StringFind(objName, InpObjectPrefix) == 0)
|
| {
|
| ObjectDelete(0, objName);
|
| }
|
| }
|
|
|
| ChartRedraw(0);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| DETECCIÓN DE FVGs EN DATOS HISTÓRICOS |
|
| //+------------------------------------------------------------------+
|
| void DetectHistoricalFVGs()
|
| {
|
| // Determinar el rango de barras a analizar
|
| int totalBars = iBars(g_chartSymbol, g_chartTimeframe);
|
| int barsToAnalyze = MathMin(InpLookbackBars, totalBars - 3);
|
|
|
| if(barsToAnalyze < 3)
|
| {
|
| Print("⚠️ No hay suficientes datos históricos para análisis");
|
| return;
|
| }
|
|
|
| // Reservar memoria para el array (estimación inicial)
|
| ArrayResize(g_fvgArray, 0, barsToAnalyze / 10);
|
|
|
| // Iterar desde la vela más antigua hacia la más reciente
|
| for(int i = barsToAnalyze; i >= 2; i--)
|
| {
|
| CheckForFVGAtBar(i);
|
| }
|
|
|
| // Ajustar tamaño final del array
|
| ArrayResize(g_fvgArray, g_totalFVG);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| VERIFICAR FVG EN UNA VELA ESPECÍFICA |
|
| //+------------------------------------------------------------------+
|
| void CheckForFVGAtBar(int barIndex)
|
| {
|
| // Obtener datos OHLC de las 3 velas necesarias
|
| // Vela 0 (barIndex+1): Primera vela
|
| // Vela 1 (barIndex): Vela del impulso (donde se forma el gap)
|
| // Vela 2 (barIndex-1): Tercera vela
|
|
|
| double high0 = iHigh(g_chartSymbol, g_chartTimeframe, barIndex + 1);
|
| double low0 = iLow(g_chartSymbol, g_chartTimeframe, barIndex + 1);
|
| double high2 = iHigh(g_chartSymbol, g_chartTimeframe, barIndex - 1);
|
| double low2 = iLow(g_chartSymbol, g_chartTimeframe, barIndex - 1);
|
|
|
| // Verificar datos válidos
|
| if(high0 <= 0 || low0 <= 0 || high2 <= 0 || low2 <= 0)
|
| return;
|
|
|
| // Tiempos de las velas
|
| datetime time1 = iTime(g_chartSymbol, g_chartTimeframe, barIndex);
|
| datetime time2 = iTime(g_chartSymbol, g_chartTimeframe, barIndex - 1);
|
|
|
| // Tamaño mínimo en precio
|
| double minSize = InpMinFVGSize * g_pointValue;
|
|
|
| // === DETECCIÓN FVG ALCISTA (BULLISH) ===
|
| // Se forma cuando: Low de vela 3 > High de vela 1
|
| if(InpShowBullishFVG && low2 > high0)
|
| {
|
| double gapHigh = low2;
|
| double gapLow = high0;
|
| double gapSize = gapHigh - gapLow;
|
|
|
| if(gapSize >= minSize)
|
| {
|
| // Crear nuevo FVG
|
| FVG_Data newFVG;
|
| newFVG.timeStart = time1;
|
| newFVG.timeEnd = time2;
|
| newFVG.priceHigh = gapHigh;
|
| newFVG.priceLow = gapLow;
|
| newFVG.isBullish = true;
|
| newFVG.isFilled = false;
|
| newFVG.barIndex = barIndex;
|
| newFVG.objectName = GenerateFVGObjectName(true, barIndex);
|
|
|
| // Verificar si ya fue llenado en el historial
|
| CheckIfFVGFilledHistorical(newFVG);
|
|
|
| // Solo agregar si no está llenado o si queremos mostrar llenados
|
| if(!newFVG.isFilled || InpShowFilledFVG)
|
| {
|
| AddFVGToArray(newFVG);
|
| }
|
| }
|
| }
|
|
|
| // === DETECCIÓN FVG BAJISTA (BEARISH) ===
|
| // Se forma cuando: High de vela 3 < Low de vela 1
|
| if(InpShowBearishFVG && high2 < low0)
|
| {
|
| double gapHigh = low0;
|
| double gapLow = high2;
|
| double gapSize = gapHigh - gapLow;
|
|
|
| if(gapSize >= minSize)
|
| {
|
| // Crear nuevo FVG
|
| FVG_Data newFVG;
|
| newFVG.timeStart = time1;
|
| newFVG.timeEnd = time2;
|
| newFVG.priceHigh = gapHigh;
|
| newFVG.priceLow = gapLow;
|
| newFVG.isBullish = false;
|
| newFVG.isFilled = false;
|
| newFVG.barIndex = barIndex;
|
| newFVG.objectName = GenerateFVGObjectName(false, barIndex);
|
|
|
| // Verificar si ya fue llenado en el historial
|
| CheckIfFVGFilledHistorical(newFVG);
|
|
|
| // Solo agregar si no está llenado o si queremos mostrar llenados
|
| if(!newFVG.isFilled || InpShowFilledFVG)
|
| {
|
| AddFVGToArray(newFVG);
|
| }
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| VERIFICAR SI FVG FUE LLENADO EN HISTORIAL |
|
| //+------------------------------------------------------------------+
|
| void CheckIfFVGFilledHistorical(FVG_Data &fvg)
|
| {
|
| // Obtener el índice de barra actual del FVG
|
| int fvgBarShift = iBarShift(g_chartSymbol, g_chartTimeframe, fvg.timeStart);
|
| if(fvgBarShift < 0) return;
|
|
|
| // Verificar todas las velas posteriores al FVG
|
| for(int i = fvgBarShift - 1; i >= 0; i--)
|
| {
|
| double high = iHigh(g_chartSymbol, g_chartTimeframe, i);
|
| double low = iLow(g_chartSymbol, g_chartTimeframe, i);
|
|
|
| if(fvg.isBullish)
|
| {
|
| // FVG alcista se llena cuando el precio baja hasta el nivel inferior
|
| if(low <= fvg.priceLow)
|
| {
|
| fvg.isFilled = true;
|
| fvg.timeEnd = iTime(g_chartSymbol, g_chartTimeframe, i);
|
| return;
|
| }
|
| }
|
| else
|
| {
|
| // FVG bajista se llena cuando el precio sube hasta el nivel superior
|
| if(high >= fvg.priceHigh)
|
| {
|
| fvg.isFilled = true;
|
| fvg.timeEnd = iTime(g_chartSymbol, g_chartTimeframe, i);
|
| return;
|
| }
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| AGREGAR FVG AL ARRAY DINÁMICO |
|
| //+------------------------------------------------------------------+
|
| void AddFVGToArray(FVG_Data &newFVG)
|
| {
|
| int newSize = g_totalFVG + 1;
|
| ArrayResize(g_fvgArray, newSize, 100);
|
| g_fvgArray[g_totalFVG] = newFVG;
|
| g_totalFVG = newSize;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| GENERAR NOMBRE ÚNICO PARA OBJETO FVG |
|
| //+------------------------------------------------------------------+
|
| string GenerateFVGObjectName(bool isBullish, int barIndex)
|
| {
|
| string type = isBullish ? "BULL_" : "BEAR_";
|
| datetime barTime = iTime(g_chartSymbol, g_chartTimeframe, barIndex);
|
| return InpObjectPrefix + type + IntegerToString((long)barTime);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| DIBUJAR TODOS LOS FVGs |
|
| //+------------------------------------------------------------------+
|
| void DrawAllFVGs()
|
| {
|
| for(int i = 0; i < g_totalFVG; i++)
|
| {
|
| DrawSingleFVG(g_fvgArray[i]);
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| DIBUJAR UN FVG INDIVIDUAL |
|
| //+------------------------------------------------------------------+
|
| void DrawSingleFVG(FVG_Data &fvg)
|
| {
|
| // Si está llenado y no queremos mostrar llenados, eliminar objeto existente
|
| if(fvg.isFilled && !InpShowFilledFVG)
|
| {
|
| if(ObjectFind(0, fvg.objectName) >= 0)
|
| {
|
| ObjectDelete(0, fvg.objectName);
|
| }
|
| return;
|
| }
|
|
|
| // Si ya existe el objeto y está llenado, eliminarlo si corresponde
|
| if(fvg.isFilled && InpDeleteOnFill && ObjectFind(0, fvg.objectName) >= 0)
|
| {
|
| ObjectDelete(0, fvg.objectName);
|
| return;
|
| }
|
|
|
| // Calcular tiempo de fin del rectángulo
|
| datetime endTime = CalculateFVGEndTime(fvg);
|
|
|
| // Obtener configuración visual según tipo
|
| color fvgColor;
|
| uchar opacity;
|
| int borderWidth;
|
| ENUM_LINE_STYLE borderStyle;
|
| bool fillRectangle;
|
|
|
| if(fvg.isBullish)
|
| {
|
| fvgColor = InpBullishColor;
|
| opacity = InpBullishOpacity;
|
| borderWidth = InpBullishWidth;
|
| borderStyle = (ENUM_LINE_STYLE)InpBullishStyle;
|
| fillRectangle = InpBullishFill;
|
| }
|
| else
|
| {
|
| fvgColor = InpBearishColor;
|
| opacity = InpBearishOpacity;
|
| borderWidth = InpBearishWidth;
|
| borderStyle = (ENUM_LINE_STYLE)InpBearishStyle;
|
| fillRectangle = InpBearishFill;
|
| }
|
|
|
| // Crear o actualizar el rectángulo
|
| if(ObjectFind(0, fvg.objectName) < 0)
|
| {
|
| // Crear nuevo objeto rectángulo
|
| if(!ObjectCreate(0, fvg.objectName, OBJ_RECTANGLE, 0,
|
| fvg.timeStart, fvg.priceHigh, endTime, fvg.priceLow))
|
| {
|
| Print("❌ Error al crear rectángulo FVG: ", GetLastError());
|
| return;
|
| }
|
| }
|
| else
|
| {
|
| // Actualizar coordenadas del objeto existente
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_TIME, 0, fvg.timeStart);
|
| ObjectSetDouble(0, fvg.objectName, OBJPROP_PRICE, 0, fvg.priceHigh);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_TIME, 1, endTime);
|
| ObjectSetDouble(0, fvg.objectName, OBJPROP_PRICE, 1, fvg.priceLow);
|
| }
|
|
|
| // Configurar propiedades visuales con color ARGB (usando función nativa)
|
| uint colorARGB = ColorToARGB(fvgColor, opacity);
|
|
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_COLOR, colorARGB);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_STYLE, borderStyle);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_WIDTH, borderWidth);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_FILL, fillRectangle);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_BACK, true);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, fvg.objectName, OBJPROP_HIDDEN, true);
|
|
|
| // Tooltip descriptivo
|
| string tooltip = StringFormat("%s FVG\nAlto: %s\nBajo: %s\nTamaño: %.1f pips%s",
|
| fvg.isBullish ? "📈 Alcista" : "📉 Bajista",
|
| DoubleToString(fvg.priceHigh, g_digits),
|
| DoubleToString(fvg.priceLow, g_digits),
|
| (fvg.priceHigh - fvg.priceLow) / g_pointValue / 10,
|
| fvg.isFilled ? "\n✓ LLENADO" : "");
|
| ObjectSetString(0, fvg.objectName, OBJPROP_TOOLTIP, tooltip);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| CALCULAR TIEMPO DE FIN DEL FVG |
|
| //+------------------------------------------------------------------+
|
| datetime CalculateFVGEndTime(FVG_Data &fvg)
|
| {
|
| if(fvg.isFilled)
|
| {
|
| return fvg.timeEnd;
|
| }
|
|
|
| switch(InpExtensionType)
|
| {
|
| case FVG_EXTEND_FIXED:
|
| {
|
| // Extensión fija: N velas desde el FVG
|
| int barShift = iBarShift(g_chartSymbol, g_chartTimeframe, fvg.timeStart);
|
| if(barShift < 0) barShift = 0;
|
|
|
| int targetBar = MathMax(0, barShift - InpFixedExtension);
|
| return iTime(g_chartSymbol, g_chartTimeframe, targetBar);
|
| }
|
|
|
| case FVG_EXTEND_UNTIL_FILL:
|
| default:
|
| {
|
| // Extender hasta el tiempo actual (última vela)
|
| return iTime(g_chartSymbol, g_chartTimeframe, 0);
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| VERIFICAR NUEVO FVG EN TIEMPO REAL |
|
| //+------------------------------------------------------------------+
|
| void CheckForNewFVG(int barIndex)
|
| {
|
| // Verificar que tenemos suficientes datos
|
| if(barIndex < 1) return;
|
|
|
| // Solo verificar si la vela en barIndex está completa
|
| // (barIndex debe ser al menos 1 para que la formación esté completa)
|
| CheckForFVGAtBar(barIndex + 1);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| ACTUALIZAR ESTADO DE FVGs (VERIFICAR SI FUERON LLENADOS) |
|
| //+------------------------------------------------------------------+
|
| void UpdateFVGStatus()
|
| {
|
| for(int i = 0; i < g_totalFVG; i++)
|
| {
|
| if(g_fvgArray[i].isFilled)
|
| continue;
|
|
|
| // Obtener precio actual
|
| double currentHigh = iHigh(g_chartSymbol, g_chartTimeframe, 0);
|
| double currentLow = iLow(g_chartSymbol, g_chartTimeframe, 0);
|
|
|
| if(g_fvgArray[i].isBullish)
|
| {
|
| // FVG alcista se llena si el precio baja hasta el nivel inferior
|
| if(currentLow <= g_fvgArray[i].priceLow)
|
| {
|
| g_fvgArray[i].isFilled = true;
|
| g_fvgArray[i].timeEnd = iTime(g_chartSymbol, g_chartTimeframe, 0);
|
| }
|
| }
|
| else
|
| {
|
| // FVG bajista se llena si el precio sube hasta el nivel superior
|
| if(currentHigh >= g_fvgArray[i].priceHigh)
|
| {
|
| g_fvgArray[i].isFilled = true;
|
| g_fvgArray[i].timeEnd = iTime(g_chartSymbol, g_chartTimeframe, 0);
|
| }
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| REDIBUJAR FVGs MODIFICADOS |
|
| //+------------------------------------------------------------------+
|
| void RedrawModifiedFVGs()
|
| {
|
| for(int i = 0; i < g_totalFVG; i++)
|
| {
|
| DrawSingleFVG(g_fvgArray[i]);
|
| }
|
|
|
| ChartRedraw(0);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| ACTUALIZAR EXTENSIONES DE FVGs |
|
| //+------------------------------------------------------------------+
|
| void UpdateFVGExtensions()
|
| {
|
| bool needsRedraw = false;
|
|
|
| for(int i = 0; i < g_totalFVG; i++)
|
| {
|
| if(!g_fvgArray[i].isFilled && InpExtensionType == FVG_EXTEND_UNTIL_FILL)
|
| {
|
| datetime newEndTime = iTime(g_chartSymbol, g_chartTimeframe, 0);
|
|
|
| if(ObjectFind(0, g_fvgArray[i].objectName) >= 0)
|
| {
|
| datetime currentEndTime = (datetime)ObjectGetInteger(0, g_fvgArray[i].objectName, OBJPROP_TIME, 1);
|
|
|
| if(currentEndTime != newEndTime)
|
| {
|
| ObjectSetInteger(0, g_fvgArray[i].objectName, OBJPROP_TIME, 1, newEndTime);
|
| needsRedraw = true;
|
| }
|
| }
|
| }
|
| }
|
|
|
| if(needsRedraw)
|
| {
|
| ChartRedraw(0);
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| FUNCIÓN PARA MANEJAR EVENTOS DEL GRÁFICO |
|
| //+------------------------------------------------------------------+
|
| void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
|
| {
|
| // Manejar cambio de escala o desplazamiento del gráfico
|
| if(id == CHARTEVENT_CHART_CHANGE)
|
| {
|
| // Redibujar FVGs para asegurar visualización correcta
|
| ChartRedraw(0);
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| FUNCIÓN PARA OBTENER ESTADÍSTICAS |
|
| //+------------------------------------------------------------------+
|
| string GetFVGStatistics()
|
| {
|
| int bullishCount = 0;
|
| int bearishCount = 0;
|
| int filledCount = 0;
|
| int activeCount = 0;
|
|
|
| for(int i = 0; i < g_totalFVG; i++)
|
| {
|
| if(g_fvgArray[i].isBullish)
|
| bullishCount++;
|
| else
|
| bearishCount++;
|
|
|
| if(g_fvgArray[i].isFilled)
|
| filledCount++;
|
| else
|
| activeCount++;
|
| }
|
|
|
| return StringFormat(
|
| "═══ ESTADÍSTICAS FVG ═══\n"
|
| "Total: %d\n"
|
| "Alcistas: %d\n"
|
| "Bajistas: %d\n"
|
| "Activos: %d\n"
|
| "Llenados: %d",
|
| g_totalFVG, bullishCount, bearishCount, activeCount, filledCount
|
| );
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| FIN DEL EXPERT ADVISOR |
|
| //+------------------------------------------------------------------+ |