diff --git a/Experts/ICT_2026.mq5 b/Experts/ICT_2026.mq5 index 8136de4..703aa43 100644 --- a/Experts/ICT_2026.mq5 +++ b/Experts/ICT_2026.mq5 @@ -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 @@ -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; } + //+------------------------------------------------------------------+ diff --git a/Include/ICT2026/Config.mqh b/Include/ICT2026/Config.mqh index 7618abf..b897ec0 100644 --- a/Include/ICT2026/Config.mqh +++ b/Include/ICT2026/Config.mqh @@ -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) diff --git a/Include/ICT2026/Fvg.mqh b/Include/ICT2026/Fvg.mqh index 1599bc5..603ab1c 100644 --- a/Include/ICT2026/Fvg.mqh +++ b/Include/ICT2026/Fvg.mqh @@ -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); diff --git a/Include/ICT2026/LowTfTrend.mqh b/Include/ICT2026/LowTfTrend.mqh index aff0019..2be52e8 100644 --- a/Include/ICT2026/LowTfTrend.mqh +++ b/Include/ICT2026/LowTfTrend.mqh @@ -11,6 +11,7 @@ #include #include #include +#include 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(); diff --git a/Include/ICT2026/MssDraw.mqh b/Include/ICT2026/MssDraw.mqh index dee80e9..7a7e91f 100644 --- a/Include/ICT2026/MssDraw.mqh +++ b/Include/ICT2026/MssDraw.mqh @@ -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); } diff --git a/Include/ICT2026/MssSetup.mqh b/Include/ICT2026/MssSetup.mqh index ed8b4b8..0a7d624 100644 --- a/Include/ICT2026/MssSetup.mqh +++ b/Include/ICT2026/MssSetup.mqh @@ -7,6 +7,8 @@ #include #include #include +#include +#include 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) diff --git a/Include/ICT2026/Panel.mqh b/Include/ICT2026/Panel.mqh index 8a2a8cf..9fb397d 100644 --- a/Include/ICT2026/Panel.mqh +++ b/Include/ICT2026/Panel.mqh @@ -9,10 +9,11 @@ #include #include #include +#include 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]); diff --git a/Include/ICT2026/README.md b/Include/ICT2026/README.md index 1b5a59a..f2aed4f 100644 --- a/Include/ICT2026/README.md +++ b/Include/ICT2026/README.md @@ -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 diff --git a/Include/ICT2026/Types.mqh b/Include/ICT2026/Types.mqh index a75e902..2f7710f 100644 --- a/Include/ICT2026/Types.mqh +++ b/Include/ICT2026/Types.mqh @@ -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 = ""; } };