From 2635379ef6c404565416379084ad3727495666bc Mon Sep 17 00:00:00 2001 From: Bell Date: Mon, 15 Dec 2025 05:31:17 +0700 Subject: [PATCH] =?UTF-8?q?Update=20FVG=20used,=20logic=20trend,=20mss=20c?= =?UTF-8?q?ho=20ltf=20m=E1=BB=9Bi,=20entry=20t=E1=BA=A1i=20ltf=20fvg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Experts/EA_ICT.txt | 9 +- Experts/EA_ICT_v2.mq5 | 456 +++++++++++++++++++++--------------------- 2 files changed, 233 insertions(+), 232 deletions(-) diff --git a/Experts/EA_ICT.txt b/Experts/EA_ICT.txt index 42ee8e1..663cd53 100644 --- a/Experts/EA_ICT.txt +++ b/Experts/EA_ICT.txt @@ -64,4 +64,11 @@ ltfSwingRange = 5; - R:R = 1:5 -> Profit 53% | Drawdown 22% - R:R = 1:6 -> Profit 55% | Drawdown 22% - R:R = 1:7 -> Profit % | Drawdown % -- R:R = 1:8 -> Profit % | Drawdown % \ No newline at end of file +- R:R = 1:8 -> Profit % | Drawdown % + +=== + +Issue: + +- WatchingMSSMode bị bật lại khi giá chạm ltf fvg -> tổ chức fvg đang sai, fvg bị find và reset liên tục +- Bị miss mss \ No newline at end of file diff --git a/Experts/EA_ICT_v2.mq5 b/Experts/EA_ICT_v2.mq5 index dee5021..9ec91ed 100644 --- a/Experts/EA_ICT_v2.mq5 +++ b/Experts/EA_ICT_v2.mq5 @@ -14,15 +14,15 @@ input double RishPercent = 1.0; // % vốn rủi ro cho mỗi lệnh input double RiskRewardRatio = 3.0; // tỉ lệ R:R mặc định input double moveSLRange = 1; // Nomal moving SL: số R cần đạt để dời SL về entry (BE) -input int MaxLimitOrderTime = 120; +input int MaxLimitOrderTime = 180; input double moveSLStartR = 3; // Advanced moving SL: bắt đầu kích hoạt trailing input double trailOffsetR = 3; // Advanced moving SL: khoảng cách SL so với giá hiện tại // Cấu hình Swing input int htfSwingRange = 2; // X: số nến trước và sau để xác định 1 đỉnh/đáy -input int mtfSwingRange = 3; // X: số nến trước và sau để xác định 1 đỉnh/đáy -input int ltfSwingRange = 4; // X: số nến trước và sau để xác định 1 đỉnh/đáy +input int mtfSwingRange = 2; // X: số nến trước và sau để xác định 1 đỉnh/đáy +input int ltfSwingRange = 2; // X: số nến trước và sau để xác định 1 đỉnh/đáy input int MaxSwingKeep = 2; // Số đỉnh/đáy gần nhất cần lưu (bạn yêu cầu 2) // Struct pending entry (single slot) @@ -54,11 +54,6 @@ bool MTF_FVG_Touched[]; // true = MTF FVG k đã bị LTF chạm datetime MTF_FVG_TouchTime[]; // thời điểm bar LTF đầu tiên chạm zone (datetime) double MTF_FVG_TouchPrice[]; // giá chạm đại diện (low khi bullish, high khi bearish) -// Bật/tắt MSS detection cho từng TF -input bool DetectMSS_HTF = false; -input bool DetectMSS_MTF = false; -input bool DetectMSS_LTF = true; - // Cấu hình vẽ Swing trên chart input bool ShowSwingMarkers = true; // Hiển thị các marker swing trên chart input string SwingObjPrefix = "Swing_"; // Tiền tố tên object (dễ xóa/điều chỉnh) @@ -100,12 +95,13 @@ struct FVG datetime timebarA; // time of bar A (older) datetime timebarC; // time of bar C (newer) bool touched; // whether LTF touched this MTF FVG yet (first-touch) + bool used; // whether this FVG has been used for entry already datetime touchTime; // time of first touch (on LowTF) double touchPrice; // representative touch price (edge) }; -FVG FVGs[]; -int FVG_count = 0; // number of FVGs found (kept for compatibility) +FVG MTF_FVGs[]; +int MTF_FVG_count = 0; // number of FVGs found (kept for compatibility) // last closed bar time per slot (index 0=HighTF,1=MiddleTF,2=LowTF) @@ -159,23 +155,23 @@ bool BOS_HaveZone[3][2]; // flag có zone hay không void UpdateMTFFVGTouched(string symbol) { - if(FVG_count <= 0) return; + if(MTF_FVG_count <= 0) return; double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double tol = (point > 0.0) ? point * 0.5 : 0.0; // scan each stored FVG - for(int fvgIndex = 0; fvgIndex < FVG_count; fvgIndex++) + for(int fvgIndex = 0; fvgIndex < MTF_FVG_count; fvgIndex++) { // nếu đã touched trước đó -> bỏ qua luôn (không in gì cả) - if(FVGs[fvgIndex].touched) continue; + if(MTF_FVGs[fvgIndex].touched) continue; - datetime mtfC = FVGs[fvgIndex].timebarC; + datetime mtfC = MTF_FVGs[fvgIndex].timebarC; if(mtfC == 0) continue; - double top = FVGs[fvgIndex].topPrice; - double bottom = FVGs[fvgIndex].bottomPrice; - int dir = FVGs[fvgIndex].type; // 1=bull, -1=bear + double top = MTF_FVGs[fvgIndex].topPrice; + double bottom = MTF_FVGs[fvgIndex].bottomPrice; + int dir = MTF_FVGs[fvgIndex].type; // 1=bull, -1=bear // find index of mtfC on LowTF int idxC_on_LTF = iBarShift(symbol, LowTF, mtfC, false); @@ -218,16 +214,14 @@ void UpdateMTFFVGTouched(string symbol) if(touchedNow) { // chỉ set + print khi trước đó chưa touched (điều kiện đã đảm bảo vì ta continue ở trên nếu touched==true) - FVGs[fvgIndex].touched = true; - FVGs[fvgIndex].touchTime = touch_time; - FVGs[fvgIndex].touchPrice = touch_price; + MTF_FVGs[fvgIndex].touched = true; + MTF_FVGs[fvgIndex].touchTime = touch_time; + MTF_FVGs[fvgIndex].touchPrice = touch_price; // enable watchingMSSMode when any MTF FVG is first touched - if(!watchingMSSMode) + if(!watchingMSSMode && !MTF_FVGs[fvgIndex].used) { - watchingMSSMode = true; - watchingFVGIndex = fvgIndex; - watchingFVGDir = dir; + ToggleWatchingMSSMode(true, fvgIndex, dir); if(PrintEntryLog) PrintFormat("watchingMSSMode ENABLED for FVG idx=%d dir=%d", watchingFVGIndex, watchingFVGDir); } @@ -235,12 +229,19 @@ void UpdateMTFFVGTouched(string symbol) } } +void ToggleWatchingMSSMode(bool enable, int fvgIndex = -1, int fvgDir = 0) +{ + watchingMSSMode = enable; + watchingFVGIndex = fvgIndex; + watchingFVGDir = fvgDir; +} + // Kiểm tra điều kiện vô hiệu hoá watchingMSSMode: // 1) last closed candle trên LowTF đóng dưới cạnh dưới của bullish FVG // 2) last closed candle trên LowTF đóng trên cạnh trên của bearish FVG void CheckWatchMSSInvalidation(string symbol) { - if(!watchingMSSMode || watchingFVGIndex < 0 || watchingFVGIndex >= FVG_count) return; + if(!watchingMSSMode || watchingFVGIndex < 0 || watchingFVGIndex >= MTF_FVG_count) return; // Lấy last closed close trên LowTF (index 1) double lastClose = iClose(symbol, LowTF, 1); @@ -253,10 +254,10 @@ void CheckWatchMSSInvalidation(string symbol) int dir = watchingFVGDir; // bảo đảm FVG arrays còn hợp lệ - if(idx < 0 || idx >= FVG_count) return; + if(idx < 0 || idx >= MTF_FVG_count) return; - double top = FVGs[idx].topPrice; - double bottom = FVGs[idx].bottomPrice; + double top = MTF_FVGs[idx].topPrice; + double bottom = MTF_FVGs[idx].bottomPrice; // Nếu bullish FVG (dir == 1): invalid khi đóng dưới bottom if(dir == 1) @@ -264,10 +265,7 @@ void CheckWatchMSSInvalidation(string symbol) if(lastClose < bottom - tol) { if(PrintEntryLog) PrintFormat("watchingMSSMode DISABLED: bullish FVG idx=%d invalidated by close=%.5f < bottom=%.5f", idx, lastClose, bottom); - // clear - watchingMSSMode = false; - watchingFVGIndex = -1; - watchingFVGDir = 0; + ToggleWatchingMSSMode(false); } } else if(dir == -1) // bearish: invalid khi đóng trên top @@ -276,9 +274,7 @@ void CheckWatchMSSInvalidation(string symbol) { if(PrintEntryLog) PrintFormat("watchingMSSMode DISABLED: bearish FVG idx=%d invalidated by close=%.5f > top=%.5f", idx, lastClose, top); // clear - watchingMSSMode = false; - watchingFVGIndex = -1; - watchingFVGDir = 0; + ToggleWatchingMSSMode(false); } } } @@ -330,7 +326,7 @@ void StoreNewBOS(int slot, const BOSInfo &bos) } // Vẽ lại tất cả BOS cho slot (DrawAllBOSForSlot sẽ tạo lại objects và ghi BOS_Names) - DrawAllBOSForSlot(slot); + // DrawAllBOSForSlot(slot); } // Vẽ tất cả BOS đã lưu cho 1 slot (0=HTF,1=MTF,2=LTF) @@ -1294,6 +1290,14 @@ void UpdateTrendForSlot(int slot, ENUM_TIMEFRAMES timeframe, string symbol) { int oldTrend = TrendTF[slot]; + // ===== LTF: KHÔNG TỰ TÍNH TREND ===== + if(slot == 2) + { + // mirror MTF hoặc bỏ luôn + TrendTF[slot] = TrendTF[1]; + return; + } + double pip = GetPipSize(symbol); double provisionalThresh = (double)ProvisionalBreakPips * pip; double point = SymbolInfoDouble(symbol, SYMBOL_POINT); @@ -1362,8 +1366,8 @@ void UpdateTrendForSlot(int slot, ENUM_TIMEFRAMES timeframe, string symbol) int FindFVG(string symbol, ENUM_TIMEFRAMES timeframe, int lookback) { // clear old array - ArrayFree(FVGs); - FVG_count = 0; + ArrayFree(MTF_FVGs); + MTF_FVG_count = 0; int total = iBars(symbol, timeframe); if(total < 5) return 0; @@ -1394,22 +1398,23 @@ int FindFVG(string symbol, ENUM_TIMEFRAMES timeframe, int lookback) double top = lowC; double bottom = highA; bool dup = false; - for(int k = 0; k < FVG_count; k++) + for(int k = 0; k < MTF_FVG_count; k++) { - if(MathAbs(FVGs[k].topPrice - top) <= tol && MathAbs(FVGs[k].bottomPrice - bottom) <= tol) { dup = true; break; } + if(MathAbs(MTF_FVGs[k].topPrice - top) <= tol && MathAbs(MTF_FVGs[k].bottomPrice - bottom) <= tol) { dup = true; break; } } if(!dup) { - ArrayResize(FVGs, FVG_count+1); - FVGs[FVG_count].type = 1; - FVGs[FVG_count].topPrice = top; - FVGs[FVG_count].bottomPrice = bottom; - FVGs[FVG_count].timebarA = iTime(symbol, timeframe, idxA); - FVGs[FVG_count].timebarC = iTime(symbol, timeframe, idxC); - FVGs[FVG_count].touched = false; - FVGs[FVG_count].touchTime = 0; - FVGs[FVG_count].touchPrice= 0.0; - FVG_count++; + ArrayResize(MTF_FVGs, MTF_FVG_count+1); + MTF_FVGs[MTF_FVG_count].type = 1; + MTF_FVGs[MTF_FVG_count].topPrice = top; + MTF_FVGs[MTF_FVG_count].bottomPrice = bottom; + MTF_FVGs[MTF_FVG_count].timebarA = iTime(symbol, timeframe, idxA); + MTF_FVGs[MTF_FVG_count].timebarC = iTime(symbol, timeframe, idxC); + MTF_FVGs[MTF_FVG_count].touched = false; + MTF_FVGs[MTF_FVG_count].used = false; + MTF_FVGs[MTF_FVG_count].touchTime = 0; + MTF_FVGs[MTF_FVG_count].touchPrice= 0.0; + MTF_FVG_count++; } continue; } @@ -1420,27 +1425,27 @@ int FindFVG(string symbol, ENUM_TIMEFRAMES timeframe, int lookback) double top = lowA; double bottom = highC; bool dup = false; - for(int k = 0; k < FVG_count; k++) + for(int k = 0; k < MTF_FVG_count; k++) { - if(MathAbs(FVGs[k].topPrice - top) <= tol && MathAbs(FVGs[k].bottomPrice - bottom) <= tol) { dup = true; break; } + if(MathAbs(MTF_FVGs[k].topPrice - top) <= tol && MathAbs(MTF_FVGs[k].bottomPrice - bottom) <= tol) { dup = true; break; } } if(!dup) { - ArrayResize(FVGs, FVG_count+1); - FVGs[FVG_count].type = -1; - FVGs[FVG_count].topPrice = top; - FVGs[FVG_count].bottomPrice = bottom; - FVGs[FVG_count].timebarA = iTime(symbol, timeframe, idxA); - FVGs[FVG_count].timebarC = iTime(symbol, timeframe, idxC); - FVGs[FVG_count].touched = false; - FVGs[FVG_count].touchTime = 0; - FVGs[FVG_count].touchPrice= 0.0; - FVG_count++; + ArrayResize(MTF_FVGs, MTF_FVG_count+1); + MTF_FVGs[MTF_FVG_count].type = -1; + MTF_FVGs[MTF_FVG_count].topPrice = top; + MTF_FVGs[MTF_FVG_count].bottomPrice = bottom; + MTF_FVGs[MTF_FVG_count].timebarA = iTime(symbol, timeframe, idxA); + MTF_FVGs[MTF_FVG_count].timebarC = iTime(symbol, timeframe, idxC); + MTF_FVGs[MTF_FVG_count].touched = false; + MTF_FVGs[MTF_FVG_count].touchTime = 0; + MTF_FVGs[MTF_FVG_count].touchPrice= 0.0; + MTF_FVG_count++; } } } - return FVG_count; + return MTF_FVG_count; } uint MakeARGB(int a, uint clr) @@ -1462,19 +1467,19 @@ void DrawFVG(string symbol, ENUM_TIMEFRAMES timeframe, bool startFromCBar = true ObjectDelete(0, nm); } - if(FVG_count <= 0) return; + if(MTF_FVG_count <= 0) return; datetime bar_secs = (datetime)PeriodSeconds(timeframe); double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double tol = (point > 0.0) ? point * 0.5 : 0.0; - for(int k = 0; k < FVG_count; k++) + for(int k = 0; k < MTF_FVG_count; k++) { - double top = FVGs[k].topPrice; - double bottom = FVGs[k].bottomPrice; - datetime timeC = FVGs[k].timebarC; - datetime timeA = FVGs[k].timebarA; + double top = MTF_FVGs[k].topPrice; + double bottom = MTF_FVGs[k].bottomPrice; + datetime timeC = MTF_FVGs[k].timebarC; + datetime timeA = MTF_FVGs[k].timebarA; datetime start_time = startFromCBar ? timeC : timeA; @@ -1522,7 +1527,7 @@ void DrawFVG(string symbol, ENUM_TIMEFRAMES timeframe, bool startFromCBar = true uint border_alpha = 0; uint color_fill; - if(FVGs[k].type == 1) color_fill = MakeARGB((int)fill_alpha, clrDodgerBlue); + if(MTF_FVGs[k].type == 1) color_fill = MakeARGB((int)fill_alpha, clrDodgerBlue); else color_fill = MakeARGB((int)fill_alpha, clrCrimson); uint col_border = MakeARGB((int)border_alpha, clrBlack); @@ -1658,14 +1663,15 @@ void DetectMSSOnTimeframe( string sym, ENUM_TIMEFRAMES tf, int slot, - bool enabled, bool requireFVG, double minBreakPips, int consecRequired, int lookForwardBars, int fvgLookback ) { - if(!enabled) return; + // ===== REQUIRE WATCHING FVG BEFORE MSS ===== + if(!watchingMSSMode) return; // chỉ xét MSS khi đang ở watching mode + if(tf != LowTF) return; // chỉ xét MSS trên LTF if(BOS_Count[slot] < 2) return; // =============================== @@ -1744,9 +1750,6 @@ void DetectMSSOnTimeframe( // =============================== if(requireFVG) { - if(tf == MiddleTF) - EnsureFVGUpToDate(sym, tf, fvgLookback); - bool hasFVG = false; if(newerBOS.direction == 1) hasFVG = HasBullFVGBetween(sym, tf, sweep_time, break_time, fvgLookback); @@ -1793,13 +1796,10 @@ void DetectMSSOnTimeframe( if(valid) { if(PrintEntryLog) - Print(">>> ENTRY CONDITIONS PASSED → SETUP ENTRY"); - - watchingMSSMode = false; - watchingFVGIndex = -1; - watchingFVGDir = 0; - + Print(">>> ENTRY CONDITIONS PASSED → SETUP ENTRY. Disable watchingMSSMode."); SetUpPendingEntryForMSS(mss, slot); + MTF_FVGs[watchingFVGIndex].used = true; + ToggleWatchingMSSMode(false); } else { @@ -1836,14 +1836,14 @@ bool CheckValidEntry( return false; } - if(fvgIndex < 0 || fvgIndex >= FVG_count) + if(fvgIndex < 0 || fvgIndex >= MTF_FVG_count) { if(PrintEntryLog) Print("CheckValidEntry: invalid FVG index"); return false; } - int fvgDir = FVGs[fvgIndex].type; // 1 bull, -1 bear + int fvgDir = MTF_FVGs[fvgIndex].type; // 1 bull, -1 bear // ============================= // 1. MSS direction == FVG direction @@ -1900,7 +1900,7 @@ bool CheckValidEntry( return true; } -void HandleLogicForTimeframe(string sym, ENUM_TIMEFRAMES tf, int slot, bool detectMSS, +void HandleLogicForTimeframe(string sym, ENUM_TIMEFRAMES tf, int slot, int swingRange, bool requireFVG, double minBreakPips, int minConsec, int lookbackBarsForSweep, int fvgLookback) @@ -1910,7 +1910,7 @@ void HandleLogicForTimeframe(string sym, ENUM_TIMEFRAMES tf, int slot, bool dete HandleBOSDetections(sym, tf, slot); if(tf == MiddleTF) EnsureFVGUpToDate(sym, MiddleTF, fvgLookback); - DetectMSSOnTimeframe(sym, tf, slot, detectMSS, requireFVG, minBreakPips, minConsec, lookbackBarsForSweep, fvgLookback); + DetectMSSOnTimeframe(sym, tf, slot, requireFVG, minBreakPips, minConsec, lookbackBarsForSweep, fvgLookback); if(ShowSwingMarkers) { @@ -1918,19 +1918,21 @@ void HandleLogicForTimeframe(string sym, ENUM_TIMEFRAMES tf, int slot, bool dete if(tf == MiddleTF) DrawFVG(sym, MiddleTF, true); } - UpdateLabel((tf == HighTF) ? "LBL_HTF" : (tf == MiddleTF) ? "LBL_MTF" : "LBL_LTF", tf, TrendTF[slot]); + UpdateLabel((tf == HighTF) ? "LBL_HTF" : (tf == MiddleTF) ? "LBL_MTF" : "LTF: TRIGGER", tf, TrendTF[slot]); } // Clear & reset pending entry (xóa visuals nếu có) void ClearPendingEntry() { - if(!pendingEntry.active && StringLen(pendingEntry.compositeName)==0) - { + if(pendingEntry.fvgIndex >= 0 && pendingEntry.fvgIndex < MTF_FVG_count) { + MTF_FVGs[pendingEntry.fvgIndex].used = true; // FVG đã cho entry nhưng không khớp lệnh + } + + if(!pendingEntry.active && StringLen(pendingEntry.compositeName)==0) { return; } - if(StringLen(pendingEntry.compositeName) > 0) - { + if(StringLen(pendingEntry.compositeName) > 0) { string parts[]; int n = StringSplit(pendingEntry.compositeName, '|', parts); for(int i=0;i open) when scanning backward from break_time -// 2) Entry = OB edge (high for bearish candle (bullish OB), low for bullish candle (bearish OB)) -// 3) SL = Entry +/- 300 pips (bullish: SL = entry - 300 pips; bearish: SL = entry + 300 pips) -// 4) TP computed using RiskRewardRatio as before (TP = entry ± R:R*|entry-sl|) -// 5) Calculate lotsize such that risk = 1% equity (use CalculateLotSizeForRisk) -// 6) Store into pendingEntry and draw visuals void SetUpPendingEntryForMSS(const MSS &mss, int slot) { - string sym = Symbol(); + string symbol = Symbol(); + int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); + double pip = GetPipSize(symbol); - if(PrintEntryLog) - PrintFormat("==> SetUpPendingEntryForMSS (NEW) START: mss.found=%d dir=%d sweep_time=%d sweep_price=%.10f break_time=%d break_price=%.10f", - mss.found ? 1 : 0, mss.direction, (int)mss.sweep_time, mss.sweep_extreme_price, (int)mss.break_time, mss.bos_close_price); - - if(!mss.found) - { - if(PrintEntryLog) Print("-> abort: mss.found == false"); + // =============================== + // 0️⃣ Sanity check + // =============================== + if(!mss.found || mss.sweep_time == 0 || mss.break_time == 0) return; - } - if(mss.break_time == 0) - { - if(PrintEntryLog) Print("-> abort: invalid break_time"); + + // =============================== + // 1️⃣ FIND ALL LTF INTERNAL FVGs + // =============================== + double fvgTopPrices[]; + double fvgBottomPrices[]; + datetime fvgTimeA[]; + datetime fvgTimeC[]; + int fvgDirections[]; + + int totalLtfFVGs = FindInternalFVG( + symbol, + LowTF, + 100, // LTF lookback + fvgTopPrices, + fvgBottomPrices, + fvgTimeA, + fvgTimeC, + fvgDirections + ); + + if(totalLtfFVGs <= 0) return; - } - // 1) Find OrderBlock (scan backward on LowTF from break_time - 1 bar) - int idxStart = iBarShift(sym, LowTF, mss.break_time, false); - if(idxStart == -1) idxStart = 0; + // =============================== + // 2️⃣ SELECT FVG: + // - same direction as MSS + // - timeC strictly between sweep & break + // - closest to break_time + // =============================== + int selectedFvgIndex = -1; + datetime selectedFvgTime = 0; - int foundIdx = -1; - double obEdgePrice = 0.0; // entry price (edge) - int obDirection = 0; // 1 = bullish candle (close>open), -1 = bearish candle (close start at idxStart (bar whose time == break_time) then go idxStart+1 ??? - // iBarShift returns index (0 = current), bar with time==break_time likely index > 0. We want bars older than break_time so begin idx = idxStart (if that bar equals break_time) then idx = idxStart - // Safer: we want bars with time < break_time, so shift to idx = idxStart (if that index's time == break_time then idx++), but iBarShift(...,false) returns exact index. - // We'll start scanning from idx = idxStart (which should point to the bar with time==break_time) and step forward (older) idx+1, idx+2... - int scanIdx = idxStart; - // move one bar older to ensure strictly before break_time - scanIdx = scanIdx + 1; - - int maxScan = 50; // cap scanning to avoid infinite loops (you can adjust) - int scanned = 0; - for(int idx = scanIdx; idx < iBars(sym, LowTF) && scanned < maxScan; idx++, scanned++) + for(int i = 0; i < totalLtfFVGs; i++) { - double o = iOpen(sym, LowTF, idx); - double c = iClose(sym, LowTF, idx); - if(o == 0.0 || c == 0.0) continue; + // direction must match MSS + if(fvgDirections[i] != mss.direction) + continue; - if(mss.direction == 1) + // must be between sweep & break + if(fvgTimeC[i] <= mss.sweep_time || fvgTimeC[i] >= mss.break_time) + continue; + + // choose the one closest to break_time + if(selectedFvgIndex == -1 || fvgTimeC[i] > selectedFvgTime) { - // bullish MSS: find first bearish candle going backward => candle where close < open (bearish) - if(c < o) - { - foundIdx = idx; - obDirection = -1; - obEdgePrice = iHigh(sym, LowTF, idx); // use high of that bearish candle as OB edge - break; - } - } - else if(mss.direction == -1) - { - // bearish MSS: find first bullish candle (close > open) - if(c > o) - { - foundIdx = idx; - obDirection = 1; - obEdgePrice = iLow(sym, LowTF, idx); // use low of that bullish candle as OB edge - break; - } + selectedFvgIndex = i; + selectedFvgTime = fvgTimeC[i]; } } - if(foundIdx == -1) + if(selectedFvgIndex == -1) { - if(PrintEntryLog) Print("-> abort: no suitable OrderBlock (opposite-color candle) found before break_time on LowTF"); + if(PrintEntryLog) + Print("SetUpPendingEntryForMSS: no valid LTF FVG between sweep & break"); return; } - // Normalize entry price to symbol digits - int digs = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); - double entryPrice = NormalizeDouble(obEdgePrice, digs); + // =============================== + // 3️⃣ ENTRY = 50% OF SELECTED FVG + // =============================== + double fvgTop = fvgTopPrices[selectedFvgIndex]; + double fvgBottom = fvgBottomPrices[selectedFvgIndex]; - if(PrintEntryLog) - PrintFormat("-> Found OB at index=%d time=%s dir=%d edge=%.10f (normalized=%.10f)", - foundIdx, TimeToString(iTime(sym, LowTF, foundIdx), TIME_DATE|TIME_MINUTES), obDirection, obEdgePrice, entryPrice); + double entryPrice = NormalizeDouble( + (fvgTop + fvgBottom) * 0.5, + digits + ); + + // =============================== + // 4️⃣ STOP LOSS = LIQUIDITY SWEEP + // =============================== + double stopLoss = mss.sweep_extreme_price; + + double slBuffer = 1.0 * pip; // safety buffer - // 2) Set SL = entry +/- 300 pips - double pip = GetPipSize(sym); - double sl; - double slDistancePoints = 300.0 * pip; if(mss.direction == 1) { - // bullish MSS -> we will BUY at entry=OB(high) -> SL below OB - sl = entryPrice - slDistancePoints; + stopLoss -= slBuffer; + if(stopLoss >= entryPrice) + return; } else { - // bearish MSS -> SELL at entry=OB(low) -> SL above OB - sl = entryPrice + slDistancePoints; + stopLoss += slBuffer; + if(stopLoss <= entryPrice) + return; } - sl = NormalizeDouble(sl, digs); - // 3) Compute TP using RiskRewardRatio (existing input) - double tp = 0.0; - double diff = MathAbs(entryPrice - sl); - if(diff > 0.0) - { - if(mss.direction == 1) - tp = entryPrice + RiskRewardRatio * diff; - else - tp = entryPrice - RiskRewardRatio * diff; - tp = NormalizeDouble(tp, digs); - } + stopLoss = NormalizeDouble(stopLoss, digits); + + // =============================== + // 5️⃣ TAKE PROFIT = RR + // =============================== + double riskDistance = MathAbs(entryPrice - stopLoss); + if(riskDistance <= 0.0) + return; + + double takeProfit; + if(mss.direction == 1) + takeProfit = entryPrice + RiskRewardRatio * riskDistance; else - { - if(PrintEntryLog) Print("-> abort: computed diff==0 between entry and SL"); - return; - } + takeProfit = entryPrice - RiskRewardRatio * riskDistance; - // 4) Calculate lotsize based on 1% equity risk (hard-coded 1% per your request) - double lots = CalculateLotSizeForRisk(sym, entryPrice, sl, 1.0); // 1% equity - if(lots <= 0.0) - { - if(PrintEntryLog) Print("-> abort: CalculateLotSizeForRisk returned 0.0 (cannot determine lotsize)"); - return; - } + takeProfit = NormalizeDouble(takeProfit, digits); - // 5) Clear previous pending and populate new pendingEntry + // =============================== + // 6️⃣ LOT SIZE BY RISK + // =============================== + double lotSize = CalculateLotSizeForRisk( + symbol, + entryPrice, + stopLoss, + RishPercent + ); + + if(lotSize <= 0.0) + return; + + // =============================== + // 7️⃣ STORE PENDING ENTRY + // =============================== ClearPendingEntry(); - pendingEntry.active = true; - pendingEntry.direction = (mss.direction == 1) ? 1 : -1; - pendingEntry.price = entryPrice; - pendingEntry.fvgIndex = -1; // not using FVG for this method + pendingEntry.active = true; + pendingEntry.direction = mss.direction; + pendingEntry.price = entryPrice; + pendingEntry.sl_price = stopLoss; + pendingEntry.tp_price = takeProfit; + pendingEntry.lotSize = lotSize; pendingEntry.created_time = TimeCurrent(); - pendingEntry.compositeName = ""; - pendingEntry.source_slot = slot; - pendingEntry.sl_price = sl; - pendingEntry.tp_price = tp; - pendingEntry.lotSize = lots; - pendingEntry.orderTicket = 0; - - // attempt to place pending order immediately - if(PrintEntryLog) Print("Attempting to place pending order from pendingEntry..."); - bool ok = PlacePendingOrderFromPendingEntry(); - if(!ok) - Print("Failed to place pending order (check broker settings, tick/tickvalue availability, volume limits)."); - + pendingEntry.source_slot = slot; + pendingEntry.fvgIndex = -1; // LTF FVG, không phải MTF + pendingEntry.orderTicket = 0; if(PrintEntryLog) { - PrintFormat("-> PendingEntry populated (OB method): dir=%d entry=%.10f sl=%.10f tp=%.10f lots=%.4f created=%s", - pendingEntry.direction, pendingEntry.price, pendingEntry.sl_price, pendingEntry.tp_price, - pendingEntry.lotSize, TimeToString(pendingEntry.created_time, TIME_DATE|TIME_SECONDS)); + PrintFormat( + "LTF FVG ENTRY SET | dir=%d | entry=%.5f | SL=%.5f | TP=%.5f | FVG time=%s", + mss.direction, + entryPrice, + stopLoss, + takeProfit, + TimeToString(selectedFvgTime, TIME_DATE|TIME_MINUTES) + ); } - if(PrintEntryLog) Print("==> SetUpPendingEntryForMSS (NEW) END"); + // =============================== + // 8️⃣ PLACE LIMIT ORDER + // =============================== + PlacePendingOrderFromPendingEntry(); } void MoveStoploss() @@ -2540,16 +2540,10 @@ void OnTick() // MoveStoplossAdvanced(); } - if(IsNewClosedBar(sym, HighTF, 0)) { - HandleLogicForTimeframe(sym, HighTF, 0, DetectMSS_HTF, htfSwingRange, false, 10.0, 2, 50, FVGLookback); - } - - if(IsNewClosedBar(sym, MiddleTF, 1)) { - HandleLogicForTimeframe(sym, MiddleTF, 1, DetectMSS_MTF, mtfSwingRange, true, 10.0, 2, 50, FVGLookback); - } - if(IsNewClosedBar(sym, LowTF, 2)) { - HandleLogicForTimeframe(sym, LowTF, 2, DetectMSS_LTF, ltfSwingRange, true, 10.0, 2, 50, FVGLookback); + HandleLogicForTimeframe(sym, HighTF, 0, htfSwingRange, false, 10.0, 2, 50, FVGLookback); + HandleLogicForTimeframe(sym, MiddleTF, 1, mtfSwingRange, true, 10.0, 2, 50, FVGLookback); + HandleLogicForTimeframe(sym, LowTF, 2, ltfSwingRange, true, 10.0, 2, 50, FVGLookback); if (watchingMSSMode) { CheckWatchMSSInvalidation(sym); } @@ -2559,11 +2553,11 @@ void OnTick() } int OnInit() -{ +{ string sym = Symbol(); - HandleLogicForTimeframe(sym, HighTF, 0, false, htfSwingRange, false, 10.0, 2, 50, FVGLookback); - HandleLogicForTimeframe(sym, MiddleTF, 1, false, mtfSwingRange, true, 10.0, 2, 50, FVGLookback); - HandleLogicForTimeframe(sym, LowTF, 2, false, ltfSwingRange, false, 10.0, 2, 50, FVGLookback); + HandleLogicForTimeframe(sym, HighTF, 0, htfSwingRange, false, 10.0, 2, 50, FVGLookback); + HandleLogicForTimeframe(sym, MiddleTF, 1, mtfSwingRange, true, 10.0, 2, 50, FVGLookback); + HandleLogicForTimeframe(sym, LowTF, 2, ltfSwingRange, true, 10.0, 2, 50, FVGLookback); return(INIT_SUCCEEDED); } @@ -2584,7 +2578,7 @@ void OnDeinit(const int reason) if(ObjectFind(0, "LBL_HTF") >= 0) ObjectDelete(0, "LBL_HTF"); if(ObjectFind(0, "LBL_MTF") >= 0) ObjectDelete(0, "LBL_MTF"); - if(ObjectFind(0, "LBL_LTF") >= 0) ObjectDelete(0, "LBL_LTF"); + if(ObjectFind(0, "LTF: TRIGGER") >= 0) ObjectDelete(0, "LTF: TRIGGER"); int tot = ObjectsTotal(0); for(int i = tot - 1; i >= 0; i--)