ICTLibraryExamples/Benchmark/OtherCode/Code4.mq5
Nique_372 Nique_372 042de68f8b
2026-10-25 12:04:40 -05:00
//+------------------------------------------------------------------+
//| 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 <TSN\\MQLArticles\\Utils\\TFManager.mqh>
/* 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();"
}
}