Chọn FVG gần nhất chưa used làm POI
This commit is contained in:
+10
-2
@@ -3,7 +3,7 @@
|
||||
//| BOS = Continue | CHoCH = Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "ICT 2026"
|
||||
#property version "1.130"
|
||||
#property version "1.138"
|
||||
#property description "ICT2026 MSS limit entry + SL swing CHoCH + TP intraday"
|
||||
|
||||
#include <ICT2026/Config.mqh>
|
||||
@@ -29,7 +29,9 @@ int OnInit()
|
||||
|
||||
if(biasOk || intraOk)
|
||||
{
|
||||
PrintFormat("[ICT2026] Init — Bias=%s | Intraday=%s | Allow=%s | FVG=%d",
|
||||
IctEaState_Refresh(_Symbol);
|
||||
PrintFormat("[ICT2026] Init — EA=%s | Bias=%s | H1=%s | Allow=%s | FVG=%d",
|
||||
IctEaState_Code(IctEaState_Current()),
|
||||
IctBiasDisplayShort(g_ictDailyBias.bias),
|
||||
IctTrendDisplayShort(g_ictIntraday.trend),
|
||||
g_ictIntraday.isAllowTrade ? "true" : "false",
|
||||
@@ -98,4 +100,10 @@ ENUM_ICT_MSS_PHASE ICT2026_GetMssPhase() { return g_ictLowTf.mss.phase; }
|
||||
|
||||
string ICT2026_GetMssReason() { return g_ictLowTf.mss.displayReason; }
|
||||
|
||||
ENUM_ICT_EA_STATE ICT2026_GetEaState() { return IctEaState_Current(); }
|
||||
|
||||
string ICT2026_GetEaStateCode() { return IctEaState_Code(IctEaState_Current()); }
|
||||
|
||||
string ICT2026_GetEaStateDetail() { return g_ictEaState.detail; }
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
@@ -48,7 +48,7 @@ input int InpFvgAtrPeriod = 14; // ATR cho lọc k
|
||||
input double InpFvgMinGapATRPct = 12.0; // H1 only: gap tối thiểu (% ATR, 0=tắt)
|
||||
input double InpFvgMinGapPoints = 0.0; // H1 only: gap tối thiểu (giá)
|
||||
input double InpFvgMinGapVsBarPct = 25.0; // H1 only: gap >= % range nến B (0=tắt)
|
||||
input double InpFvgUsedFillPct = 38.2; // % lấp FVG → Used
|
||||
input double InpFvgUsedFillPct = 25.0; // % lấp FVG → Used
|
||||
input int InpFvgExpireDays = 3; // Xóa Available sau N ngày
|
||||
input int InpFvgMaxZones = 24; // Số FVG tối đa trên chart
|
||||
input bool InpDrawFvgZones = true; // Vẽ FVG + Premium/Discount
|
||||
@@ -59,9 +59,9 @@ input color InpPdPremiumColor = clrMaroon;
|
||||
input color InpPdDiscountColor = clrDarkGreen;
|
||||
|
||||
input group "══ MSS / Entry (Confirm TF) ══"
|
||||
input bool InpMssH1RetestWickOnly = true; // Retest FVG H1: chỉ cần râu chạm (không bắt thân nến lấp %)
|
||||
input double InpMssH1MinFillPct = 38.2; // Nếu tắt wick-only: % lấp tối thiểu trên H1
|
||||
input double InpMssEntryFillPct = 38.2; // M5 FVG: % lấp để sẵn sàng entry
|
||||
input bool InpMssH1RetestWickOnly = true; // (legacy, bỏ qua) Retest = giá chạm FVG
|
||||
input double InpMssH1MinFillPct = 25.0; // Vẽ mức % tham khảo trên chart (không chặn retest)
|
||||
input double InpMssEntryFillPct = 25.0; // M5 FVG: % lấp để sẵn sàng entry
|
||||
input int InpMssConfirmLookback = 80; // Lookback pivot CHoCH trên Confirm TF
|
||||
input int InpMssConfirmFvgBars = 40; // Quét M5 FVG sau CHoCH
|
||||
input double InpMssMaxDistGapPct = 75.0; // MSS: buffer quanh H1 FVG (% chiều cao gap)
|
||||
|
||||
@@ -702,9 +702,12 @@ void IctFvg_UpdateZoneState(const string sym, const ENUM_TIMEFRAMES tf, IctFvgZo
|
||||
|
||||
IctFvg_UpdateTouchAndFill(sym, tf, zone);
|
||||
|
||||
const double usedPct = InpFvgUsedFillPct / 100.0;
|
||||
if(zone.maxFillRatio >= usedPct || zone.maxFillRatio >= 0.999)
|
||||
zone.state = ICT_FVG_USED;
|
||||
if(tf != InpFvgTf)
|
||||
{
|
||||
const double usedPct = InpFvgUsedFillPct / 100.0;
|
||||
if(zone.maxFillRatio >= usedPct || zone.maxFillRatio >= 0.999)
|
||||
zone.state = ICT_FVG_USED;
|
||||
}
|
||||
|
||||
if(zone.state == ICT_FVG_USED && prevState == ICT_FVG_AVAILABLE && zone.fvgUsedTime == 0)
|
||||
zone.fvgUsedTime = iTime(sym, tf, 0);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <ICT2026/MssEntry.mqh>
|
||||
#include <ICT2026/FvgDraw.mqh>
|
||||
#include <ICT2026/MssDraw.mqh>
|
||||
#include <ICT2026/EaState.mqh>
|
||||
|
||||
void IctLowTfTrend_Reset()
|
||||
{
|
||||
@@ -57,9 +58,9 @@ bool IctLowTfTrend_Update(const string sym, const bool force = false)
|
||||
{
|
||||
const ENUM_ICT_FVG_SIDE side = IctFvgSideFromBias(g_ictDailyBias.bias);
|
||||
IctFvg_ScanNew(sym, tf, side, force || allowJustOn);
|
||||
g_ictLowTf.displayReason = StringFormat("Scan iTF FVG %s | %d avail / %d total",
|
||||
IctFvgSideText(side),
|
||||
IctFvg_CountAvailable(), g_ictFvgCount);
|
||||
g_ictLowTf.displayReason = StringFormat("H1 POI %d avail / %d FVG | scan %s",
|
||||
IctMss_CountH1PoiEligible(), g_ictFvgCount,
|
||||
IctFvgSideText(side));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,6 +78,7 @@ bool IctLowTfTrend_Update(const string sym, const bool force = false)
|
||||
IctConfirmFvg_UpdateAll(sym, InpConfirmTf);
|
||||
IctMss_Update(sym);
|
||||
IctMssEntry_Update(sym);
|
||||
IctEaState_Refresh(sym);
|
||||
g_ictLowTf.confirmFvgCount = g_ictConfirmFvgCount;
|
||||
}
|
||||
|
||||
@@ -127,6 +129,7 @@ void IctLowTfTrend_TickRefresh(const string sym)
|
||||
const ENUM_ICT_MSS_PHASE prevMss = g_ictLowTf.mss.phase;
|
||||
IctMss_Update(sym);
|
||||
IctMssEntry_Update(sym);
|
||||
IctEaState_Refresh(sym);
|
||||
if(prevMss != g_ictLowTf.mss.phase)
|
||||
dirty = true;
|
||||
}
|
||||
@@ -139,7 +142,7 @@ void IctLowTfTrend_TickRefresh(const string sym)
|
||||
if(dirty)
|
||||
IctFvgDraw_Render(sym);
|
||||
|
||||
if(g_ictLowTf.mss.phase >= ICT_MSS_CHOCH)
|
||||
if(g_ictLowTf.mss.phase >= ICT_MSS_H1_TOUCH)
|
||||
IctMssDraw_Render(sym);
|
||||
else if(!InpDrawMssChoch)
|
||||
IctMssDraw_DeleteAll();
|
||||
|
||||
@@ -143,22 +143,32 @@ void IctMssDraw_Render(const string sym)
|
||||
datetime keyT = 0, chochT = 0;
|
||||
IctMssDraw_ResolveChoch(mss, keyLv, keyT, slSwing, chochT);
|
||||
|
||||
const bool mssPending = (mss.phase == ICT_MSS_H1_TOUCH && !mss.chochLocked);
|
||||
if(mssPending && mss.liveL0Price > 0.0)
|
||||
{
|
||||
keyLv = isBear ? mss.liveL0Price : mss.liveH0Price;
|
||||
keyT = isBear ? mss.liveL0Time : mss.liveH0Time;
|
||||
slSwing = isBear ? mss.liveH0Price : mss.liveL0Price;
|
||||
chochT = 0;
|
||||
}
|
||||
|
||||
if(keyLv > 0.0)
|
||||
{
|
||||
if(keyT <= 0)
|
||||
keyT = tTouch;
|
||||
if(chochT <= 0)
|
||||
chochT = keyT;
|
||||
chochT = mss.chochTime > 0 ? mss.chochTime : keyT;
|
||||
|
||||
const color clrChoch = clrGold;
|
||||
const string chochLbl = isBear ? " MSS↓ L0 " : " MSS↑ H0 ";
|
||||
const string chochLbl = mssPending ?
|
||||
(isBear ? " L0 live " : " H0 live ") :
|
||||
(isBear ? " MSS↓ L0 " : " MSS↑ H0 ");
|
||||
const bool chochAbove = !isBear;
|
||||
const datetime tLineStart = (keyT > 0) ? keyT : tTouch;
|
||||
const datetime tChoch = (chochT > 0) ? chochT : tLineStart;
|
||||
const bool chochPending = (mss.phase == ICT_MSS_H1_TOUCH && !mss.chochLocked);
|
||||
|
||||
IctMssDraw_HLine("CHOCH", tLineStart, tEnd, keyLv, clrChoch,
|
||||
chochPending ? STYLE_DASH : STYLE_SOLID, 2);
|
||||
mssPending ? STYLE_DOT : STYLE_SOLID, 2);
|
||||
IctMssDraw_Label("CHOCH_LBL", chochLbl, tChoch, keyLv, chochAbove, clrChoch);
|
||||
}
|
||||
|
||||
@@ -166,9 +176,14 @@ void IctMssDraw_Render(const string sym)
|
||||
{
|
||||
const string swTag = isBear ? " H0 " : " L0 ";
|
||||
const color clrSw = clrYellow;
|
||||
datetime tSw = (keyT > 0) ? keyT : tTouch;
|
||||
datetime tSw = isBear ?
|
||||
(mss.liveH0Time > 0 ? mss.liveH0Time : keyT) :
|
||||
(mss.liveL0Time > 0 ? mss.liveL0Time : keyT);
|
||||
if(tSw <= 0)
|
||||
tSw = tTouch;
|
||||
|
||||
IctMssDraw_HLine("MSS_SW", tSw, tEnd, slSwing, clrSw, STYLE_DASH, 1);
|
||||
IctMssDraw_HLine("MSS_SW", tSw, tEnd, slSwing, clrSw,
|
||||
mssPending ? STYLE_DOT : STYLE_DASH, 1);
|
||||
IctMssDraw_Label("MSS_SW_LBL", swTag, tSw, slSwing, true, clrSw);
|
||||
}
|
||||
|
||||
|
||||
+372
-260
@@ -7,6 +7,8 @@
|
||||
#include <ICT2026/ConfirmFvg.mqh>
|
||||
#include <ICT2026/Journal.mqh>
|
||||
#include <ICT2026/LowTfApi.mqh>
|
||||
#include <ICT2026/StructureCore.mqh>
|
||||
#include <ICT2026/Swing.mqh>
|
||||
|
||||
string IctMssPhaseText(const ENUM_ICT_MSS_PHASE ph)
|
||||
{
|
||||
@@ -15,7 +17,7 @@ string IctMssPhaseText(const ENUM_ICT_MSS_PHASE ph)
|
||||
case ICT_MSS_H1_TOUCH: return "Retest FVG H1 OK";
|
||||
case ICT_MSS_CHOCH: return "MSS OK";
|
||||
case ICT_MSS_M5_FVG: return "M5 FVG";
|
||||
case ICT_MSS_ENTRY_FILL: return "M5 fill 38.2%";
|
||||
case ICT_MSS_ENTRY_FILL: return "M5 fill entry";
|
||||
case ICT_MSS_READY: return "READY entry";
|
||||
default: return "Idle";
|
||||
}
|
||||
@@ -29,51 +31,121 @@ int IctMss_FindH1FvgById(const ulong id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// H1 FVG mục tiêu: Bear → Premium cao nhất; Bull → Discount thấp nhất (chờ hồi chạm, chưa cần fill)
|
||||
int IctMss_SelectTargetH1Fvg()
|
||||
// POI H1: FVG chưa Used, gần giá nhất (không phải Premium cao nhất / Discount thấp nhất)
|
||||
double IctMss_DistancePriceToFvg(const double price, const IctFvgZone &z)
|
||||
{
|
||||
int bestIdx = -1;
|
||||
if(price >= z.lower - _Point && price <= z.upper + _Point)
|
||||
return 0.0;
|
||||
if(price > z.upper)
|
||||
return price - z.upper;
|
||||
return z.lower - price;
|
||||
}
|
||||
|
||||
bool IctMss_IsH1PoiEligible(const IctFvgZone &z)
|
||||
{
|
||||
if(!IctFvg_MatchesBias(z))
|
||||
return false;
|
||||
if(!IctFvg_IsEntryRepPd(z))
|
||||
return false;
|
||||
if(z.state == ICT_FVG_USED)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int IctMss_CountH1PoiEligible()
|
||||
{
|
||||
int n = 0;
|
||||
for(int i = 0; i < g_ictFvgCount; i++)
|
||||
if(IctMss_IsH1PoiEligible(g_ictFvgZones[i]))
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
int IctMss_SelectNearestH1Poi(const string sym)
|
||||
{
|
||||
const double px = SymbolInfoDouble(sym, SYMBOL_BID);
|
||||
int bestIdx = -1;
|
||||
double bestDist = DBL_MAX;
|
||||
|
||||
for(int i = 0; i < g_ictFvgCount; i++)
|
||||
{
|
||||
if(!IctFvg_MatchesBias(g_ictFvgZones[i]))
|
||||
continue;
|
||||
if(!IctFvg_IsEntryRepPd(g_ictFvgZones[i]))
|
||||
continue;
|
||||
if(g_ictFvgZones[i].state == ICT_FVG_USED && g_ictFvgZones[i].maxFillRatio < 0.01)
|
||||
if(!IctMss_IsH1PoiEligible(g_ictFvgZones[i]))
|
||||
continue;
|
||||
|
||||
if(g_ictDailyBias.bias == ICT_BIAS_BEAR)
|
||||
const double d = IctMss_DistancePriceToFvg(px, g_ictFvgZones[i]);
|
||||
if(d < bestDist - _Point)
|
||||
{
|
||||
if(bestIdx < 0 || g_ictFvgZones[i].upper > g_ictFvgZones[bestIdx].upper)
|
||||
bestIdx = i;
|
||||
bestDist = d;
|
||||
bestIdx = i;
|
||||
}
|
||||
else if(g_ictDailyBias.bias == ICT_BIAS_BULL)
|
||||
else if(MathAbs(d - bestDist) <= _Point && bestIdx >= 0)
|
||||
{
|
||||
if(bestIdx < 0 || g_ictFvgZones[i].lower < g_ictFvgZones[bestIdx].lower)
|
||||
if(g_ictFvgZones[i].createdTime > g_ictFvgZones[bestIdx].createdTime)
|
||||
bestIdx = i;
|
||||
}
|
||||
}
|
||||
return bestIdx;
|
||||
}
|
||||
|
||||
// Retest FVG H1 (POI): râu H1 chạm gap (InpMssH1RetestWickOnly) hoặc thêm % lấp thân/râu
|
||||
bool IctMss_HasH1FvgRetest(const string sym, IctFvgZone &h1)
|
||||
bool IctMss_PriceInsideFvg(const IctFvgZone &h1, const double price)
|
||||
{
|
||||
return (price >= h1.lower - _Point && price <= h1.upper + _Point);
|
||||
}
|
||||
|
||||
bool IctMss_BarOverlapsFvg(const IctFvgZone &h1, const double barHi, const double barLo)
|
||||
{
|
||||
return (barHi >= h1.lower - _Point && barLo <= h1.upper + _Point);
|
||||
}
|
||||
|
||||
bool IctMss_LivePriceTouchesFvg(const string sym, const IctFvgZone &h1)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(sym, SYMBOL_BID);
|
||||
const double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
|
||||
return IctMss_PriceInsideFvg(h1, bid) || IctMss_PriceInsideFvg(h1, ask);
|
||||
}
|
||||
|
||||
datetime IctMss_FirstM5TouchInFvg(const string sym, const IctFvgZone &h1)
|
||||
{
|
||||
const ENUM_TIMEFRAMES tf = InpConfirmTf;
|
||||
const int lim = MathMin(InpMssConfirmLookback, iBars(sym, tf) - 2);
|
||||
datetime best = 0;
|
||||
for(int sh = lim; sh >= 1; sh--)
|
||||
{
|
||||
const datetime t = iTime(sym, tf, sh);
|
||||
if(t <= h1.createdTime)
|
||||
break;
|
||||
if(!IctMss_BarOverlapsFvg(h1, iHigh(sym, tf, sh), iLow(sym, tf, sh)))
|
||||
continue;
|
||||
if(best == 0 || t < best)
|
||||
best = t;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
datetime IctMss_GetFvgTouchTime(const string sym, IctFvgZone &h1)
|
||||
{
|
||||
IctFvg_UpdateZoneState(sym, InpFvgTf, h1);
|
||||
if(h1.firstTouchTime <= 0)
|
||||
return false;
|
||||
|
||||
if(InpMssH1RetestWickOnly)
|
||||
{
|
||||
h1.mssH1Touch382 = true;
|
||||
return true;
|
||||
}
|
||||
datetime t = h1.firstTouchTime;
|
||||
const datetime tM5 = IctMss_FirstM5TouchInFvg(sym, h1);
|
||||
if(tM5 > 0 && (t <= 0 || tM5 < t))
|
||||
t = tM5;
|
||||
|
||||
if(!IctFvg_HasFillAtLeast(h1, InpMssH1MinFillPct))
|
||||
return false;
|
||||
h1.mssH1Touch382 = true;
|
||||
return true;
|
||||
if(t <= 0 && IctMss_LivePriceTouchesFvg(sym, h1))
|
||||
t = iTime(sym, InpConfirmTf, 0);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
// Retest = giá chạm FVG (H1/M5/bid-ask). Râu H1 xuyên FVG vẫn OK; chỉ hủy khi H1 body đóng xuyên.
|
||||
bool IctMss_HasFvgPriceTouch(const string sym, IctFvgZone &h1)
|
||||
{
|
||||
return (IctMss_GetFvgTouchTime(sym, h1) > 0);
|
||||
}
|
||||
|
||||
bool IctMss_HasH1FvgRetest(const string sym, IctFvgZone &h1)
|
||||
{
|
||||
return IctMss_HasFvgPriceTouch(sym, h1);
|
||||
}
|
||||
|
||||
double IctMss_H1Atr(const string sym)
|
||||
@@ -178,234 +250,165 @@ bool IctMss_BodyBrokeLevelSince(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IctMss_BarOverlapsFvg(const IctFvgZone &h1, const double barHi, const double barLo)
|
||||
bool IctMss_HasM5BarInH1Fvg(const string sym, const IctFvgZone &h1,
|
||||
const datetime since)
|
||||
{
|
||||
return (barHi >= h1.lower - _Point && barLo <= h1.upper + _Point);
|
||||
}
|
||||
|
||||
IctSwingPoint IctMss_ProtectedLowBeforeHigh(const IctSwingPoint &lows[], const int nL,
|
||||
const IctSwingPoint &h0,
|
||||
const datetime tTouch)
|
||||
{
|
||||
IctSwingPoint empty;
|
||||
empty.Clear();
|
||||
if(!h0.Valid())
|
||||
return empty;
|
||||
|
||||
int best = -1;
|
||||
for(int i = 0; i < nL; i++)
|
||||
const ENUM_TIMEFRAMES tf = InpConfirmTf;
|
||||
const int lim = MathMin(InpMssConfirmLookback, iBars(sym, tf) - 2);
|
||||
for(int sh = 1; sh <= lim; sh++)
|
||||
{
|
||||
if(lows[i].time < tTouch)
|
||||
continue;
|
||||
if(lows[i].shift <= h0.shift)
|
||||
continue;
|
||||
if(lows[i].price >= h0.price - _Point)
|
||||
continue;
|
||||
if(best < 0 || lows[i].shift < lows[best].shift)
|
||||
best = i;
|
||||
if(since > 0 && iTime(sym, tf, sh) < since)
|
||||
break;
|
||||
if(IctMss_BarOverlapsFvg(h1, iHigh(sym, tf, sh), iLow(sym, tf, sh)))
|
||||
return true;
|
||||
}
|
||||
if(best < 0)
|
||||
return empty;
|
||||
return lows[best];
|
||||
return false;
|
||||
}
|
||||
|
||||
IctSwingPoint IctMss_ProtectedHighAfterLow(const IctSwingPoint &highs[], const int nH,
|
||||
const IctSwingPoint &l0,
|
||||
const datetime tTouch)
|
||||
bool IctMss_UpdateLiveM5Swings(const string sym, const ENUM_ICT_BIAS bias,
|
||||
const datetime tTouch)
|
||||
{
|
||||
IctSwingPoint empty;
|
||||
empty.Clear();
|
||||
if(!l0.Valid())
|
||||
return empty;
|
||||
g_ictLowTf.mss.liveL0Price = 0.0;
|
||||
g_ictLowTf.mss.liveH0Price = 0.0;
|
||||
g_ictLowTf.mss.liveL0Time = 0;
|
||||
g_ictLowTf.mss.liveH0Time = 0;
|
||||
|
||||
int best = -1;
|
||||
for(int i = 0; i < nH; i++)
|
||||
{
|
||||
if(highs[i].time < tTouch || highs[i].time < l0.time)
|
||||
continue;
|
||||
if(highs[i].shift >= l0.shift)
|
||||
continue;
|
||||
if(highs[i].price <= l0.price + _Point)
|
||||
continue;
|
||||
if(best < 0 || highs[i].shift < highs[best].shift)
|
||||
best = i;
|
||||
}
|
||||
if(best < 0)
|
||||
return empty;
|
||||
return highs[best];
|
||||
}
|
||||
|
||||
bool IctMss_ReactionHighAfterTouch(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
const IctFvgZone &h1, const datetime tTouch,
|
||||
IctSwingPoint &outHi)
|
||||
{
|
||||
outHi.Clear();
|
||||
IctSwingSet sw;
|
||||
ENUM_ICT_STRUCT st = ICT_STRUCT_NONE;
|
||||
if(IctBuildConfirmSwingSet(sym, sw, st) && sw.hasH0 && sw.h0.time >= tTouch)
|
||||
{
|
||||
outHi = sw.h0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int touchSh = iBarShift(sym, tf, tTouch, true);
|
||||
if(touchSh < 0)
|
||||
touchSh = iBarShift(sym, tf, tTouch, false);
|
||||
if(touchSh < 1)
|
||||
if(!IctBuildConfirmSwingSet(sym, sw, st))
|
||||
return false;
|
||||
|
||||
double bestP = -DBL_MAX;
|
||||
for(int sh = touchSh; sh >= 1; sh--)
|
||||
{
|
||||
if(iTime(sym, tf, sh) < tTouch)
|
||||
continue;
|
||||
const double hi = iHigh(sym, tf, sh);
|
||||
const double lo = iLow(sym, tf, sh);
|
||||
if(!IctMss_BarOverlapsFvg(h1, hi, lo))
|
||||
continue;
|
||||
if(hi > bestP)
|
||||
{
|
||||
bestP = hi;
|
||||
outHi.price = hi;
|
||||
outHi.time = iTime(sym, tf, sh);
|
||||
outHi.shift = sh;
|
||||
}
|
||||
}
|
||||
return outHi.Valid();
|
||||
}
|
||||
|
||||
bool IctMss_ReactionLowAfterTouch(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
const IctFvgZone &h1, const datetime tTouch,
|
||||
IctSwingPoint &outLo)
|
||||
{
|
||||
outLo.Clear();
|
||||
IctSwingSet sw;
|
||||
ENUM_ICT_STRUCT st = ICT_STRUCT_NONE;
|
||||
if(IctBuildConfirmSwingSet(sym, sw, st) && sw.hasL0 && sw.l0.time >= tTouch)
|
||||
{
|
||||
outLo = sw.l0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int touchSh = iBarShift(sym, tf, tTouch, true);
|
||||
if(touchSh < 0)
|
||||
touchSh = iBarShift(sym, tf, tTouch, false);
|
||||
if(touchSh < 1)
|
||||
return false;
|
||||
|
||||
double bestP = DBL_MAX;
|
||||
for(int sh = touchSh; sh >= 1; sh--)
|
||||
{
|
||||
if(iTime(sym, tf, sh) < tTouch)
|
||||
continue;
|
||||
const double hi = iHigh(sym, tf, sh);
|
||||
const double lo = iLow(sym, tf, sh);
|
||||
if(!IctMss_BarOverlapsFvg(h1, hi, lo))
|
||||
continue;
|
||||
if(lo < bestP)
|
||||
{
|
||||
bestP = lo;
|
||||
outLo.price = lo;
|
||||
outLo.time = iTime(sym, tf, sh);
|
||||
outLo.shift = sh;
|
||||
}
|
||||
}
|
||||
return outLo.Valid();
|
||||
}
|
||||
|
||||
// MSS M5 sau retest FVG H1: pullback ngược cục bộ → phá L0 (bear HTF) hoặc H0 (bull HTF)
|
||||
bool IctMss_FindMssAfterFvgRetest(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
const IctFvgZone &h1, const datetime tTouch,
|
||||
const ENUM_ICT_BIAS bias,
|
||||
double &keyLevelOut, datetime &keyTimeOut,
|
||||
datetime &mssBarTimeOut, double &slSwingOut)
|
||||
{
|
||||
keyLevelOut = 0.0;
|
||||
keyTimeOut = 0;
|
||||
mssBarTimeOut = 0;
|
||||
slSwingOut = 0.0;
|
||||
|
||||
IctSwingPoint highs[], lows[];
|
||||
IctCollectSwings(sym, tf, InpConfirmSwingRange, InpConfirmSwingLookback, highs, lows);
|
||||
const int nH = ArraySize(highs);
|
||||
const int nL = ArraySize(lows);
|
||||
|
||||
if(bias == ICT_BIAS_BEAR)
|
||||
{
|
||||
IctSwingPoint h0;
|
||||
if(!IctMss_ReactionHighAfterTouch(sym, tf, h1, tTouch, h0))
|
||||
if(!sw.hasL0 || !sw.hasH0)
|
||||
return false;
|
||||
|
||||
IctSwingPoint keyLo = IctMss_ProtectedLowBeforeHigh(lows, nL, h0, tTouch);
|
||||
if(!keyLo.Valid())
|
||||
if(sw.l0.time < tTouch && sw.h0.time < tTouch)
|
||||
return false;
|
||||
|
||||
const int startSh = MathMax(1, h0.shift - 1);
|
||||
for(int sh = startSh; sh >= 1; sh--)
|
||||
{
|
||||
if(iTime(sym, tf, sh) < h0.time)
|
||||
continue;
|
||||
if(!IctBodyBreakBelow(sym, tf, sh, keyLo.price))
|
||||
continue;
|
||||
|
||||
keyLevelOut = keyLo.price;
|
||||
keyTimeOut = keyLo.time;
|
||||
mssBarTimeOut = iTime(sym, tf, sh);
|
||||
slSwingOut = h0.price;
|
||||
return true;
|
||||
}
|
||||
g_ictLowTf.mss.liveL0Price = sw.l0.price;
|
||||
g_ictLowTf.mss.liveL0Time = sw.l0.time;
|
||||
g_ictLowTf.mss.liveH0Price = sw.h0.price;
|
||||
g_ictLowTf.mss.liveH0Time = sw.h0.time;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(bias == ICT_BIAS_BULL)
|
||||
{
|
||||
IctSwingPoint l0;
|
||||
if(!IctMss_ReactionLowAfterTouch(sym, tf, h1, tTouch, l0))
|
||||
if(!sw.hasL0 || !sw.hasH0)
|
||||
return false;
|
||||
|
||||
IctSwingPoint keyHi = IctMss_ProtectedHighAfterLow(highs, nH, l0, tTouch);
|
||||
if(!keyHi.Valid())
|
||||
if(sw.l0.time < tTouch && sw.h0.time < tTouch)
|
||||
return false;
|
||||
|
||||
const int startSh = MathMax(1, l0.shift - 1);
|
||||
for(int sh = startSh; sh >= 1; sh--)
|
||||
{
|
||||
if(iTime(sym, tf, sh) < l0.time)
|
||||
continue;
|
||||
if(!IctBodyBreakAbove(sym, tf, sh, keyHi.price))
|
||||
continue;
|
||||
|
||||
keyLevelOut = keyHi.price;
|
||||
keyTimeOut = keyHi.time;
|
||||
mssBarTimeOut = iTime(sym, tf, sh);
|
||||
slSwingOut = l0.price;
|
||||
return true;
|
||||
}
|
||||
g_ictLowTf.mss.liveL0Price = sw.l0.price;
|
||||
g_ictLowTf.mss.liveL0Time = sw.l0.time;
|
||||
g_ictLowTf.mss.liveH0Price = sw.h0.price;
|
||||
g_ictLowTf.mss.liveH0Time = sw.h0.time;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hủy setup: H1 đóng (shift=1) — thân xuyên FVG (không giữ giá). Râu xuyên vẫn tiếp tục MSS.
|
||||
bool IctMss_H1BodyInvalidatedSetup(const string sym, const IctFvgZone &h1,
|
||||
const ENUM_ICT_BIAS bias)
|
||||
{
|
||||
if(h1.upper <= h1.lower)
|
||||
return false;
|
||||
|
||||
const double cls = iClose(sym, InpFvgTf, 1);
|
||||
if(bias == ICT_BIAS_BEAR)
|
||||
return (cls > h1.upper + _Point);
|
||||
if(bias == ICT_BIAS_BULL)
|
||||
return (cls < h1.lower - _Point);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IctMss_CheckH1InvalidationOnNewBar(const string sym, const IctFvgZone &h1,
|
||||
const ENUM_ICT_BIAS bias)
|
||||
{
|
||||
const datetime h1Bar1 = iTime(sym, InpFvgTf, 1);
|
||||
if(h1Bar1 <= 0)
|
||||
return false;
|
||||
|
||||
if(h1Bar1 == g_ictLowTf.mss.h1LastInvalidBarTime)
|
||||
return false;
|
||||
|
||||
g_ictLowTf.mss.h1LastInvalidBarTime = h1Bar1;
|
||||
return IctMss_H1BodyInvalidatedSetup(sym, h1, bias);
|
||||
}
|
||||
|
||||
bool IctMss_FindMssBreakBar(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
const double keyLevel, const datetime keyTime,
|
||||
const bool wantBreakBelow,
|
||||
datetime &breakTimeOut)
|
||||
{
|
||||
breakTimeOut = 0;
|
||||
if(keyLevel <= 0.0 || keyTime <= 0)
|
||||
return false;
|
||||
|
||||
const int lim = MathMin(InpMssConfirmLookback, iBars(sym, tf) - 2);
|
||||
for(int sh = 1; sh <= lim; sh++)
|
||||
{
|
||||
if(iTime(sym, tf, sh) < keyTime)
|
||||
break;
|
||||
const bool broke = wantBreakBelow ?
|
||||
IctBodyBreakBelow(sym, tf, sh, keyLevel) :
|
||||
IctBodyBreakAbove(sym, tf, sh, keyLevel);
|
||||
if(!broke)
|
||||
continue;
|
||||
breakTimeOut = iTime(sym, tf, sh);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IctMss_TryLockMss(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
const IctFvgZone &h1, const ENUM_ICT_BIAS bias)
|
||||
{
|
||||
if(g_ictLowTf.mss.chochLocked && g_ictLowTf.mss.chochKeyLevel > 0.0)
|
||||
return true;
|
||||
|
||||
const datetime tTouch = h1.firstTouchTime;
|
||||
const datetime tTouch = (g_ictLowTf.mss.h1TouchTime > 0) ?
|
||||
g_ictLowTf.mss.h1TouchTime : h1.firstTouchTime;
|
||||
if(tTouch <= 0)
|
||||
return false;
|
||||
|
||||
double keyLv = 0.0, slSwing = 0.0;
|
||||
datetime keyT = 0, mssBarT = 0;
|
||||
if(!IctMss_FindMssAfterFvgRetest(sym, tf, h1, tTouch, bias,
|
||||
keyLv, keyT, mssBarT, slSwing))
|
||||
if(!IctMss_UpdateLiveM5Swings(sym, bias, tTouch))
|
||||
return false;
|
||||
|
||||
g_ictLowTf.mss.chochKeyLevel = keyLv;
|
||||
g_ictLowTf.mss.chochKeyTime = keyT;
|
||||
g_ictLowTf.mss.slSwingPrice = slSwing;
|
||||
g_ictLowTf.mss.chochTime = mssBarT;
|
||||
g_ictLowTf.mss.chochLocked = true;
|
||||
if(g_ictLowTf.mss.liveL0Time < tTouch && g_ictLowTf.mss.liveH0Time < tTouch)
|
||||
return false;
|
||||
|
||||
datetime breakT = 0;
|
||||
|
||||
if(bias == ICT_BIAS_BEAR)
|
||||
{
|
||||
if(!IctMss_FindMssBreakBar(sym, tf, g_ictLowTf.mss.liveL0Price,
|
||||
g_ictLowTf.mss.liveL0Time, true, breakT))
|
||||
return false;
|
||||
if(breakT < tTouch)
|
||||
return false;
|
||||
|
||||
g_ictLowTf.mss.chochKeyLevel = g_ictLowTf.mss.liveL0Price;
|
||||
g_ictLowTf.mss.chochKeyTime = g_ictLowTf.mss.liveL0Time;
|
||||
g_ictLowTf.mss.slSwingPrice = g_ictLowTf.mss.liveH0Price;
|
||||
}
|
||||
else if(bias == ICT_BIAS_BULL)
|
||||
{
|
||||
if(!IctMss_FindMssBreakBar(sym, tf, g_ictLowTf.mss.liveH0Price,
|
||||
g_ictLowTf.mss.liveH0Time, false, breakT))
|
||||
return false;
|
||||
if(breakT < tTouch)
|
||||
return false;
|
||||
|
||||
g_ictLowTf.mss.chochKeyLevel = g_ictLowTf.mss.liveH0Price;
|
||||
g_ictLowTf.mss.chochKeyTime = g_ictLowTf.mss.liveH0Time;
|
||||
g_ictLowTf.mss.slSwingPrice = g_ictLowTf.mss.liveL0Price;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
g_ictLowTf.mss.chochTime = breakT;
|
||||
g_ictLowTf.mss.chochLocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -572,6 +575,96 @@ bool IctMss_PriceInEntryZone(const string sym, const ENUM_TIMEFRAMES tf,
|
||||
return false;
|
||||
}
|
||||
|
||||
void IctMss_MarkFvgUsed(IctFvgZone &zone, const string sym)
|
||||
{
|
||||
if(zone.id == 0)
|
||||
return;
|
||||
|
||||
zone.state = ICT_FVG_USED;
|
||||
if(zone.fvgUsedTime == 0)
|
||||
zone.fvgUsedTime = iTime(sym, InpFvgTf, 0);
|
||||
zone.timeEnd = IctFvg_GetFvgDrawTimeEnd(sym, InpFvgTf, zone);
|
||||
}
|
||||
|
||||
void IctMss_MarkFvgUsedOnMssSuccess(const string sym, const ulong fvgId)
|
||||
{
|
||||
if(fvgId == 0)
|
||||
return;
|
||||
|
||||
const int idx = IctMss_FindH1FvgById(fvgId);
|
||||
if(idx < 0)
|
||||
return;
|
||||
|
||||
const bool fresh = (g_ictFvgZones[idx].state != ICT_FVG_USED);
|
||||
if(fresh)
|
||||
IctMss_MarkFvgUsed(g_ictFvgZones[idx], sym);
|
||||
|
||||
if(g_ictLowTf.mss.h1WatchFvgId == fvgId)
|
||||
g_ictLowTf.mss.h1WatchFvgId = 0;
|
||||
|
||||
if(fresh && InpMssLogJournal)
|
||||
PrintFormat("[ICT2026/MSS] FVG #%I64u → Used (giữ giá + MSS M5 OK)", fvgId);
|
||||
}
|
||||
|
||||
void IctMss_FailFvgH1Body(const string sym, const ulong fvgId, const string reason)
|
||||
{
|
||||
if(fvgId > 0)
|
||||
{
|
||||
const int idx = IctMss_FindH1FvgById(fvgId);
|
||||
if(idx >= 0)
|
||||
IctMss_MarkFvgUsed(g_ictFvgZones[idx], sym);
|
||||
}
|
||||
|
||||
IctMss_ResetState();
|
||||
g_ictLowTf.mss.displayReason = reason;
|
||||
|
||||
if(InpMssLogJournal)
|
||||
PrintFormat("[ICT2026/MSS] FVG #%I64u → Used (H1 body xuyên) | %s", fvgId, reason);
|
||||
g_ictMssJournalLast = "";
|
||||
}
|
||||
|
||||
void IctMss_ResetPipelineKeepWatch()
|
||||
{
|
||||
const ulong watch = g_ictLowTf.mss.h1WatchFvgId;
|
||||
const datetime inv = g_ictLowTf.mss.h1LastInvalidBarTime;
|
||||
g_ictLowTf.mss.Clear();
|
||||
g_ictLowTf.mss.h1WatchFvgId = watch;
|
||||
g_ictLowTf.mss.h1LastInvalidBarTime = inv;
|
||||
IctMss_JournalReset();
|
||||
}
|
||||
|
||||
void IctMss_CheckH1WatchInvalidation(const string sym)
|
||||
{
|
||||
const ulong watchId = g_ictLowTf.mss.h1WatchFvgId;
|
||||
if(watchId == 0)
|
||||
return;
|
||||
|
||||
const int idx = IctMss_FindH1FvgById(watchId);
|
||||
if(idx < 0)
|
||||
{
|
||||
g_ictLowTf.mss.h1WatchFvgId = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_ictFvgZones[idx].state == ICT_FVG_USED)
|
||||
{
|
||||
g_ictLowTf.mss.h1WatchFvgId = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const ENUM_ICT_BIAS bias = g_ictDailyBias.bias;
|
||||
if(bias != ICT_BIAS_BEAR && bias != ICT_BIAS_BULL)
|
||||
return;
|
||||
|
||||
if(!IctMss_CheckH1InvalidationOnNewBar(sym, g_ictFvgZones[idx], bias))
|
||||
return;
|
||||
|
||||
IctMss_FailFvgH1Body(sym, watchId,
|
||||
(bias == ICT_BIAS_BEAR) ?
|
||||
"FVG Used: H1 đóng thân trên Premium — không giữ giá" :
|
||||
"FVG Used: H1 đóng thân dưới Discount — không giữ giá");
|
||||
}
|
||||
|
||||
void IctMss_ResetState()
|
||||
{
|
||||
g_ictLowTf.mss.Clear();
|
||||
@@ -582,11 +675,18 @@ void IctMss_Update(const string sym)
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = IctMssPhaseText(g_ictLowTf.mss.phase);
|
||||
|
||||
IctMss_CheckH1WatchInvalidation(sym);
|
||||
|
||||
if(!g_ictIntraday.isAllowTrade)
|
||||
{
|
||||
if(g_ictLowTf.mss.phase != ICT_MSS_IDLE)
|
||||
IctMss_ResetState();
|
||||
g_ictLowTf.mss.displayReason = "AllowTrade=false";
|
||||
{
|
||||
if(g_ictLowTf.mss.h1WatchFvgId == 0 && g_ictLowTf.mss.h1FvgId > 0)
|
||||
g_ictLowTf.mss.h1WatchFvgId = g_ictLowTf.mss.h1FvgId;
|
||||
IctMss_ResetPipelineKeepWatch();
|
||||
}
|
||||
if(StringFind(g_ictLowTf.mss.displayReason, "FVG Used") != 0)
|
||||
g_ictLowTf.mss.displayReason = "AllowTrade=false (H1 lệch Bias — MSS tạm dừng)";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -599,37 +699,41 @@ void IctMss_Update(const string sym)
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_ictLowTf.mss.phase == ICT_MSS_IDLE || g_ictLowTf.mss.h1FvgId == 0)
|
||||
if(g_ictLowTf.mss.phase == ICT_MSS_IDLE)
|
||||
{
|
||||
const int hIdx = IctMss_SelectTargetH1Fvg();
|
||||
const int hIdx = IctMss_SelectNearestH1Poi(sym);
|
||||
if(hIdx < 0)
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = StringFormat("Chờ H1 %s FVG",
|
||||
(wantSide == ICT_FVG_BEAR) ? "Premium" : "Discount");
|
||||
g_ictLowTf.mss.h1FvgId = 0;
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"Chờ H1 %s FVG gần giá (%d POI / %d FVG)",
|
||||
(wantSide == ICT_FVG_BEAR) ? "Premium" : "Discount",
|
||||
IctMss_CountH1PoiEligible(), g_ictFvgCount);
|
||||
return;
|
||||
}
|
||||
|
||||
g_ictLowTf.mss.h1FvgId = g_ictFvgZones[hIdx].id;
|
||||
|
||||
if(!IctMss_HasH1FvgRetest(sym, g_ictFvgZones[hIdx]))
|
||||
if(!IctMss_HasFvgPriceTouch(sym, g_ictFvgZones[hIdx]))
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = InpMssH1RetestWickOnly ?
|
||||
StringFormat("Chờ râu H1 chạm FVG %s [%.0f–%.0f]",
|
||||
IctPdZoneText(IctFvg_GetRepPd(g_ictFvgZones[hIdx])),
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper) :
|
||||
StringFormat("Chờ retest FVG H1 %s [%.0f–%.0f] (%.0f%% lấp)",
|
||||
IctPdZoneText(IctFvg_GetRepPd(g_ictFvgZones[hIdx])),
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper,
|
||||
InpMssH1MinFillPct);
|
||||
const double px = SymbolInfoDouble(sym, SYMBOL_BID);
|
||||
const double dist = IctMss_DistancePriceToFvg(px, g_ictFvgZones[hIdx]);
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"POI #%I64u [%.0f–%.0f] dist %.0f pts — chờ chạm",
|
||||
g_ictFvgZones[hIdx].id,
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper, dist / _Point);
|
||||
return;
|
||||
}
|
||||
|
||||
const datetime tTouch = IctMss_GetFvgTouchTime(sym, g_ictFvgZones[hIdx]);
|
||||
g_ictLowTf.mss.phase = ICT_MSS_H1_TOUCH;
|
||||
g_ictLowTf.mss.h1TouchTime = g_ictFvgZones[hIdx].firstTouchTime;
|
||||
g_ictLowTf.mss.displayReason = InpMssH1RetestWickOnly ?
|
||||
"Retest FVG H1 OK | râu chạm POI" :
|
||||
StringFormat("Retest FVG H1 OK | lấp %.0f%% (H1)",
|
||||
g_ictFvgZones[hIdx].maxFillRatio * 100.0);
|
||||
g_ictLowTf.mss.h1FvgId = g_ictFvgZones[hIdx].id;
|
||||
g_ictLowTf.mss.h1WatchFvgId = g_ictFvgZones[hIdx].id;
|
||||
g_ictLowTf.mss.h1TouchTime = tTouch;
|
||||
g_ictLowTf.mss.h1LastInvalidBarTime = 0;
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"Giá đã chạm FVG — theo dõi MSS M5 [%.0f–%.0f]",
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper);
|
||||
}
|
||||
|
||||
const int h1Idx = IctMss_FindH1FvgById(g_ictLowTf.mss.h1FvgId);
|
||||
@@ -640,44 +744,52 @@ void IctMss_Update(const string sym)
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_ictLowTf.mss.phase >= ICT_MSS_H1_TOUCH &&
|
||||
!IctMss_HasH1FvgRetest(sym, g_ictFvgZones[h1Idx]))
|
||||
if(g_ictFvgZones[h1Idx].state == ICT_FVG_USED &&
|
||||
g_ictLowTf.mss.phase <= ICT_MSS_H1_TOUCH &&
|
||||
!g_ictLowTf.mss.chochLocked)
|
||||
{
|
||||
IctMss_ResetState();
|
||||
g_ictLowTf.mss.displayReason = "FVG H1 chưa retest đủ % — reset MSS";
|
||||
g_ictLowTf.mss.displayReason = "FVG POI Used — chọn FVG gần giá tiếp theo";
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_ictLowTf.mss.phase >= ICT_MSS_H1_TOUCH && g_ictLowTf.mss.h1TouchTime <= 0)
|
||||
g_ictLowTf.mss.h1TouchTime = g_ictFvgZones[h1Idx].firstTouchTime;
|
||||
|
||||
if(g_ictLowTf.mss.phase == ICT_MSS_H1_TOUCH)
|
||||
{
|
||||
if(!IctMss_TryLockMss(sym, cTf, g_ictFvgZones[h1Idx], g_ictDailyBias.bias))
|
||||
const datetime t = IctMss_GetFvgTouchTime(sym, g_ictFvgZones[h1Idx]);
|
||||
if(t > 0)
|
||||
g_ictLowTf.mss.h1TouchTime = t;
|
||||
}
|
||||
|
||||
if(g_ictLowTf.mss.phase == ICT_MSS_H1_TOUCH && !g_ictLowTf.mss.chochLocked)
|
||||
{
|
||||
const datetime tTouch = g_ictLowTf.mss.h1TouchTime;
|
||||
if(IctMss_UpdateLiveM5Swings(sym, g_ictDailyBias.bias, tTouch))
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = (g_ictDailyBias.bias == ICT_BIAS_BEAR) ?
|
||||
"Retest FVG H1 OK — chờ MSS↓ phá L0 M5" :
|
||||
"Retest FVG H1 OK — chờ MSS↑ phá H0 M5";
|
||||
return;
|
||||
if(g_ictDailyBias.bias == ICT_BIAS_BEAR)
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"Chờ MSS↓ phá L0=%.2f | H0=%.2f (M5 bull)",
|
||||
g_ictLowTf.mss.liveL0Price, g_ictLowTf.mss.liveH0Price);
|
||||
else
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"Chờ MSS↑ phá H0=%.2f | L0=%.2f (M5 bear)",
|
||||
g_ictLowTf.mss.liveH0Price, g_ictLowTf.mss.liveL0Price);
|
||||
}
|
||||
else
|
||||
g_ictLowTf.mss.displayReason = "M5 trong FVG — chờ pivot L0/H0";
|
||||
|
||||
if(!IctMss_TryLockMss(sym, cTf, g_ictFvgZones[h1Idx], g_ictDailyBias.bias))
|
||||
return;
|
||||
|
||||
IctMss_MarkFvgUsedOnMssSuccess(sym, g_ictLowTf.mss.h1FvgId);
|
||||
|
||||
const double keyLv = g_ictLowTf.mss.chochKeyLevel;
|
||||
const double slSwing = g_ictLowTf.mss.slSwingPrice;
|
||||
const datetime mssT = g_ictLowTf.mss.chochTime;
|
||||
|
||||
if(!IctMss_IsChochNearH1Fvg(sym, g_ictFvgZones[h1Idx], g_ictDailyBias.bias,
|
||||
keyLv, slSwing, mssT))
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"MSS khóa @ %.2f — xa H1 FVG, chờ gần hơn", keyLv);
|
||||
return;
|
||||
}
|
||||
|
||||
g_ictLowTf.mss.phase = ICT_MSS_CHOCH;
|
||||
IctConfirmFvg_ScanNew(sym, cTf, wantSide, g_ictLowTf.mss.chochTime, true);
|
||||
g_ictLowTf.mss.displayReason = (g_ictDailyBias.bias == ICT_BIAS_BEAR) ?
|
||||
StringFormat("MSS↓ khóa L0=%.2f | H0=%.2f — quét M5 FVG", keyLv, slSwing) :
|
||||
StringFormat("MSS↑ khóa H0=%.2f | L0=%.2f — quét M5 FVG", keyLv, slSwing);
|
||||
StringFormat("MSS↓ @ L0=%.2f | SL H0=%.2f — quét M5 FVG", keyLv, slSwing) :
|
||||
StringFormat("MSS↑ @ H0=%.2f | SL L0=%.2f — quét M5 FVG", keyLv, slSwing);
|
||||
}
|
||||
|
||||
if(g_ictLowTf.mss.phase == ICT_MSS_CHOCH)
|
||||
|
||||
+39
-10
@@ -9,10 +9,11 @@
|
||||
#include <ICT2026/DailyBias.mqh>
|
||||
#include <ICT2026/IntradayStructure.mqh>
|
||||
#include <ICT2026/LowTfTrend.mqh>
|
||||
#include <ICT2026/EaState.mqh>
|
||||
|
||||
const string ICT26_PANEL_PFX = "ICT26_PNL_";
|
||||
const string ICT26_PANEL_LEGACY = "ICT26_BIAS_PANEL";
|
||||
const int ICT26_PANEL_MAXLINE = 14;
|
||||
const int ICT26_PANEL_MAXLINE = 16;
|
||||
|
||||
const color ICT_PANEL_CLR_UP = clrLime;
|
||||
const color ICT_PANEL_CLR_DOWN = clrOrangeRed;
|
||||
@@ -59,6 +60,16 @@ color IctPanelAllowTradeColor()
|
||||
return ICT_PANEL_CLR_NEUTRAL;
|
||||
}
|
||||
|
||||
color IctPanelEaStateColor()
|
||||
{
|
||||
switch(g_ictEaState.category)
|
||||
{
|
||||
case ICT_EA_CAT_TRADE: return clrAqua;
|
||||
case ICT_EA_CAT_SETUP: return clrGold;
|
||||
default: return ICT_PANEL_CLR_MUTED;
|
||||
}
|
||||
}
|
||||
|
||||
void IctPanel_Clear()
|
||||
{
|
||||
const long ch = ChartID();
|
||||
@@ -98,6 +109,7 @@ void IctPanel_Render(const string sym)
|
||||
}
|
||||
|
||||
IctIntraday_UpdateAllowTrade();
|
||||
IctEaState_Refresh(sym);
|
||||
|
||||
const long ch = ChartID();
|
||||
ObjectDelete(ch, ICT26_PANEL_LEGACY);
|
||||
@@ -108,6 +120,7 @@ void IctPanel_Render(const string sym)
|
||||
const color clrBias = IctPanelBiasDirectionColor(g_ictDailyBias.bias);
|
||||
const color clrIntra = IctPanelIntradayDirectionColor(g_ictIntraday.trend);
|
||||
const color clrAllow = IctPanelAllowTradeColor();
|
||||
const color clrEa = IctPanelEaStateColor();
|
||||
|
||||
string lines[];
|
||||
color colors[];
|
||||
@@ -116,6 +129,27 @@ void IctPanel_Render(const string sym)
|
||||
|
||||
int n = 0;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = StringFormat("[%s] %s", IctEaState_CategoryText(g_ictEaState.category),
|
||||
IctEaState_Code(g_ictEaState.state));
|
||||
colors[n++] = clrEa;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = IctEaState_TitleVi(g_ictEaState.state);
|
||||
colors[n++] = clrEa;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = StringFormat("Chi tiet: %s", g_ictEaState.detail);
|
||||
colors[n++] = clrEa;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = "-------------------------";
|
||||
colors[n++] = ICT_PANEL_CLR_NEUTRAL;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = StringFormat("ICT2026 %s | %s[0] chua dong", EnumToString(InpBiasTf), dTag);
|
||||
@@ -153,21 +187,16 @@ void IctPanel_Render(const string sym)
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = StringFormat("IsAllowTrade: %s",
|
||||
g_ictIntraday.isAllowTrade ? "true" : "false");
|
||||
lines[n] = StringFormat("AllowTrade: %s | MSS phase: %s",
|
||||
g_ictIntraday.isAllowTrade ? "YES" : "NO",
|
||||
IctMssPhaseText(g_ictLowTf.mss.phase));
|
||||
colors[n++] = clrAllow;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = StringFormat("iTF FVG: %s", g_ictLowTf.displayReason);
|
||||
lines[n] = StringFormat("H1 FVG: %s", g_ictLowTf.displayReason);
|
||||
colors[n++] = (g_ictLowTf.availableCount > 0) ? ICT_PANEL_CLR_ALLOW : ICT_PANEL_CLR_MUTED;
|
||||
|
||||
ArrayResize(lines, n + 1);
|
||||
ArrayResize(colors, n + 1);
|
||||
lines[n] = StringFormat("MSS: %s", g_ictLowTf.mss.displayReason);
|
||||
colors[n++] = (g_ictLowTf.mss.phase >= ICT_MSS_READY) ? ICT_PANEL_CLR_ALLOW :
|
||||
(g_ictLowTf.mss.phase > ICT_MSS_IDLE) ? clrGold : ICT_PANEL_CLR_MUTED;
|
||||
|
||||
for(int i = 0; i < n; i++)
|
||||
IctPanel_SetLine(ch, i, i * lh, lines[i], colors[i]);
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ MQL5/
|
||||
├── LowTfTrend.mqh ← iTF FVG khi IsAllowTrade
|
||||
├── Fvg.mqh ← detect / Available-Used
|
||||
├── FvgDraw.mqh ← vẽ FVG + Premium/Discount
|
||||
├── Panel.mqh ← bias + intraday + IsAllowTrade (góc trên)
|
||||
├── EaState.mqh ← state machine STOP / SETUP / TRADE
|
||||
├── Panel.mqh ← bias + intraday + EA state (góc trên)
|
||||
└── Draw.mqh ← label b*/i*/H0–L1 trên chart
|
||||
```
|
||||
|
||||
@@ -216,9 +217,13 @@ FVG Used **không** kéo dài P/D thêm; P/D độc lập với Used (theo swing
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| **Mặc định (`InpMssH1RetestWickOnly`)** | **Râu H1** chạm `[lower … upper]` là đủ — không cần thân nến xuyên FVG |
|
||||
| **Tùy chọn** | `InpMssH1RetestWickOnly=false` → thêm lấp ≥ `InpMssH1MinFillPct` |
|
||||
| **MSS M5** | Sau râu chạm POI → M5 pullback ngược cục bộ → **phá L0** (bear HTF) hoặc **phá H0** (bull HTF) |
|
||||
| **Retest** | **Giá chạm** FVG (H1/M5/bid-ask) — không cần râu H1 riêng |
|
||||
| **Giữ giá H1** | Râu xuyên FVG → tiếp tục chờ MSS M5 |
|
||||
| **AllowTrade** | H1 trend đồng thuận Bias — **không** đánh dấu FVG Used |
|
||||
| **FVG Used (fail)** | H1 body đóng xuyên FVG — không giữ giá |
|
||||
| **FVG Used (OK)** | Giữ giá + **MSS M5** khóa → Used, tiếp tục M5 FVG / entry trên cùng setup |
|
||||
| **AllowTrade=false** | Tạm dừng MSS pipeline; vẫn **watch** FVG cho H1 body fail |
|
||||
| **MSS M5** | Sau chạm FVG → M5 pullback → **phá L0** (bear) / **phá H0** (bull) |
|
||||
|
||||
**Định nghĩa MSS vs CHoCH (cùng rule Key H0/L0):**
|
||||
|
||||
@@ -234,6 +239,28 @@ CHoCH trên D/H1 = phá key level **ngược** xu hướng HTF (bull→bear phá
|
||||
|
||||
Sau retest FVG H1 OK → arm MSS trên M5.
|
||||
|
||||
### EA state machine (`EaState.mqh`, v1.131)
|
||||
|
||||
Panel luôn hiển thị 3 dòng đầu: **`[CATEGORY] CODE`**, tiêu đề, chi tiết.
|
||||
|
||||
| Category | State `CODE` | Khi nào |
|
||||
|----------|--------------|---------|
|
||||
| **STOP** | `STOP_NO_BIAS` | Daily bias = None |
|
||||
| **STOP** | `STOP_BIAS_RANGE` | Bias = Range |
|
||||
| **STOP** | `STOP_INTRADAY_NONE` | Bias Bull/Bear nhưng H1 trend = None |
|
||||
| **STOP** | `STOP_BIAS_INTRADAY_MISMATCH` | Bias ≠ H1 (vd Bull + H1 Down) |
|
||||
| **SETUP** | `WAIT_FVG_TOUCH` | AllowTrade, chờ retest FVG H1 |
|
||||
| **SETUP** | `FVG_TOUCHED_WAIT_MSS` | H1 retest + M5 trong FVG; live L0/H0 M5, chờ body phá L0 (bear) |
|
||||
| **SETUP** | `MSS_OK_WAIT_M5_FVG` | MSS khóa, chờ M5 FVG |
|
||||
| **SETUP** | `M5_FVG_WAIT_RETRACE` | Có M5 FVG, chờ hồi fill entry |
|
||||
| **SETUP** | `READY_FOR_LIMIT` | Giá trong vùng entry, sắp đặt limit |
|
||||
| **TRADE** | `LIMIT_ORDER_PENDING` | Có lệnh limit MSS magic |
|
||||
| **TRADE** | `ON_TRADE` | Có position MSS magic |
|
||||
|
||||
`ENUM_ICT_MSS_PHASE` (nội bộ) vẫn giữ; `IctEaState_Refresh()` map sang state trên sau mỗi `IctMss_Update` / `IctMssEntry_Update`.
|
||||
|
||||
API: `ICT2026_GetEaState()`, `ICT2026_GetEaStateCode()`, `ICT2026_GetEaStateDetail()`.
|
||||
|
||||
### MSS entry (Confirm TF, v1.115)
|
||||
|
||||
Khi `IsAllowTrade`, state machine `g_ictLowTf.mss`:
|
||||
@@ -539,6 +566,54 @@ ENUM_ICT_BIAS ICT2026_GetDailyBias();
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.138 — POI H1 = FVG chưa Used **gần giá nhất**
|
||||
|
||||
- `IctMss_SelectNearestH1Poi`: khoảng cách bid → FVG; bỏ qua Used; tie → FVG mới hơn
|
||||
- H1 FVG **không** auto-Used theo % fill (chỉ MSS fail/OK đánh dấu Used)
|
||||
- IDLE: luôn chọn lại POI gần nhất; chạm → `FVG_TOUCHED_WAIT_MSS`
|
||||
|
||||
### v1.137 — Fill FVG mặc định 25% (thay 38.2%)
|
||||
|
||||
- `InpFvgUsedFillPct`, `InpMssEntryFillPct`, `InpMssH1MinFillPct` = 25
|
||||
|
||||
### v1.136 — FVG Used khi MSS OK (giữ giá)
|
||||
|
||||
- MSS khóa → `IctMss_MarkFvgUsedOnMssSuccess` (POI đã phục vụ, tiếp M5 FVG/entry)
|
||||
- Used fail vs Used OK: cùng `ICT_FVG_USED`, journal khác nhau
|
||||
- Pipeline MSS/entry vẫn chạy sau Used nếu `chochLocked`
|
||||
|
||||
### v1.135 — Tách AllowTrade vs FVG Used
|
||||
|
||||
- `AllowTrade=false`: pause MSS (`ResetPipelineKeepWatch`), **không** Used FVG
|
||||
- `FVG Used`: chỉ khi H1 **body đóng xuyên** FVG (`IctMss_FailFvgH1Body`)
|
||||
- `h1WatchFvgId`: theo dõi invalidation kể cả khi H1 lệch Bias
|
||||
|
||||
### v1.134 — FVG Used khi H1 body xuyên; skip FVG Used trong target
|
||||
|
||||
- `IctMss_SelectTargetH1Fvg`: bỏ qua mọi FVG Used
|
||||
- MSS chỉ khóa nếu break sau `h1TouchTime`
|
||||
|
||||
### v1.133 — Retest = giá chạm FVG; H1 chỉ hủy khi body đóng xuyên
|
||||
|
||||
- Retest: `IctMss_HasFvgPriceTouch` (H1 touch / M5 overlap / giá live trong gap)
|
||||
- Không còn “chờ râu H1”; `InpMssH1RetestWickOnly` legacy (bỏ qua)
|
||||
- Hủy: `iClose` H1[1] xuyên FVG — râu xuyên vẫn chờ MSS
|
||||
|
||||
### v1.132 — MSS đơn giản: live L0/H0 M5 + hủy H1 body phá FVG
|
||||
|
||||
- `FVG_TOUCHED_WAIT_MSS`: cập nhật liên tục `liveL0`/`liveH0` từ `IctBuildConfirmSwingSet` (M5 bull khi bear HTF)
|
||||
- MSS bear: body phá **L0** hiện tại → vẽ MSS tại L0, SL tại H0
|
||||
- Hủy setup: mỗi nến H1 mới, body đóng **trên** FVG (bear) / **dưới** FVG (bull)
|
||||
- Vào setup: H1 retest + ít nhất một nến M5 overlap H1 FVG
|
||||
- Bỏ gate “MSS xa H1 FVG”; vẽ L0 live (nét chấm) trước khi khóa MSS
|
||||
|
||||
### v1.131 — EA state machine (STOP / SETUP / TRADE)
|
||||
|
||||
- `EaState.mqh`: `IctEaState_Refresh()`, panel 3 dòng state đầu
|
||||
- STOP: no bias, range, H1 none, bias≠H1
|
||||
- SETUP: WAIT_FVG_TOUCH → … → READY_FOR_LIMIT
|
||||
- TRADE: LIMIT_ORDER_PENDING, ON_TRADE
|
||||
|
||||
### v1.130 — MSS = phá L0/H0 M5 (định nghĩa CHoCH đúng)
|
||||
|
||||
- Bear HTF: hồi Bearish H1 FVG → M5 pullback (bull cục bộ) → **MSS↓ = body phá L0** (đáy tạo H0)
|
||||
@@ -564,7 +639,7 @@ ENUM_ICT_BIAS ICT2026_GetDailyBias();
|
||||
### v1.126 — Thuật ngữ: Retest FVG H1 (POI), không ghi chung “H1 retest”
|
||||
|
||||
- Panel/journal: “Retest FVG H1”, “Chờ retest FVG H1 … (lấp trên H1)”
|
||||
- `IctMss_HasH1FvgRetest`, `IctMss_TryLockMss`, `chochLocked`
|
||||
- `IctMss_FailFvgH1Body`, `IctMss_CheckH1WatchInvalidation`, `chochLocked`
|
||||
|
|
||||
|
||||
### v1.125 — MSS = phá swing M5 (confirm), build structure như chart
|
||||
|
||||
@@ -228,6 +228,12 @@ struct IctMssState
|
||||
double pendingEntry;
|
||||
double pendingSl;
|
||||
double pendingTp;
|
||||
double liveL0Price;
|
||||
double liveH0Price;
|
||||
datetime liveL0Time;
|
||||
datetime liveH0Time;
|
||||
datetime h1LastInvalidBarTime;
|
||||
ulong h1WatchFvgId;
|
||||
string displayReason;
|
||||
|
||||
void Clear()
|
||||
@@ -246,6 +252,12 @@ struct IctMssState
|
||||
pendingEntry = 0.0;
|
||||
pendingSl = 0.0;
|
||||
pendingTp = 0.0;
|
||||
liveL0Price = 0.0;
|
||||
liveH0Price = 0.0;
|
||||
liveL0Time = 0;
|
||||
liveH0Time = 0;
|
||||
h1LastInvalidBarTime = 0;
|
||||
h1WatchFvgId = 0;
|
||||
displayReason = "";
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user