ICTLibraryEasy/Examples/Benchmark/OtherCode/Code6.mq5
2026-01-14 07:41:55 -05:00

745 lines
No EOL
56 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 |
//+------------------------------------------------------------------+