//+------------------------------------------------------------------+ //| FVG_Detector.mq5 | //| Expert Advisor para MT5 | //| Detección y Visualización de Fair Value Gaps| //+------------------------------------------------------------------+ #property copyright "FVG Visual Detector" #property link "" #property version "1.00" #property description "Expert Advisor exclusivo para detectar y dibujar Fair Value Gaps" #property description "NO ejecuta trades - Solo análisis visual" #property strict //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\Utils\\MetricSaver.mqh" #include /* Codigo hecho por ia Claude Sonnet 4.6 Promt: Desarrolla un BOT (Expert Advisor) para MetaTrader 5 (MT5) que EXCLUSIVAMENTE detecte y dibuje Fair Value Gaps (FVG) en el gráfico. ⚠️ Este BOT NO debe ejecutar operaciones, NO debe abrir/cerrar trades y NO debe funcionar como indicador. Su única función es análisis visual mediante dibujo en el gráfico . Requisitos técnicos obligatorios: Implementado como Expert Advisor (EA) en MQL5. Detección y dibujo de FVG alcistas y bajistas. Funcionamiento rápido, eficiente y altamente optimizado. Capaz de trabajar con datos históricos y en tiempo real. Configuración personalizable desde el panel del BOT: Colores de los FVG. Opacidad / transparencia. Grosor y estilo de los rectángulos. Activar o desactivar FVG alcistas o bajistas. Timeframe de análisis. Aspectos visuales: Los FVG deben dibujarse como zonas (rectángulos) claras y limpias. Evitar redibujados innecesarios. El BOT debe actualizar los FVG solo cuando sea necesario. Buenas prácticas de desarrollo: Código limpio, modular y bien comentado. Uso eficiente de OnInit, OnTick y manejo de objetos gráficos. Optimización de loops y cálculos para mínimo consumo de CPU. Usa todo tu conocimiento avanzado en MQL5, optimización y Smart Money Concepts para crear un BOT profesional, estable y rápido, enfocado únicamente en dibujar Fair Value Gaps. */ //+------------------------------------------------------------------+ //| INPUTS - Configuración Personalizable | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| CFvgFast.mqh — Fair Value Gap · Zero-alloc · Flat arrays | //| Mismo estilo que CFvgBar: Create / Set / SetGraphic / OnNewBar | //| Sin clases internas · Sin new/delete · Arrays estáticos | //+------------------------------------------------------------------+ #property strict #ifndef FVG_MAX #define FVG_MAX 200 #endif //────────────────────────────────────────────────────────────────── // Zona FVG — 40 bytes, sin strings dinámicos //────────────────────────────────────────────────────────────────── struct FVGZone { double hi; double lo; datetime t; bool active; bool bullish; }; //────────────────────────────────────────────────────────────────── // CFvgFast //────────────────────────────────────────────────────────────────── class CFvgFast { private: //--- arrays planos en el heap del objeto (no BSS, pero un solo alloc en Create) FVGZone m_bull[FVG_MAX]; FVGZone m_bear[FVG_MAX]; int m_bull_n; int m_bear_n; int m_bull_head; // circular overwrite int m_bear_head; long m_bull_total; // total insertados (para saber si circular) long m_bear_total; //--- nearest (actualizado 1x barra, leído N veces por EA) int m_near_bull; int m_near_bear; //--- config string m_symbol; ENUM_TIMEFRAMES m_tf; string m_prefix; int m_lookback; double m_min_gap; // en precio, ya convertido bool m_draw; //--- visual color m_col_bull; color m_col_bear; ENUM_LINE_STYLE m_style; bool m_fill; int m_border_w; bool m_extend; int m_extend_bars; //──────── helpers privados ──────── string _Name(bool bull, int idx) { return m_prefix + (bull ? "B" : "S") + IntegerToString(idx); } void _DrawZone(int idx, bool bull) { if(!m_draw) return; FVGZone z = bull ? m_bull[idx] : m_bear[idx]; if(!z.active) return; string nm = _Name(bull, idx); color c = bull ? m_col_bull : m_col_bear; datetime t1 = z.t; datetime t2 = m_extend ? z.t + (datetime)PeriodSeconds(m_tf) * m_extend_bars : 0; if(ObjectFind(0, nm) < 0) ObjectCreate(0, nm, OBJ_RECTANGLE, 0, t1, z.hi, t2, z.lo); else { ObjectSetInteger(0, nm, OBJPROP_TIME, 0, t1); ObjectSetInteger(0, nm, OBJPROP_TIME, 1, t2); ObjectSetDouble (0, nm, OBJPROP_PRICE, 0, z.hi); ObjectSetDouble (0, nm, OBJPROP_PRICE, 1, z.lo); } ObjectSetInteger(0, nm, OBJPROP_COLOR, c); ObjectSetInteger(0, nm, OBJPROP_STYLE, m_style); ObjectSetInteger(0, nm, OBJPROP_WIDTH, m_border_w); ObjectSetInteger(0, nm, OBJPROP_FILL, m_fill); ObjectSetInteger(0, nm, OBJPROP_BACK, true); ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true); } void _DeleteZone(int idx, bool bull) { if(!m_draw) return; ObjectDelete(0, _Name(bull, idx)); } void _Insert(const FVGZone &z) { if(z.bullish) { int i = m_bull_head; if(m_bull_total >= FVG_MAX) _DeleteZone(i, true); m_bull[i] = z; _DrawZone(i, true); m_bull_head = (m_bull_head + 1) % FVG_MAX; if(m_bull_n < FVG_MAX) m_bull_n++; m_bull_total++; } else { int i = m_bear_head; if(m_bear_total >= FVG_MAX) _DeleteZone(i, false); m_bear[i] = z; _DrawZone(i, false); m_bear_head = (m_bear_head + 1) % FVG_MAX; if(m_bear_n < FVG_MAX) m_bear_n++; m_bear_total++; } } //--- Detección O(1): lee 3 velas, evalúa gap void _Detect(int shift) { double hi[3], lo[3]; datetime t[1]; if(CopyHigh(m_symbol, m_tf, shift, 3, hi) < 3) return; if(CopyLow (m_symbol, m_tf, shift, 3, lo) < 3) return; if(CopyTime(m_symbol, m_tf, shift+1, 1, t) < 1) return; //--- Bull FVG: low[0] > high[2] if(lo[0] > hi[2] && (lo[0] - hi[2]) >= m_min_gap) { FVGZone z; z.hi = lo[0]; z.lo = hi[2]; z.t = t[0]; z.active = true; z.bullish = true; _Insert(z); } //--- Bear FVG: high[0] < low[2] if(hi[0] < lo[2] && (lo[2] - hi[0]) >= m_min_gap) { FVGZone z; z.hi = lo[2]; z.lo = hi[0]; z.t = t[0]; z.active = true; z.bullish = false; _Insert(z); } } //--- Mitigación O(n), solo comparaciones de double void _Mitigate(double price) { int n = (int)MathMin(m_bull_n, FVG_MAX); for(int i = 0; i < n; i++) { if(!m_bull[i].active) continue; if(price <= m_bull[i].hi && price >= m_bull[i].lo) { m_bull[i].active = false; _DeleteZone(i, true); } } n = (int)MathMin(m_bear_n, FVG_MAX); for(int i = 0; i < n; i++) { if(!m_bear[i].active) continue; if(price >= m_bear[i].lo && price <= m_bear[i].hi) { m_bear[i].active = false; _DeleteZone(i, false); } } } //--- Nearest O(n), 1x barra void _UpdateNearest(double price) { double best_b = DBL_MAX, best_s = DBL_MAX; m_near_bull = -1; m_near_bear = -1; int n = (int)MathMin(m_bull_n, FVG_MAX); for(int i = 0; i < n; i++) { if(!m_bull[i].active) continue; double d = MathAbs(price - (m_bull[i].hi + m_bull[i].lo) * 0.5); if(d < best_b) { best_b = d; m_near_bull = i; } } n = (int)MathMin(m_bear_n, FVG_MAX); for(int i = 0; i < n; i++) { if(!m_bear[i].active) continue; double d = MathAbs(price - (m_bear[i].hi + m_bear[i].lo) * 0.5); if(d < best_s) { best_s = d; m_near_bear = i; } } } public: CFvgFast() : m_bull_n(0), m_bear_n(0), m_bull_head(0), m_bear_head(0), m_bull_total(0), m_bear_total(0), m_near_bull(-1), m_near_bear(-1), m_lookback(500), m_min_gap(0.0), m_draw(true), m_fill(true), m_col_bull(clrLimeGreen), m_col_bear(clrCrimson), m_style(STYLE_SOLID), m_border_w(1), m_extend(true), m_extend_bars(40), m_prefix("FVG_") {} //────────────────────────────────────────────────────────────── // API pública — mismo estilo que CFvgBar //────────────────────────────────────────────────────────────── //--- 1. Create: asigna símbolo y timeframe void Create(string symbol, ENUM_TIMEFRAMES tf) { m_symbol = symbol; m_tf = tf; } //--- 2. SetGraphic: visual (equivalente a fvg.SetGrapich(...)) void SetGraphic(color col_bull = clrLimeGreen, color col_bear = clrCrimson, ENUM_LINE_STYLE style = STYLE_SOLID, bool fill = true, int border_w = 1, bool extend = true, int extend_bars = 40, string prefix = "FVG_") { m_col_bull = col_bull; m_col_bear = col_bear; m_style = style; m_fill = fill; m_border_w = border_w; m_extend = extend; m_extend_bars = extend_bars; m_prefix = prefix; } //--- 3. Set: config lógica + arranca historial // min_gap_points: 0 = sin filtro void Set(int lookback = 500, double min_gap_pts = 0.0, bool draw = true) { m_draw = draw; m_lookback = lookback; m_min_gap = min_gap_pts * SymbolInfoDouble(m_symbol, SYMBOL_POINT); // Reset m_bull_n = m_bear_n = 0; m_bull_head = m_bear_head = 0; m_bull_total = m_bear_total = 0; m_near_bull = m_near_bear = -1; // Limpiar objetos anteriores for(int i = ObjectsTotal(0) - 1; i >= 0; i--) { string nm = ObjectName(0, i); if(StringFind(nm, m_prefix) == 0) ObjectDelete(0, nm); } // Carga histórica int bars = MathMin(m_lookback, Bars(m_symbol, m_tf) - 3); for(int s = bars; s >= 1; s--) _Detect(s); double price = SymbolInfoDouble(m_symbol, SYMBOL_BID); _Mitigate(price); _UpdateNearest(price); } //--- 4. OnNewBar: llama en cada cierre de vela void OnNewBar() { _Detect(1); double price = SymbolInfoDouble(m_symbol, SYMBOL_BID); _Mitigate(price); _UpdateNearest(price); } //--- 5. OnTick (opcional): solo mitigación intra-barra void OnTick() { double price = SymbolInfoDouble(m_symbol, SYMBOL_BID); _Mitigate(price); } //--- 6. Deinit void Deinit() { for(int i = ObjectsTotal(0) - 1; i >= 0; i--) { string nm = ObjectName(0, i); if(StringFind(nm, m_prefix) == 0) ObjectDelete(0, nm); } } //--- Acceso al nearest (lectura O(1)) bool GetNearestBull(FVGZone &out) { if(m_near_bull < 0) return false; out = m_bull[m_near_bull]; return out.active; } bool GetNearestBear(FVGZone &out) { if(m_near_bear < 0) return false; out = m_bear[m_near_bear]; return out.active; } bool IsInsideBull(double price) { if(m_near_bull < 0) return false; return m_bull[m_near_bull].active && price >= m_bull[m_near_bull].lo && price <= m_bull[m_near_bull].hi; } bool IsInsideBear(double price) { if(m_near_bear < 0) return false; return m_bear[m_near_bear].active && price >= m_bear[m_near_bear].lo && price <= m_bear[m_near_bear].hi; } int CountBull() const { return m_bull_n; } int CountBear() const { return m_bear_n; } }; //+------------------------------------------------------------------+ int8_t idx_ctrl_d1 = INVALID_INDEX; int8_t idx_ctrl_curr = INVALID_INDEX; CFvgFast fvg; // ← mismo estilo que tu "CFvgBar fvg;" TSN::CNewBarManager g_new_bar_manager; //+------------------------------------------------------------------+ int OnInit() { CMetricsSave::Start(FILE_CODE4); g_new_bar_manager.GetPosExecute(PERIOD_D1, idx_ctrl_d1); g_new_bar_manager.GetPosExecute(_Period, idx_ctrl_curr); //--- 1. Create fvg.Create(_Symbol, _Period); //--- 2. SetGraphic (colores, estilo, extensión) fvg.SetGraphic( clrLimeGreen, // bull clrCrimson, // bear STYLE_SOLID, // borde true, // fill 1, // border width true, // extend 40, // barras extensión "FVG_" // prefijo ); //--- 3. Set (lookback, min gap en puntos, dibujar) fvg.Set(500, 0.0, true); Print("Init OK | Bull: ", fvg.CountBull(), " | Bear: ", fvg.CountBear()); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { fvg.Deinit(); CMetricsSave::Destroy(); } //+------------------------------------------------------------------+ void OnTick() { const datetime curr_time = TimeCurrent(); g_new_bar_manager.Execute(curr_time); if(CNewBarManager_IsNewBarM1(g_new_bar_manager) && CNewBarManager_IsNewBar(g_new_bar_manager, idx_ctrl_curr)) { const bool new_day = CNewBarManager_IsNewBar(g_new_bar_manager, idx_ctrl_d1); fvg.OnNewBar(); // ← idéntico a tu "fvg.OnNewBar();" } }