745 lines
No EOL
56 KiB
MQL5
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 |
|
|
//+------------------------------------------------------------------+ |