diff --git a/Experts/ICT_2026.mq5 b/Experts/ICT_2026.mq5 index f2f1d67..04d3de8 100644 --- a/Experts/ICT_2026.mq5 +++ b/Experts/ICT_2026.mq5 @@ -3,8 +3,8 @@ //| BOS = Continue | CHoCH = Reversal | //+------------------------------------------------------------------+ #property copyright "ICT 2026" -#property version "1.142" -#property description "ICT2026 MSS limit entry + SL swing CHoCH + TP intraday" +#property version "1.151" +#property description "ICT2026 MSS | SL = swing + ATR + spread | TP = iL0/iH0 + ATR − 2×spread (dễ khớp)" #include #include diff --git a/Include/ICT2026/Config.mqh b/Include/ICT2026/Config.mqh index 3bc3f2e..dd122a5 100644 --- a/Include/ICT2026/Config.mqh +++ b/Include/ICT2026/Config.mqh @@ -35,7 +35,7 @@ input int InpChartLabelFontSize = 9; input group "══ Confirm swing (M5) ══" input ENUM_TIMEFRAMES InpConfirmTf = PERIOD_M5; // TF confirmation (tương lai) -input int InpConfirmSwingRange = 1; +input int InpConfirmSwingRange = 2; // M5 swing strength: N bar mỗi bên (5-bar fractal khi =2) input int InpConfirmSwingLookback = 80; input int InpConfirmRecentBars = 40; @@ -77,8 +77,12 @@ input group "══ MSS Trade ══" input bool InpMssTradeEnabled = true; // Đặt lệnh limit MSS input ulong InpMssMagic = 202604; // Magic number input double InpMssRiskPct = 1.0; // R % balance mỗi lệnh -input double InpMssSlAtrMult = 0.5; // SL: buffer ngoài H0/L0 M5 (× ATR M5) -input double InpMssMinRR = 2.0; // TP tối thiểu (× risk entry→SL) +input ENUM_TIMEFRAMES InpMssSlAtrTf = PERIOD_M5; // TF tính ATR cho buffer SL +input ENUM_TIMEFRAMES InpMssTpAtrTf = PERIOD_M5; // TF tính ATR cho buffer TP +input int InpMssBufferAtrPeriod = 14; // Period ATR cho buffer SL/TP +input double InpMssSlAtrMult = 2.0; // SL: buffer ngoài max(H0,H1)/min(L0,L1) (× ATR M5) — tránh spike đỉnh +input double InpMssMinRR = 1.5; // TP tối thiểu (× risk entry→SL) — block trade nếu iL0/iH0 không đạt +input double InpMssTpAtrBuffer = 2.0; // TP buffer trước iL0/iH0 (× ATR M5) — chốt trước vùng cản input int InpMssPendingExpireHours = 24; // Hết hạn pending (giờ, 0=không) input bool InpMssOnePosition = true; // Một position/pending MSS input bool InpMssCancelPendingEod = true; // Hủy pending cuối phiên Mỹ (nếu chưa khớp) diff --git a/Include/ICT2026/MssEntry.mqh b/Include/ICT2026/MssEntry.mqh index 9d96236..55ad491 100644 --- a/Include/ICT2026/MssEntry.mqh +++ b/Include/ICT2026/MssEntry.mqh @@ -103,17 +103,54 @@ bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone, return false; } - const double atrM5 = IctMssEntry_Atr(sym, InpConfirmTf, InpFvgAtrPeriod); - const double bufSl = (atrM5 > 0.0) ? atrM5 * InpMssSlAtrMult : 10.0 * _Point; + // SL dùng ATR M5 (sát swing M5, không quá rộng để giữ risk thấp + RR đạt minRR) + // TP dùng ATR H1 (trung bình sóng H1 đang đánh, buffer rõ ràng trên chart) + double atrSl = IctMssEntry_Atr(sym, InpMssSlAtrTf, InpMssBufferAtrPeriod); + if(atrSl <= 0.0) + atrSl = IctMssEntry_Atr(sym, InpConfirmTf, InpFvgAtrPeriod); + double atrTp = IctMssEntry_Atr(sym, InpMssTpAtrTf, InpMssBufferAtrPeriod); + if(atrTp <= 0.0) + atrTp = atrSl; + + // SL: cộng spread (stop trigger theo ask/bid — bù để khỏi bị quét sớm) + // TP: trừ 2×spread (TP gần entry hơn để dễ khớp — không cần giá hit chính xác iL0/iH0) + const double spreadNow = MathMax(0.0, + SymbolInfoDouble(sym, SYMBOL_ASK) - + SymbolInfoDouble(sym, SYMBOL_BID)); + + const double bufSl = ((atrSl > 0.0) ? atrSl * InpMssSlAtrMult : 100.0 * _Point) + spreadNow; + const double bufTp = ((atrTp > 0.0) ? atrTp * InpMssTpAtrBuffer : 50.0 * _Point) + 2.0 * spreadNow; const double minRR = MathMax(1.0, InpMssMinRR); + // ── TP target = iL0 (BEAR) / iH0 (BULL): sóng H1, m5 chỉ để entry + // TP đặt CÁCH target một buffer (gần chạm — không chờ hit chính xác) + const IctSwingSet iSw = g_ictIntraday.swings; + double tpTarget = 0.0; + if(isBuy && iSw.hasH0) + tpTarget = iSw.h0.price; + else if(!isBuy && iSw.hasL0) + tpTarget = iSw.l0.price; + if(isBuy) { slOut = swingSl - bufSl; const double risk = entryOut - slOut; if(risk <= _Point) return false; - tpOut = entryOut + risk * minRR; + + if(tpTarget > 0.0 && tpTarget > entryOut + bufTp + _Point) + tpOut = tpTarget - bufTp; + else + tpOut = entryOut + risk * minRR; // fallback nếu iH0 không có hoặc quá gần + + const double rrAchieved = (tpOut - entryOut) / risk; + if(rrAchieved < minRR - 0.01) + { + g_ictLowTf.mss.displayReason = StringFormat( + "RR=%.2f < min %.2f (entry=%.2f SL=%.2f TP=%.2f) — chốt iH0 không đủ", + rrAchieved, minRR, entryOut, slOut, tpOut); + return false; + } } else { @@ -121,7 +158,20 @@ bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone, const double risk = slOut - entryOut; if(risk <= _Point) return false; - tpOut = entryOut - risk * minRR; + + if(tpTarget > 0.0 && tpTarget < entryOut - bufTp - _Point) + tpOut = tpTarget + bufTp; + else + tpOut = entryOut - risk * minRR; // fallback nếu iL0 không có hoặc quá gần + + const double rrAchieved = (entryOut - tpOut) / risk; + if(rrAchieved < minRR - 0.01) + { + g_ictLowTf.mss.displayReason = StringFormat( + "RR=%.2f < min %.2f (entry=%.2f SL=%.2f TP=%.2f) — chốt iL0 không đủ", + rrAchieved, minRR, entryOut, slOut, tpOut); + return false; + } } entryOut = IctMssEntry_NormalizePrice(sym, entryOut); diff --git a/Include/ICT2026/MssSetup.mqh b/Include/ICT2026/MssSetup.mqh index 0a7d624..cc15412 100644 --- a/Include/ICT2026/MssSetup.mqh +++ b/Include/ICT2026/MssSetup.mqh @@ -272,26 +272,17 @@ bool IctMss_UpdateLiveM5Swings(const string sym, const ENUM_ICT_BIAS bias, g_ictLowTf.mss.liveH0Price = 0.0; g_ictLowTf.mss.liveL0Time = 0; g_ictLowTf.mss.liveH0Time = 0; + g_ictLowTf.mss.liveL1Price = 0.0; + g_ictLowTf.mss.liveH1Price = 0.0; + g_ictLowTf.mss.liveL1Time = 0; + g_ictLowTf.mss.liveH1Time = 0; IctSwingSet sw; ENUM_ICT_STRUCT st = ICT_STRUCT_NONE; if(!IctBuildConfirmSwingSet(sym, sw, st)) return false; - if(bias == ICT_BIAS_BEAR) - { - if(!sw.hasL0 || !sw.hasH0) - return false; - if(sw.l0.time < tTouch && sw.h0.time < tTouch) - return false; - 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) + if(bias == ICT_BIAS_BEAR || bias == ICT_BIAS_BULL) { if(!sw.hasL0 || !sw.hasH0) return false; @@ -301,6 +292,16 @@ bool IctMss_UpdateLiveM5Swings(const string sym, const ENUM_ICT_BIAS bias, g_ictLowTf.mss.liveL0Time = sw.l0.time; g_ictLowTf.mss.liveH0Price = sw.h0.price; g_ictLowTf.mss.liveH0Time = sw.h0.time; + if(sw.hasL1) + { + g_ictLowTf.mss.liveL1Price = sw.l1.price; + g_ictLowTf.mss.liveL1Time = sw.l1.time; + } + if(sw.hasH1) + { + g_ictLowTf.mss.liveH1Price = sw.h1.price; + g_ictLowTf.mss.liveH1Time = sw.h1.time; + } return true; } @@ -388,9 +389,14 @@ bool IctMss_TryLockMss(const string sym, const ENUM_TIMEFRAMES tf, if(breakT < tTouch) return false; + // SL trên cao nhất giữa H0 và H1 (cap toàn pullback bull) + buffer ATR (cộng ở MssEntry) + double slSwingHi = g_ictLowTf.mss.liveH0Price; + if(g_ictLowTf.mss.liveH1Price > slSwingHi) + slSwingHi = g_ictLowTf.mss.liveH1Price; + g_ictLowTf.mss.chochKeyLevel = g_ictLowTf.mss.liveL0Price; g_ictLowTf.mss.chochKeyTime = g_ictLowTf.mss.liveL0Time; - g_ictLowTf.mss.slSwingPrice = g_ictLowTf.mss.liveH0Price; + g_ictLowTf.mss.slSwingPrice = slSwingHi; } else if(bias == ICT_BIAS_BULL) { @@ -400,9 +406,14 @@ bool IctMss_TryLockMss(const string sym, const ENUM_TIMEFRAMES tf, if(breakT < tTouch) return false; + // SL dưới thấp nhất giữa L0 và L1 (đáy toàn pullback bear) + buffer ATR (trừ ở MssEntry) + double slSwingLo = g_ictLowTf.mss.liveL0Price; + if(g_ictLowTf.mss.liveL1Price > 0.0 && g_ictLowTf.mss.liveL1Price < slSwingLo) + slSwingLo = g_ictLowTf.mss.liveL1Price; + g_ictLowTf.mss.chochKeyLevel = g_ictLowTf.mss.liveH0Price; g_ictLowTf.mss.chochKeyTime = g_ictLowTf.mss.liveH0Time; - g_ictLowTf.mss.slSwingPrice = g_ictLowTf.mss.liveL0Price; + g_ictLowTf.mss.slSwingPrice = slSwingLo; } else return false; @@ -449,12 +460,20 @@ bool IctMss_GetConfirmMssSwing(const string sym, const ENUM_ICT_BIAS bias, doubl { if(bias == ICT_BIAS_BEAR && sw.hasH0) { - swingOut = sw.h0.price; + // BEAR SL: max(H0, H1) — cap toàn pullback bull + double hi = sw.h0.price; + if(sw.hasH1 && sw.h1.price > hi) + hi = sw.h1.price; + swingOut = hi; return true; } if(bias == ICT_BIAS_BULL && sw.hasL0) { - swingOut = sw.l0.price; + // BULL SL: min(L0, L1) — đáy toàn pullback bear + double lo = sw.l0.price; + if(sw.hasL1 && sw.l1.price < lo) + lo = sw.l1.price; + swingOut = lo; return true; } } @@ -466,12 +485,20 @@ bool IctMss_GetConfirmMssSwing(const string sym, const ENUM_ICT_BIAS bias, doubl const int nL = ArraySize(lows); if(bias == ICT_BIAS_BEAR && nH > 0) { - swingOut = highs[nH - 1].price; + // max của 2 swing high gần nhất + double hi = highs[nH - 1].price; + if(nH >= 2 && highs[nH - 2].price > hi) + hi = highs[nH - 2].price; + swingOut = hi; return true; } if(bias == ICT_BIAS_BULL && nL > 0) { - swingOut = lows[nL - 1].price; + // min của 2 swing low gần nhất + double lo = lows[nL - 1].price; + if(nL >= 2 && lows[nL - 2].price < lo) + lo = lows[nL - 2].price; + swingOut = lo; return true; } return false; diff --git a/Include/ICT2026/Types.mqh b/Include/ICT2026/Types.mqh index 2f7710f..cb495d8 100644 --- a/Include/ICT2026/Types.mqh +++ b/Include/ICT2026/Types.mqh @@ -232,6 +232,10 @@ struct IctMssState double liveH0Price; datetime liveL0Time; datetime liveH0Time; + double liveL1Price; + double liveH1Price; + datetime liveL1Time; + datetime liveH1Time; datetime h1LastInvalidBarTime; ulong h1WatchFvgId; string displayReason; @@ -256,6 +260,10 @@ struct IctMssState liveH0Price = 0.0; liveL0Time = 0; liveH0Time = 0; + liveL1Price = 0.0; + liveH1Price = 0.0; + liveL1Time = 0; + liveH1Time = 0; h1LastInvalidBarTime = 0; h1WatchFvgId = 0; displayReason = "";