Structuring code into files

This commit is contained in:
Bell
2026-03-10 13:47:42 +07:00
parent 3591694c96
commit 1ddb802480
22 changed files with 1766 additions and 1416 deletions
+242
View File
@@ -0,0 +1,242 @@
#ifndef EA_ICT_CL__SIGNALS_BOS_FVG_OB_MQH
#define EA_ICT_CL__SIGNALS_BOS_FVG_OB_MQH
// Module: Signals (FVG)
// Extracted from EA_ICT_CL.mq5 (Sections 5, 6, 7, 8).
// FVG helpers, scan/register, status update, best selector.
// NOTE: Uses EA globals (g_*) and inputs. Include AFTER globals exist.
//+------------------------------------------------------------------+
//| SECTION 5 FVG HELPERS |
//+------------------------------------------------------------------+
inline bool IsCandleStrong(ENUM_TIMEFRAMES tf, int i)
{
double h = iHigh(_Symbol, tf, i), l = iLow(_Symbol, tf, i);
double o = iOpen(_Symbol, tf, i), c = iClose(_Symbol, tf, i);
double range = h - l;
if (range < _Point) return false;
return (MathAbs(c - o) / range * 100.0) >= InpFVGMinBodyPct;
}
inline bool IsFVGInPool(datetime created)
{
for (int j = 0; j < g_FVGCount; j++)
if (g_FVGPool[j].createdTime == created) return true;
return false;
}
//+------------------------------------------------------------------+
//| SECTION 6 FVG POOL: SCAN & REGISTER |
//+------------------------------------------------------------------+
inline void ScanAndRegisterFVGs()
{
static datetime s_lastScan = 0;
datetime t0 = iTime(_Symbol, InpMiddleTF, 0);
if (t0 == s_lastScan) return;
s_lastScan = t0;
MarketDir dir = g_MiddleTrend.trend;
if (dir == DIR_NONE) return;
int maxBar = MathMin(InpFVGScanBars, Bars(_Symbol, InpMiddleTF) - 2);
for (int i = 2; i <= maxBar; i++)
{
double leftH = iHigh (_Symbol, InpMiddleTF, i + 1);
double leftL = iLow (_Symbol, InpMiddleTF, i + 1);
double rightH = iHigh (_Symbol, InpMiddleTF, i - 1);
double rightL = iLow (_Symbol, InpMiddleTF, i - 1);
double midO = iOpen (_Symbol, InpMiddleTF, i);
double midC = iClose(_Symbol, InpMiddleTF, i);
double gH = 0, gL = 0;
if (dir == DIR_UP)
{
if (leftH >= rightL) continue;
if (midC <= midO) continue;
if (!IsCandleStrong(InpMiddleTF, i)) continue;
gL = leftH; gH = rightL;
}
else
{
if (leftL <= rightH) continue;
if (midC >= midO) continue;
if (!IsCandleStrong(InpMiddleTF, i)) continue;
gH = leftL; gL = rightH;
}
datetime created = iTime(_Symbol, InpMiddleTF, i - 1);
if (IsFVGInPool(created)) continue;
if (g_FVGCount >= MAX_FVG_POOL)
{
int evict = -1; datetime oldest = TimeCurrent();
for (int j = 0; j < g_FVGCount; j++)
if (g_FVGPool[j].status == FVG_USED && g_FVGPool[j].createdTime < oldest)
{ oldest = g_FVGPool[j].createdTime; evict = j; }
if (evict < 0) { if (InpDebugLog) Print("[FVG POOL] Full"); break; }
for (int j = evict; j < g_FVGCount - 1; j++) g_FVGPool[j] = g_FVGPool[j + 1];
g_FVGCount--;
if (g_ActiveFVGIdx > evict) g_ActiveFVGIdx--;
else if (g_ActiveFVGIdx == evict) g_ActiveFVGIdx = -1;
}
FVGRecord rec;
ZeroMemory(rec);
rec.id = g_NextFVGId++; rec.direction = dir;
rec.high = gH; rec.low = gL; rec.mid = (gH + gL) / 2.0;
rec.createdTime = created;
int rightBar = i - 1;
bool c1Hit = false; datetime c1T = 0;
for (int j = rightBar - 1; j >= 1; j--)
{
double cl = iClose(_Symbol, InpMiddleTF, j);
if ((rec.direction == DIR_UP && cl < rec.low) ||
(rec.direction == DIR_DOWN && cl > rec.high))
{ c1Hit = true; c1T = iTime(_Symbol, InpMiddleTF, j); break; }
}
if (c1Hit) { rec.status = FVG_USED; rec.usedCase = 1; rec.usedTime = c1T; }
else
{
bool tdHit = false; datetime tdT = 0;
for (int j = rightBar - 1; j >= 1; j--)
{
bool inGap = (rec.direction == DIR_UP && iLow (_Symbol, InpMiddleTF, j) <= rec.high) ||
(rec.direction == DIR_DOWN && iHigh(_Symbol, InpMiddleTF, j) >= rec.low);
if (inGap) { tdHit = true; tdT = iTime(_Symbol, InpMiddleTF, j); break; }
}
if (tdHit) { rec.status = FVG_TOUCHED; rec.touchTime = tdT; rec.triggerTrendAtTouch = g_TriggerTrend.trend; }
else
{
rec.status = FVG_PENDING;
if ((int)(TimeCurrent() - rec.createdTime) > InpFVGMaxAliveMin * 60)
{ rec.status = FVG_USED; rec.usedCase = 0; rec.usedTime = TimeCurrent(); }
}
}
g_FVGPool[g_FVGCount] = rec;
g_FVGCount++;
if (InpDebugLog)
PrintFormat("[FVG +] #%d %s [%.5f%.5f] %s | %s",
rec.id, EnumToString(rec.direction), rec.low, rec.high,
EnumToString(rec.status), TimeToString(rec.createdTime));
}
}
//+------------------------------------------------------------------+
//| SECTION 7 FVG POOL: UPDATE STATUSES (every tick) |
//+------------------------------------------------------------------+
inline void UpdateFVGStatuses()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double midC1 = iClose(_Symbol, InpMiddleTF, 1);
datetime midT1 = iTime (_Symbol, InpMiddleTF, 1);
for (int i = 0; i < g_FVGCount; i++)
{
if (g_FVGPool[i].status == FVG_USED) continue;
bool c1 = (g_FVGPool[i].direction == DIR_UP && midC1 < g_FVGPool[i].low) ||
(g_FVGPool[i].direction == DIR_DOWN && midC1 > g_FVGPool[i].high);
if (c1)
{
g_FVGPool[i].status = FVG_USED;
g_FVGPool[i].usedCase = 1;
g_FVGPool[i].usedTime = midT1;
if (InpDebugLog)
PrintFormat("[FVG #%d] BROKEN | close=%.5f", g_FVGPool[i].id, midC1);
continue;
}
if (g_FVGPool[i].status == FVG_PENDING)
{
int age = (int)(TimeCurrent() - g_FVGPool[i].createdTime);
if (age > InpFVGMaxAliveMin * 60)
{
g_FVGPool[i].status = FVG_USED; g_FVGPool[i].usedCase = 0;
g_FVGPool[i].usedTime = TimeCurrent();
continue;
}
bool touched = (g_FVGPool[i].direction == DIR_UP && bid <= g_FVGPool[i].high) ||
(g_FVGPool[i].direction == DIR_DOWN && bid >= g_FVGPool[i].low);
if (touched)
{
g_FVGPool[i].status = FVG_TOUCHED;
g_FVGPool[i].touchTime = TimeCurrent();
g_FVGPool[i].triggerTrendAtTouch = g_TriggerTrend.trend;
if (InpDebugLog)
PrintFormat("[FVG #%d] TOUCHED | bid=%.5f [%.5f%.5f]",
g_FVGPool[i].id, bid, g_FVGPool[i].low, g_FVGPool[i].high);
}
}
else if (g_FVGPool[i].status == FVG_TOUCHED)
{
bool hasMSS =
g_TriggerTrend.lastMssTime > g_FVGPool[i].touchTime &&
g_TriggerTrend.lastMssBreak == g_FVGPool[i].direction;
if (hasMSS)
{
g_FVGPool[i].status = FVG_USED;
g_FVGPool[i].usedCase = 2;
g_FVGPool[i].usedTime = TimeCurrent();
g_FVGPool[i].mssTime = g_TriggerTrend.lastMssTime;
g_FVGPool[i].mssEntry = g_TriggerTrend.lastMssLevel;
g_FVGPool[i].mssSL = g_TriggerTrend.mssSLSwing;
if (InpDebugLog)
PrintFormat("[FVG #%d] TRIGGERED | MSS %s entry=%.5f SL=%.5f @ %s",
g_FVGPool[i].id,
(g_TriggerTrend.lastMssBreak == DIR_UP) ? "▲" : "▼",
g_FVGPool[i].mssEntry, g_FVGPool[i].mssSL,
TimeToString(g_FVGPool[i].mssTime, TIME_MINUTES));
continue;
}
int ageMin = (int)((TimeCurrent() - g_FVGPool[i].createdTime) / 60);
if (ageMin > InpFVGMaxAliveMin)
{
g_FVGPool[i].status = FVG_USED;
g_FVGPool[i].usedCase = 0;
g_FVGPool[i].usedTime = TimeCurrent();
if (g_ActiveFVGIdx >= 0
&& g_FVGPool[g_ActiveFVGIdx].id == g_FVGPool[i].id)
g_ActiveFVGIdx = -1;
if (InpDebugLog)
PrintFormat("[FVG #%d] TOUCHED EXPIRED (age=%dmin > %d) → USED",
g_FVGPool[i].id, ageMin, InpFVGMaxAliveMin);
}
}
}
}
//+------------------------------------------------------------------+
//| SECTION 8 BEST FVG SELECTOR |
//+------------------------------------------------------------------+
inline int GetBestActiveFVGIdx()
{
int bestIdx = -1; datetime bestTime = 0; bool foundTouch = false;
for (int i = 0; i < g_FVGCount; i++)
{
if (g_FVGPool[i].status == FVG_USED) continue;
if (g_FVGPool[i].status == FVG_TOUCHED)
{
if (!foundTouch || g_FVGPool[i].createdTime > bestTime)
{ foundTouch = true; bestIdx = i; bestTime = g_FVGPool[i].createdTime; }
}
else if (!foundTouch && g_FVGPool[i].createdTime > bestTime)
{ bestIdx = i; bestTime = g_FVGPool[i].createdTime; }
}
return bestIdx;
}
#endif // EA_ICT_CL__SIGNALS_BOS_FVG_OB_MQH