forked from nique_372/ICTLibraryEasy
626 lines
No EOL
42 KiB
MQL5
626 lines
No EOL
42 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| FVG_Detector_EA.mq5 |
|
|
//| Fair Value Gap Detection Tool |
|
|
//| Professional Smart Money Concepts Expert |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "FVG Detector"
|
|
#property version "1.00"
|
|
#property strict
|
|
#property description "High-performance FVG detector. Visualization only. No trading."
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "..\\Utils\\MetricSaver.mqh"
|
|
|
|
/* Codigo hecho por ia Claude Opus 4.7 (best ai today 25/05/2026)
|
|
Desarrollo de EA MT5 (MQL5) para detección y dibujo de FVG
|
|
Objetivo
|
|
|
|
Necesito un Expert Advisor (EA) para MetaTrader 5, desarrollado en MQL5, cuya única función sea detectar, dibujar y gestionar automáticamente los Fair Value Gaps (FVG) en el gráfico.
|
|
|
|
No debe abrir operaciones ni ejecutar lógica de trading. Debe comportarse como un sistema visual de detección, similar a un indicador, pero implementado como EA.
|
|
|
|
Requerimientos funcionales
|
|
1. Detección de FVG
|
|
|
|
El EA debe detectar automáticamente todos los Fair Value Gaps (FVG) que se vayan formando en el gráfico.
|
|
|
|
2. Dibujado visual
|
|
|
|
Cada FVG detectado debe representarse visualmente mediante:
|
|
|
|
Un rectángulo que cubra toda la zona del FVG.
|
|
Una línea horizontal central ubicada exactamente en el midpoint del gap (size / 2).
|
|
3. Mitigación
|
|
|
|
El EA debe detectar cuándo un FVG ha sido mitigado (precio toca o invalida la zona, segun esta logica simple:
|
|
para alcitas: top > low (rectangulo)
|
|
para bajistas: high > bottom (rectangulo))
|
|
|
|
Cuando esto ocurra:
|
|
|
|
El FVG debe marcarse como mitigado.
|
|
Debe eliminarse automáticamente del gráfico.
|
|
También deben eliminarse sus objetos asociados (línea central, rectángulo, etc.).
|
|
4. Multi-timeframe
|
|
|
|
Debe permitirme seleccionar el timeframe sobre el cual calcular los FVG, independientemente del timeframe del gráfico actual.
|
|
|
|
Ejemplo:
|
|
|
|
Calcular FVG en H1 mientras estoy viendo M5.
|
|
Parámetros configurables
|
|
|
|
El EA debe exponer inputs configurables para:
|
|
|
|
Timeframe
|
|
Timeframe de cálculo de FVG.
|
|
Apariencia visual
|
|
Color del rectángulo FVG bullish.
|
|
Color del rectángulo FVG bearish.
|
|
Color de la línea central.
|
|
Estilo de línea (ENUM_LINE_STYLE).
|
|
Grosor de línea.
|
|
Transparencia del rectángulo (si aplica).
|
|
Si mostrar u ocultar la línea central.
|
|
Comportamiento
|
|
Activar/desactivar eliminación automática al mitigarse.
|
|
Máximo número de FVG activos a mantener en memoria/gráfico.
|
|
Requisitos de rendimiento (MUY IMPORTANTE)
|
|
|
|
La prioridad principal es máxima velocidad y optimización.
|
|
|
|
Necesito que el EA esté diseñado con enfoque de alto rendimiento.
|
|
|
|
Reglas de optimización:
|
|
Debe ejecutarse solo en nueva vela (new bar detection).
|
|
No necesito procesamiento tick-by-tick.
|
|
Nada debe recalcularse en cada tick innecesariamente.
|
|
Debe minimizar:
|
|
uso de memoria,
|
|
llamadas innecesarias a funciones,
|
|
creación/destrucción excesiva de objetos,
|
|
búsquedas repetitivas en arrays o estructuras.
|
|
Reutilizar datos/cache siempre que sea posible.
|
|
Evitar loops completos sobre histórico innecesario.
|
|
Debe estar preparado para manejar muchos FVG sin degradar rendimiento.
|
|
Diseño enfocado en bajo overhead gráfico.
|
|
Arquitectura y código
|
|
|
|
Puedes elegir la arquitectura que consideres más eficiente:
|
|
|
|
Programación procedural
|
|
OOP / clases
|
|
structs
|
|
arrays raw
|
|
buffers
|
|
cualquier enfoque híbrido
|
|
|
|
La prioridad no es elegancia, sino MAXIMA velocidad posible y estabilidad.
|
|
|
|
Restricciones importantes
|
|
1. NO trading
|
|
|
|
El EA:
|
|
|
|
NO debe abrir órdenes.
|
|
NO debe cerrar órdenes.
|
|
NO debe modificar posiciones.
|
|
NO debe usar lógica de ejecución de mercado.
|
|
|
|
Solo visualización y gestión de FVG.
|
|
|
|
2. Logging / Debug
|
|
|
|
No quiero Print() innecesarios.
|
|
|
|
Opciones aceptables:
|
|
|
|
Sin logs.
|
|
O logs opcionales mediante #ifdef DEBUG.
|
|
|
|
La build final debe quedar limpia y SUPER RAPIDA.
|
|
|
|
3. Archivo único
|
|
|
|
Necesito recibir todo en un solo archivo .mq5 de tipo Expert Advisor.
|
|
|
|
No usar:
|
|
|
|
.mqh externos
|
|
includes personalizados
|
|
archivos auxiliares
|
|
librerías separadas
|
|
|
|
Todo debe estar autocontenido en un único EA.
|
|
|
|
Buenas prácticas esperadas
|
|
|
|
Quiero que uses buenas prácticas de MQL5 orientadas a performance:
|
|
|
|
manejo correcto de OnInit()
|
|
limpieza en OnDeinit()
|
|
ejecución optimizada en OnTick()
|
|
detección eficiente de nueva vela
|
|
naming consistente de objetos
|
|
eliminación segura de objetos
|
|
evitar memory leaks
|
|
control de duplicados de FVG
|
|
no redibujar objetos existentes
|
|
gestión robusta de arrays/estructuras
|
|
Entregable
|
|
|
|
Genera el código completo de un EA en MQL5 (.mq5) que cumpla todo lo anterior.
|
|
|
|
Debe ser:
|
|
|
|
estable,
|
|
altamente optimizado,
|
|
fácil de mantener,
|
|
rápido incluso con muchos FVG,
|
|
y centrado exclusivamente en dibujo/gestión de FVG.
|
|
|
|
Empieza directamente generando el código completo del EA.
|
|
*/
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Inputs |
|
|
//+------------------------------------------------------------------+
|
|
input group "=== Timeframe ==="
|
|
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Timeframe de cálculo
|
|
|
|
input group "=== Apariencia Visual ==="
|
|
input color InpBullColor = clrDodgerBlue; // Color FVG alcista
|
|
input color InpBearColor = clrOrangeRed; // Color FVG bajista
|
|
input color InpMidLineColor = clrGold; // Color línea central
|
|
input ENUM_LINE_STYLE InpLineStyle = STYLE_DOT; // Estilo de línea
|
|
input int InpLineWidth = 1; // Grosor de línea
|
|
input bool InpFillRectangle = true; // Rellenar rectángulo
|
|
input bool InpRectBack = true; // Rectángulo en fondo
|
|
input bool InpShowMidLine = true; // Mostrar línea central
|
|
|
|
input group "=== Comportamiento ==="
|
|
input bool InpRemoveOnMitig = true; // Eliminar al mitigarse
|
|
input int InpMaxActiveFVG = 200; // Máx. FVG activos
|
|
input int InpScanInitialBars = 500; // Barras a escanear al inicio
|
|
|
|
input group "=== Identificación ==="
|
|
input string InpObjPrefix = "FVG_"; // Prefijo objetos
|
|
input long InpMagic = 20260101; // Identificador único
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Estructura compacta para FVG activo |
|
|
//+------------------------------------------------------------------+
|
|
struct FVG
|
|
{
|
|
double top; // Borde superior del gap
|
|
double bottom; // Borde inferior del gap
|
|
double mid; // Punto medio precalculado
|
|
datetime time_start; // Tiempo barra inicio del rectángulo
|
|
datetime time_end; // Tiempo barra fin (se extiende)
|
|
long id; // ID único
|
|
bool bullish; // true=alcista, false=bajista
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Variables globales |
|
|
//+------------------------------------------------------------------+
|
|
FVG g_fvgs[]; // Array dinámico de FVG activos
|
|
int g_fvg_count = 0; // Conteo lógico (puede ser <= ArraySize)
|
|
long g_next_id = 0; // Generador de IDs
|
|
datetime g_last_bar_time = 0; // Para detección de nueva vela
|
|
string g_prefix; // Prefijo cacheado
|
|
int g_prefix_len = 0; // Longitud cacheada
|
|
ENUM_TIMEFRAMES g_tf; // Timeframe efectivo
|
|
long g_chart_id = 0; // Chart ID cacheado
|
|
|
|
|
|
// CORRECION por mi parte (LEO) no se puede hacer esto asi que hare el array dinamico no hay de otra...
|
|
// Los arrays estaticos no pueden ser set "set as series"
|
|
// Buffers reutilizables para evitar reallocs en cada nueva barra
|
|
MqlRates g_rates[]; // Solo necesitamos 3 barras para detectar FVG
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Helpers de naming (inline) |
|
|
//+------------------------------------------------------------------+
|
|
string MakeRectName(const long id)
|
|
{
|
|
return g_prefix + "R_" + IntegerToString(id);
|
|
}
|
|
string MakeLineName(const long id)
|
|
{
|
|
return g_prefix + "L_" + IntegerToString(id);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Crear objetos gráficos para un FVG |
|
|
//+------------------------------------------------------------------+
|
|
void DrawFVG(const FVG &f)
|
|
{
|
|
const string rname = MakeRectName(f.id);
|
|
const color col = f.bullish ? InpBullColor : InpBearColor;
|
|
|
|
if(ObjectCreate(g_chart_id, rname, OBJ_RECTANGLE, 0, f.time_start, f.top, f.time_end, f.bottom))
|
|
{
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_COLOR, col);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_FILL, InpFillRectangle);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_BACK, InpRectBack);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_SELECTABLE,false);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_SELECTED, false);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_HIDDEN, true);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_STYLE, STYLE_SOLID);
|
|
}
|
|
|
|
if(InpShowMidLine)
|
|
{
|
|
const string lname = MakeLineName(f.id);
|
|
if(ObjectCreate(g_chart_id, lname, OBJ_TREND, 0, f.time_start, f.mid, f.time_end, f.mid))
|
|
{
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_COLOR, InpMidLineColor);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_STYLE, InpLineStyle);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_WIDTH, InpLineWidth);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_RAY_RIGHT, false);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_RAY_LEFT, false);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_SELECTED, false);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_HIDDEN, true);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_BACK, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Extender el tiempo final del rectángulo y la línea |
|
|
//+------------------------------------------------------------------+
|
|
void ExtendFVG(const FVG &f, const datetime new_end)
|
|
{
|
|
const string rname = MakeRectName(f.id);
|
|
ObjectSetInteger(g_chart_id, rname, OBJPROP_TIME, 1, new_end);
|
|
|
|
if(InpShowMidLine)
|
|
{
|
|
const string lname = MakeLineName(f.id);
|
|
ObjectSetInteger(g_chart_id, lname, OBJPROP_TIME, 1, new_end);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Eliminar objetos gráficos asociados a un FVG |
|
|
//+------------------------------------------------------------------+
|
|
void DeleteFVGObjects(const long id)
|
|
{
|
|
ObjectDelete(g_chart_id, MakeRectName(id));
|
|
if(InpShowMidLine)
|
|
ObjectDelete(g_chart_id, MakeLineName(id));
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Remover FVG del array por índice (swap con último, O(1)) |
|
|
//+------------------------------------------------------------------+
|
|
void RemoveFVGAt(const int idx)
|
|
{
|
|
const int last = g_fvg_count - 1;
|
|
if(idx != last)
|
|
g_fvgs[idx] = g_fvgs[last];
|
|
g_fvg_count--;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Eliminar todos los FVG y limpiar gráfico |
|
|
//+------------------------------------------------------------------+
|
|
void ClearAllFVG()
|
|
{
|
|
for(int i = 0; i < g_fvg_count; i++)
|
|
DeleteFVGObjects(g_fvgs[i].id);
|
|
g_fvg_count = 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Eliminar TODOS los objetos del EA del gráfico (limpieza total) |
|
|
//+------------------------------------------------------------------+
|
|
void CleanupChartObjects()
|
|
{
|
|
ObjectsDeleteAll(g_chart_id, g_prefix);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Agregar un nuevo FVG al array (gestionando capacidad y máximo) |
|
|
//+------------------------------------------------------------------+
|
|
void AddFVG(const double top, const double bottom, const datetime ts,
|
|
const datetime te, const bool bullish)
|
|
{
|
|
// Si se alcanza el máximo, eliminar el más antiguo (índice 0)
|
|
if(g_fvg_count >= InpMaxActiveFVG)
|
|
{
|
|
DeleteFVGObjects(g_fvgs[0].id);
|
|
// Desplazar (shift) — solo cuando llegamos al límite, no es hot path
|
|
for(int i = 1; i < g_fvg_count; i++)
|
|
g_fvgs[i - 1] = g_fvgs[i];
|
|
g_fvg_count--;
|
|
}
|
|
|
|
// Asegurar capacidad sin reallocar en cada inserción
|
|
if(ArraySize(g_fvgs) <= g_fvg_count)
|
|
{
|
|
int new_size = ArraySize(g_fvgs) * 2;
|
|
if(new_size < 64) new_size = 64;
|
|
if(new_size > InpMaxActiveFVG) new_size = InpMaxActiveFVG;
|
|
ArrayResize(g_fvgs, new_size);
|
|
}
|
|
|
|
FVG f;
|
|
f.top = top;
|
|
f.bottom = bottom;
|
|
f.mid = (top + bottom) * 0.5;
|
|
f.time_start = ts;
|
|
f.time_end = te;
|
|
f.id = g_next_id++;
|
|
f.bullish = bullish;
|
|
|
|
g_fvgs[g_fvg_count++] = f;
|
|
DrawFVG(f);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Comprobar mitigación de TODOS los FVG con la barra recién cerrada|
|
|
//| Lógica solicitada: |
|
|
//| alcista: bar.low <= fvg.top (precio entró desde arriba) |
|
|
//| bajista: bar.high >= fvg.bottom (precio entró desde abajo) |
|
|
//+------------------------------------------------------------------+
|
|
void CheckMitigation(const double bar_high, const double bar_low)
|
|
{
|
|
if(!InpRemoveOnMitig)
|
|
return;
|
|
|
|
// Recorrido inverso para borrar in-place con swap
|
|
for(int i = g_fvg_count - 1; i >= 0; i--)
|
|
{
|
|
const FVG f = g_fvgs[i];
|
|
bool mitigated = false;
|
|
|
|
if(f.bullish)
|
|
{
|
|
// FVG alcista: mitigado si el low de la barra entra en el gap
|
|
if(bar_low <= f.top)
|
|
mitigated = true;
|
|
}
|
|
else
|
|
{
|
|
// FVG bajista: mitigado si el high de la barra entra en el gap
|
|
if(bar_high >= f.bottom)
|
|
mitigated = true;
|
|
}
|
|
|
|
if(mitigated)
|
|
{
|
|
DeleteFVGObjects(f.id);
|
|
RemoveFVGAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Extender el time_end de todos los FVG activos a la barra actual |
|
|
//+------------------------------------------------------------------+
|
|
void ExtendAllFVG(const datetime new_end)
|
|
{
|
|
for(int i = 0; i < g_fvg_count; i++)
|
|
{
|
|
if(g_fvgs[i].time_end != new_end)
|
|
{
|
|
g_fvgs[i].time_end = new_end;
|
|
ExtendFVG(g_fvgs[i], new_end);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Escaneo inicial de N barras históricas para sembrar FVG activos |
|
|
//+------------------------------------------------------------------+
|
|
void InitialScan()
|
|
{
|
|
if(InpScanInitialBars < 3)
|
|
return;
|
|
|
|
int bars_avail = Bars(_Symbol, g_tf);
|
|
if(bars_avail < 3)
|
|
return;
|
|
|
|
int to_scan = InpScanInitialBars;
|
|
if(to_scan > bars_avail - 1)
|
|
to_scan = bars_avail - 1;
|
|
|
|
MqlRates rates[];
|
|
ArraySetAsSeries(rates, true);
|
|
int copied = CopyRates(_Symbol, g_tf, 1, to_scan, rates); // descartamos barra en formación
|
|
if(copied < 3)
|
|
return;
|
|
|
|
// rates[0] = barra cerrada más reciente
|
|
// Recorremos desde la más antigua a la más reciente para mantener orden cronológico de IDs
|
|
// Triple cronológico: oldest=rates[k+2], middle=rates[k+1], newest=rates[k]
|
|
const datetime extend_to = rates[0].time;
|
|
|
|
for(int k = copied - 3; k >= 0; k--)
|
|
{
|
|
const MqlRates b0 = rates[k]; // newest del triple
|
|
const MqlRates b1 = rates[k + 1]; // middle
|
|
const MqlRates b2 = rates[k + 2]; // oldest
|
|
|
|
bool bullish = false, bearish = false;
|
|
double top = 0, bottom = 0;
|
|
|
|
if(b0.low > b2.high)
|
|
{
|
|
bullish = true;
|
|
top = b0.low;
|
|
bottom = b2.high;
|
|
}
|
|
else if(b0.high < b2.low)
|
|
{
|
|
bearish = true;
|
|
top = b2.low;
|
|
bottom = b0.high;
|
|
}
|
|
|
|
if(!bullish && !bearish)
|
|
continue;
|
|
|
|
// Verificar si ya está mitigado por alguna barra posterior (entre b0 y la barra actual)
|
|
// Si está mitigado, no lo añadimos.
|
|
bool already_mitigated = false;
|
|
for(int m = k - 1; m >= 0; m--)
|
|
{
|
|
if(bullish)
|
|
{
|
|
if(rates[m].low <= top)
|
|
{
|
|
already_mitigated = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(rates[m].high >= bottom)
|
|
{
|
|
already_mitigated = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(already_mitigated)
|
|
continue;
|
|
|
|
AddFVG(top, bottom, b1.time, extend_to, bullish);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Procesamiento de nueva barra cerrada |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessNewBar()
|
|
{
|
|
// Necesitamos las 3 últimas barras CERRADAS: índices 1, 2, 3 (0 = en formación)
|
|
// Pero como solo procesamos en nueva vela, también validamos mitigación con la
|
|
// recién cerrada (índice 1).
|
|
|
|
|
|
ArraySetAsSeries(g_rates, true);
|
|
int copied = CopyRates(_Symbol, g_tf, 1, 3, g_rates);
|
|
if(copied < 3)
|
|
return;
|
|
|
|
// g_rates[0] = barra recién cerrada (la más reciente cerrada)
|
|
// g_rates[1] = anterior
|
|
// g_rates[2] = penúltima anterior
|
|
const MqlRates b0 = g_rates[0]; // newest closed
|
|
const MqlRates b1 = g_rates[1];
|
|
const MqlRates b2 = g_rates[2];
|
|
|
|
// 1) Comprobar mitigación con la barra recién cerrada
|
|
CheckMitigation(b0.high, b0.low);
|
|
|
|
// 2) Extender visualmente todos los FVG activos hasta el tiempo de la barra recién cerrada
|
|
ExtendAllFVG(b0.time);
|
|
|
|
// 3) Detectar nuevo FVG con el triple (b2, b1, b0) cronológico
|
|
bool bullish = false, bearish = false;
|
|
double top = 0, bottom = 0;
|
|
|
|
if(b0.low > b2.high)
|
|
{
|
|
bullish = true;
|
|
top = b0.low;
|
|
bottom = b2.high;
|
|
}
|
|
else if(b0.high < b2.low)
|
|
{
|
|
bearish = true;
|
|
top = b2.low;
|
|
bottom = b0.high;
|
|
}
|
|
|
|
if(bullish || bearish)
|
|
AddFVG(top, bottom, b1.time, b0.time, bullish);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Detección eficiente de nueva vela (en el TF de cálculo) |
|
|
//+------------------------------------------------------------------+
|
|
bool IsNewBar()
|
|
{
|
|
datetime t[1];
|
|
if(CopyTime(_Symbol, g_tf, 0, 1, t) != 1)
|
|
return false;
|
|
if(t[0] != g_last_bar_time)
|
|
{
|
|
g_last_bar_time = t[0];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnInit |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
CMetricsSave::Start(FILE_CODE6);
|
|
|
|
g_chart_id = ChartID();
|
|
g_tf = (InpTimeframe == PERIOD_CURRENT) ? (ENUM_TIMEFRAMES)Period() : InpTimeframe;
|
|
g_prefix = InpObjPrefix + IntegerToString(InpMagic) + "_";
|
|
g_prefix_len= StringLen(g_prefix);
|
|
|
|
// Reservar capacidad inicial razonable para evitar reallocaciones tempranas
|
|
int initial_cap = 64;
|
|
if(initial_cap > InpMaxActiveFVG) initial_cap = InpMaxActiveFVG;
|
|
ArrayResize(g_fvgs, initial_cap);
|
|
g_fvg_count = 0;
|
|
g_next_id = 0;
|
|
|
|
// Limpieza de objetos antiguos por seguridad
|
|
CleanupChartObjects();
|
|
|
|
// Inicializar última barra para evitar disparar en el primer tick
|
|
datetime t[1];
|
|
if(CopyTime(_Symbol, g_tf, 0, 1, t) == 1)
|
|
g_last_bar_time = t[0];
|
|
|
|
// Escaneo histórico inicial (siembra)
|
|
InitialScan();
|
|
|
|
ChartRedraw(g_chart_id);
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnDeinit |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Liberar gráficamente todos los objetos creados
|
|
CleanupChartObjects();
|
|
|
|
// Liberar memoria del array
|
|
ArrayFree(g_fvgs);
|
|
g_fvg_count = 0;
|
|
|
|
ChartRedraw(g_chart_id);
|
|
CMetricsSave::Destroy();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnTick — solo trabaja en nueva barra |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
if(!IsNewBar())
|
|
return;
|
|
|
|
ProcessNewBar();
|
|
ChartRedraw(g_chart_id);
|
|
} |