208 lines
7.2 KiB
MQL5
208 lines
7.2 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| EA_FVG_Scanner.mq5 |
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Open Source"
|
|
#property version "1.00"
|
|
|
|
//--- Input Parameters for the Scanner Engine
|
|
input int InpHistoryDepth = 100; // Historical Bars to Evaluate
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "..\\Utils\\MetricSaver.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Class: CFVGScanner |
|
|
//| Purpose: Detects, draws, and manages Fair Value Gaps |
|
|
//+------------------------------------------------------------------+
|
|
class CFVGScanner
|
|
{
|
|
private:
|
|
string m_symbol; // Target symbol for the scanner
|
|
ENUM_TIMEFRAMES m_timeframe; // Target timeframe
|
|
int m_history_bars; // Depth of historical scan
|
|
string m_obj_prefix; // Prefix for graphical objects
|
|
|
|
color m_color_bullish; // Color for bullish imbalances
|
|
color m_color_bearish; // Color for bearish imbalances
|
|
|
|
void DrawRectangle(string name, datetime time1, double price1, datetime time2, double price2, color clr);
|
|
void ClearOldObjects(void);
|
|
|
|
public:
|
|
CFVGScanner(string symbol, ENUM_TIMEFRAMES tf, int depth);
|
|
~CFVGScanner(void);
|
|
|
|
void ScanAndDraw(void);
|
|
void SetColors(color bull, color bear);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor: Initializes the environment and routing variables |
|
|
//+------------------------------------------------------------------+
|
|
CFVGScanner::CFVGScanner(string symbol, ENUM_TIMEFRAMES tf, int depth)
|
|
{
|
|
m_symbol = (symbol == "") ? _Symbol : symbol;
|
|
m_timeframe = tf;
|
|
m_history_bars = (depth < 10) ? 10 : depth;
|
|
m_obj_prefix = "FVG_" + m_symbol + "_" + EnumToString(m_timeframe) + "_";
|
|
|
|
m_color_bullish = clrDarkGreen;
|
|
m_color_bearish = clrMaroon;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor: Systematic cleanup on terminal shutdown |
|
|
//+------------------------------------------------------------------+
|
|
CFVGScanner::~CFVGScanner(void)
|
|
{
|
|
ClearOldObjects();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Core Engine: Extracts data, evaluates geometry, and filters voids|
|
|
//+------------------------------------------------------------------+
|
|
void CFVGScanner::ScanAndDraw(void)
|
|
{
|
|
MqlRates rates[];
|
|
ArraySetAsSeries(rates, true);
|
|
|
|
if(CopyRates(m_symbol, m_timeframe, 0, m_history_bars, rates) < m_history_bars)
|
|
{
|
|
PrintFormat("Warning: Failed to synchronize %s history for FVG scanner.", m_symbol);
|
|
return;
|
|
}
|
|
|
|
ClearOldObjects();
|
|
|
|
for(int i = m_history_bars - 1; i >= 3; i--)
|
|
{
|
|
double origin_high = rates[i].high;
|
|
double origin_low = rates[i].low;
|
|
|
|
double confirm_high = rates[i - 2].high;
|
|
double confirm_low = rates[i - 2].low;
|
|
|
|
bool is_bullish_fvg = (origin_high < confirm_low);
|
|
bool is_bearish_fvg = (origin_low > confirm_high);
|
|
|
|
if(is_bullish_fvg)
|
|
{
|
|
bool is_mitigated = false;
|
|
for(int j = i - 3; j >= 1; j--)
|
|
{
|
|
if(rates[j].low <= origin_high)
|
|
{ is_mitigated = true; break; }
|
|
}
|
|
|
|
if(!is_mitigated)
|
|
{
|
|
string name = m_obj_prefix + "BULL_" + IntegerToString(i);
|
|
DrawRectangle(name, rates[i].time, confirm_low, rates[1].time, origin_high, m_color_bullish);
|
|
}
|
|
}
|
|
|
|
if(is_bearish_fvg)
|
|
{
|
|
bool is_mitigated = false;
|
|
for(int j = i - 3; j >= 1; j--)
|
|
{
|
|
if(rates[j].high >= origin_low)
|
|
{ is_mitigated = true; break; }
|
|
}
|
|
|
|
if(!is_mitigated)
|
|
{
|
|
string name = m_obj_prefix + "BEAR_" + IntegerToString(i);
|
|
DrawRectangle(name, rates[i].time, origin_low, rates[1].time, confirm_high, m_color_bearish);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Renders the institutional imbalance zone on the chart canvas |
|
|
//+------------------------------------------------------------------+
|
|
void CFVGScanner::DrawRectangle(string name, datetime time1, double price1, datetime time2, double price2, color clr)
|
|
{
|
|
if(ObjectFind(0, name) < 0)
|
|
{
|
|
ObjectCreate(0, name, OBJ_RECTANGLE, 0, time1, price1, time2, price2);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, false);
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
|
}
|
|
else
|
|
{
|
|
ObjectSetInteger(0, name, OBJPROP_TIME, 1, time2);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Prevents memory leaks by purging outdated graphical objects |
|
|
//+------------------------------------------------------------------+
|
|
void CFVGScanner::ClearOldObjects(void)
|
|
{
|
|
ObjectsDeleteAll(0, m_obj_prefix);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Public method to allow developers to customize the visual output |
|
|
//+------------------------------------------------------------------+
|
|
void CFVGScanner::SetColors(color bull, color bear)
|
|
{
|
|
m_color_bullish = bull;
|
|
m_color_bearish = bear;
|
|
}
|
|
|
|
//--- Global Pointer to the encapsulated scanner object
|
|
CFVGScanner *g_fvg_engine;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
CMetricsSave::Start(FILE_CODE7);
|
|
g_fvg_engine = new CFVGScanner(_Symbol, PERIOD_CURRENT, InpHistoryDepth);
|
|
g_fvg_engine.SetColors(clrLimeGreen, clrRed);
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
if(CheckPointer(g_fvg_engine) == POINTER_DYNAMIC)
|
|
{
|
|
delete g_fvg_engine;
|
|
}
|
|
CMetricsSave::Destroy();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function with strict CPU optimization |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
static datetime last_bar_time = 0;
|
|
datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
|
|
if(current_bar_time != last_bar_time)
|
|
{
|
|
last_bar_time = current_bar_time;
|
|
|
|
if(CheckPointer(g_fvg_engine) != POINTER_INVALID)
|
|
{
|
|
g_fvg_engine.ScanAndDraw();
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|