diff --git a/Experts/HyperICT.mq5 b/Experts/HyperICT.mq5 index cde8bcf..9bffbeb 100644 --- a/Experts/HyperICT.mq5 +++ b/Experts/HyperICT.mq5 @@ -17,7 +17,7 @@ //| CHƯA CÓ TRONG DỰ ÁN: đặt lệnh, LTF, session, alert | //+------------------------------------------------------------------+ #property copyright "HyperICT" -#property version "0.11" +#property version "1.11" #property description "HTF structure | Fib=init only | pivot confirm=InpSwingRange" #include diff --git a/Experts/HyperICT_compile.log b/Experts/HyperICT_compile.log index c1c57a4..06c84d8 100644 Binary files a/Experts/HyperICT_compile.log and b/Experts/HyperICT_compile.log differ diff --git a/Include/HyperICT/Config.mqh b/Include/HyperICT/Config.mqh index 3f28407..dee5521 100644 --- a/Include/HyperICT/Config.mqh +++ b/Include/HyperICT/Config.mqh @@ -17,7 +17,7 @@ input group "══ HTF Structure ══" input ENUM_TIMEFRAMES InpHtf = PERIOD_H1; // §1: timeframe cấu trúc HTF -input int InpSwingRange = 3; // §1.4: số nến mỗi bên pivot (confirm đỉnh/đáy) +input int InpSwingRange = 5; // §1.4: số nến mỗi bên pivot (confirm đỉnh/đáy) input int InpSwingLookback = 400; // quét swing lúc lock ban đầu input double InpFibMinRatio = 0.382; // § tiên quyết: H1→L0 vs L1→H1 (chỉ lúc lock trend) diff --git a/Include/HyperICT/README.md b/Include/HyperICT/README.md index ab15acd..31d05e2 100644 --- a/Include/HyperICT/README.md +++ b/Include/HyperICT/README.md @@ -65,11 +65,19 @@ MQL5/ - **Fib fail** → state `BULL/BEAR (Fib pending)` — *không* coi là Neutral. - **Neutral** chỉ khi không đủ 4 swing hoặc cấu trúc không HH-HL / LH-LL. -### Swing H0, H1, L0, L1 +### Swing H0, H1, L0, L1 (ghép theo leg — không lấy 2 đỉnh + 2 đáy tách rời) -- Đếm từ **giá hiện tại về trước** trên HTF (mặc định H1). -- **H0 / L0** = đỉnh/đáy gần nhất; **H1 / L1** = đỉnh/đáy trước đó. -- **Khóa snapshot** khi bot chạy (`OnInit`) — **không** đổi mỗi nến mới. +Trên HTF, pivot được gán sao cho **Key LV1 tạo ra Key LV2** (đúng thứ tự thời gian): + +| Bias | Key LV1 | Key LV2 | Cách gán | +|------|---------|---------|----------| +| **Bear** | **H0** = Key LV1 (đỉnh trước L0) | **L0** = Key LV2 | Thời gian: **H1 → L1 → H0 → L0** | +| **Bull** | **L0** = Key LV1 (đáy trước H0) | **H0** = Key LV2 | Thời gian: **L1 → H1 → L0 → H0** | + +Gán pivot: từ L0/H0 gần nhất, lùi từng bước tới pivot cũ **kề** (không nhảy tới đỉnh/đáy xa hơn). + +- **Đỉnh/đáy hình thành sau Key LV2** (ví dụ đỉnh nhỏ sau L0 trong bear) **không** gán là H0 — thuộc `newH0` / `UpdateEngine` §1.4. +- **Khóa snapshot** khi bot chạy — chỉ đổi khi phá Key (§1.4). ### Key Level (§1.2) diff --git a/Include/HyperICT/SwingEngine.mqh b/Include/HyperICT/SwingEngine.mqh index 94f59bb..f379167 100644 --- a/Include/HyperICT/SwingEngine.mqh +++ b/Include/HyperICT/SwingEngine.mqh @@ -3,7 +3,9 @@ //| Pivot HTF, H0–L1, khóa snapshot, roll swing §1.4 | //+------------------------------------------------------------------+ //| ĐÃ GIẢI QUYẾT: | -//| • §1: H0,H1,L0,L1 từ giá hiện tại về trước (2 high + 2 low) | +//| • H0–L1 ghép theo leg (Key LV1 tạo Key LV2), không 2H+2L tách rời | +//| • Bear: L0=KeyLV2, H0=đỉnh trước L1 (tạo đáy L0); đỉnh sau L0→newH0 | +//| • Bull: H0=KeyLV2, L0=đáy trước H1 (tạo đỉnh H0) | //| • HH-HL / LH-LL — ClassifyStructure | //| • Khóa swing lúc bot chạy — LockInitialSnapshot (không đổi mỗi nến) | //| • §1.4 roll: Continue, CHoCH case1 (L1 giữ), CHoCH case2 bear | @@ -140,17 +142,93 @@ public: SortByTime(lows, nL); } - static void PickLastTwo(const SwingPoint &pts[], const int count, - SwingPoint &s0, SwingPoint &s1, - bool &has0, bool &has1) + // Pivot cũ hơn anchor (shift > anchor) và GẦN anchor nhất (shift nhỏ nhất trong các ứng viên) + static bool FindMostRecentOlder(const SwingPoint &pts[], const int count, + const int anchorShift, + SwingPoint &out) { - has0 = has1 = false; - if(count < 1) return; - s0 = pts[count - 1]; - has0 = true; - if(count < 2) return; - s1 = pts[count - 2]; - has1 = true; + int best = -1; + for(int i = 0; i < count; i++) + { + if(pts[i].shift <= anchorShift) + continue; + if(best < 0 || pts[i].shift < pts[best].shift) + best = i; + } + if(best < 0) + return false; + out = pts[best]; + return true; + } + + // Thứ tự thời gian (cũ → mới): Bear H1→L1→H0→L0 | Bull L1→H1→L0→H0 + static bool ValidateSwingChronology(const SwingSet &sw, const ENUM_STRUCT_BIAS bias) + { + if(!sw.IsComplete()) + return false; + if(bias == STRUCT_BEAR) + return sw.h1.shift > sw.l1.shift && + sw.l1.shift > sw.h0.shift && + sw.h0.shift > sw.l0.shift; + if(bias == STRUCT_BULL) + return sw.l1.shift > sw.h1.shift && + sw.h1.shift > sw.l0.shift && + sw.l0.shift > sw.h0.shift; + return false; + } + + //--- Bear: L0=KeyLV2, H0=KeyLV1 | Chuỗi: H1 → L1 → H0 → L0 + static bool PickStructuralBear(const SwingPoint &highs[], const int nH, + const SwingPoint &lows[], const int nL, + SwingSet &out) + { + out.Clear(); + if(nH < 2 || nL < 2) + return false; + + out.l0 = lows[nL - 1]; + out.hasL0 = true; + + if(!FindMostRecentOlder(lows, nL, out.l0.shift, out.l1)) + return false; + out.hasL1 = true; + + if(!FindMostRecentOlder(highs, nH, out.l1.shift, out.h0)) + return false; + out.hasH0 = true; + + if(!FindMostRecentOlder(highs, nH, out.h0.shift, out.h1)) + return false; + out.hasH1 = true; + + return true; + } + + //--- Bull: H0=KeyLV2, L0=KeyLV1 | Chuỗi: L1 → H1 → L0 → H0 + static bool PickStructuralBull(const SwingPoint &highs[], const int nH, + const SwingPoint &lows[], const int nL, + SwingSet &out) + { + out.Clear(); + if(nH < 2 || nL < 2) + return false; + + out.h0 = highs[nH - 1]; + out.hasH0 = true; + + if(!FindMostRecentOlder(lows, nL, out.h0.shift, out.l0)) + return false; + out.hasL0 = true; + + if(!FindMostRecentOlder(highs, nH, out.l0.shift, out.h1)) + return false; + out.hasH1 = true; + + if(!FindMostRecentOlder(lows, nL, out.h1.shift, out.l1)) + return false; + out.hasL1 = true; + + return true; } //--- §1.1 tiên quyết đặc điểm 1: HH-HL hoặc LH-LL @@ -172,8 +250,43 @@ public: out.Clear(); SwingPoint highs[], lows[]; CollectSwings(sym, tf, range, lookback, highs, lows); - PickLastTwo(highs, ArraySize(highs), out.h0, out.h1, out.hasH0, out.hasH1); - PickLastTwo(lows, ArraySize(lows), out.l0, out.l1, out.hasL0, out.hasL1); + const int nH = ArraySize(highs); + const int nL = ArraySize(lows); + + SwingSet bear, bull; + const bool hasBear = PickStructuralBear(highs, nH, lows, nL, bear); + const bool hasBull = PickStructuralBull(highs, nH, lows, nL, bull); + + const ENUM_STRUCT_BIAS biasBear = hasBear ? ClassifyStructure(bear) : STRUCT_NONE; + const ENUM_STRUCT_BIAS biasBull = hasBull ? ClassifyStructure(bull) : STRUCT_NONE; + + ENUM_STRUCT_BIAS chosen = STRUCT_NONE; + if(biasBear == STRUCT_BEAR) + { + out = bear; + chosen = STRUCT_BEAR; + } + else if(biasBull == STRUCT_BULL) + { + out = bull; + chosen = STRUCT_BULL; + } + else if(hasBear) + { + out = bear; + chosen = ClassifyStructure(bear); + } + else if(hasBull) + { + out = bull; + chosen = ClassifyStructure(bull); + } + else + return false; + + if(InpDebug && chosen != STRUCT_NONE && !ValidateSwingChronology(out, chosen)) + Print("[HyperICT/Swing] Cảnh báo: thứ tự H/L không khớp chuỗi chuẩn"); + return out.IsComplete(); }