Optimize entry

This commit is contained in:
Bell
2026-05-27 03:18:59 +07:00
parent 3fb31c4c1e
commit e816b3744d
22 changed files with 1899 additions and 105 deletions
+24 -4
View File
@@ -1,10 +1,30 @@
//+------------------------------------------------------------------+
//| ICT_2026.mq5 — Daily Bias + Intraday Structure (H1) |
//| BOS = Continue | CHoCH = Reversal |
//| ICT_2026.mq5 — Root EA (orchestrator only, no business logic) |
//+------------------------------------------------------------------+
//| Layer pipeline (đọc từ trên xuống = TF cao xuống thấp): |
//| D1 → DailyBias — bias hôm nay (BULL/BEAR/RANGE/NONE) |
//| H1 → IntradayStructure — H1 trend + IsAllowTrade |
//| H1 → LowTfTrend — orchestrator FVG + MSS pipeline |
//| ├── Fvg — detect/state/PD |
//| ├── ConfirmFvg — M5 FVG pool |
//| ├── MssSetup — phase machine + keylv lock |
//| └── MssEntry — order + partial + BE + timeout |
//| UI → Panel / Draw / FvgDraw / MssDraw + Stats |
//| |
//| Trigger: |
//| OnInit — init từng module, gọi Update force=true |
//| OnTick — orchestrator (xem README ▸ "Current |
//| architecture A.") |
//| OnTradeTransaction — filter DEAL_ENTRY_OUT magic → call |
//| IctMss_OnPositionClosed (reset state) |
//| OnDeinit — clean up objects |
//| |
//| Quy ước: file này KHÔNG được chứa business logic. Mọi rule đi vào|
//| module tương ứng. |
//+------------------------------------------------------------------+
#property copyright "ICT 2026"
#property version "1.170"
#property description "ICT2026 MSS | Pipeline gate=Bias, Entry gate=AllowTrade — track MSS cả khi intraday ngược, chỉ block entry"
#property version "1.187"
#property description "ICT2026 MSS | strict body-break, PD-for-all-FVG, bias-aware touch, stale-limit cap"
#include <ICT2026/Config.mqh>
#include <ICT2026/DailyBias.mqh>
+36 -2
View File
@@ -1,5 +1,29 @@
//+------------------------------------------------------------------+
//| Config.mqh — ICT 2026 inputs |
//| Config.mqh — ICT 2026 input parameters (single source of truth) |
//+------------------------------------------------------------------+
//| Tất cả `input` của EA gom vào file này, chia thành 8 groups: |
//| 1) Daily Bias — TF, swing range, fib |
//| 2) Intraday Structure — TF (default H1), pivot, recent bars |
//| 3) Test mode — InpOnlyStatsMode (skip render cho tester)|
//| 4) Display panel — panel font, position |
//| 5) Chart draw — bias/intra/confirm swing labels |
//| 6) Confirm swing (M5)— InpConfirmTf (M5), swing range, lookback|
//| 7) Low TF / FVG — InpFvgTf (H1), ATR period, gap filters, |
//| PD toggle / overlap %, large bypass, |
//| tiered touch (Pure/Mixed), |
//| draw top-N + price-near (v1.180) |
//| 8) MSS / Trade — risk %, SL/TP spread mults, min RR, |
//| partial close, BE@N×R, pending timeout,|
//| require intraday aligned (v1.181), |
//| max limit distance (v1.184), |
//| one-position, EOD cancel |
//| |
//| Lưu ý: KHÔNG đặt logic ở đây; chỉ khai báo `input`. Default values|
//| phải tương đương "production safe" — user mới chỉ cần tắt/bật là |
//| chạy ổn. |
//| |
//| Khi thêm input mới: cập nhật README mục [Input](#inputs) + |
//| changelog v1.x trong cùng commit. |
//+------------------------------------------------------------------+
#ifndef ICT2026_CONFIG_MQH
#define ICT2026_CONFIG_MQH
@@ -40,6 +64,7 @@ input group "══ Confirm swing (M5) ══"
input ENUM_TIMEFRAMES InpConfirmTf = PERIOD_M5; // TF confirmation (tương lai)
input int InpConfirmSwingRange = 2; // M5 swing strength: N bar mỗi bên (5-bar fractal khi =2)
input int InpConfirmSwingLookback = 80;
input int InpBodyBreakSpreadMult = 10; // Body break: thân nến đóng vượt level ≥ N × spread (siết chặt BOS/CHoCH, MSS confirm, H1 invalidate)
input int InpConfirmRecentBars = 40;
input group "══ Low TF / iTF FVG ══"
@@ -52,6 +77,7 @@ input double InpFvgMinGapATRPct = 12.0; // H1 only: gap 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 = 25.0; // % lấp FVG → Used
input bool InpFvgPdEnabled = true; // Bật/tắt điều kiện Premium/Discount toàn cục (false = chấp nhận FVG bất kể vùng)
input double InpFvgPdMinOverlapPct = 0.0; // POI filter: % chiều cao FVG nằm trong Premium/Discount (0=chấp nhận bất kỳ overlap > 0)
input double InpFvgTouchFillPure = 25.0; // Touch threshold khi FVG nằm hẳn trong vùng PD đúng chiều (bear→Premium, bull→Discount)
input double InpFvgTouchFillMixed = 50.0; // Touch threshold khi FVG straddle equilibrium (overlap cả Premium & Discount)
@@ -63,6 +89,8 @@ input color InpFvgBearColor = clrRed; // Bear FVG
input color InpFvgUsedColor = clrDimGray; // Used FVG
input color InpPdPremiumColor = clrMaroon;
input color InpPdDiscountColor = clrDarkGreen;
input int InpDrawMaxRecentPerState = 3; // FVG/PD: vẽ tối đa N gần nhất cho mỗi state (Used / Available)
input double InpDrawNearAtrMult = 2.0; // FVG khác chỉ vẽ khi giá cách ≤ N × ATR(FvgTf) (0=không vẽ "near", >0=vẽ thêm)
input group "══ MSS / Entry (Confirm TF) ══"
input bool InpMssH1RetestWickOnly = true; // (legacy, bỏ qua) Retest = giá chạm FVG
@@ -87,7 +115,13 @@ input int InpMssSlSpreadMult = 8; // SL buffer = N
input int InpMssTpSpreadMult = 16; // TP buffer = N × spread (chốt trước iL0/iH0 để dễ khớp)
input double InpMssMinRR = 2.0; // Ngưỡng RR: nếu TP@iL0/iH0 < ngưỡng → dùng TP cố định = entry ± risk × InpMssMinRR (= 2R), ngược lại TP tại iL0/iH0
input double InpMssPartialClosePct = 50.0; // % volume chốt khi giá đạt swing iL0/iH0 (TP gồng xa hơn) — sau đó dời SL về BE (0=tắt)
input int InpMssPendingExpireHours = 4; // Hết hạn pending limit (giờ): hủy + reset → WAIT_FVG_TOUCH (0=không timeout)
input bool InpMssBeEnabled = false; // Bật/tắt dời SL về entry khi giá đi được N×R (mặc định tắt)
input double InpMssBeAtRR = 2.0; // Ngưỡng N×R để kích hoạt dời BE (chỉ áp dụng khi InpMssBeEnabled=true)
input int InpMssPendingExpireHours = 1; // Hết hạn pending limit (giờ): hủy + reset → WAIT_FVG_TOUCH (0=không timeout)
input double InpMssMaxLimitDistAtrMult = 3.0; // Cap khoảng cách limit-giá theo bội số ATR(FvgTf). >cap ⇒ không đặt / cancel limit hiện có (tránh limit chết). 0 = tắt
input bool InpMssCancelStaleLimit = true; // Cancel pending + reset state khi giá rời xa entry vượt cap (mark FVG used để chọn POI khác)
input bool InpMssCancelLimitWhenTpReached = true; // Nếu giá chạm TP TRƯỚC khi limit khớp ⇒ cancel limit + reset (không đợi hồi nữa)
input bool InpMssRequireIntradayAligned = false; // Bắt buộc Intraday cùng chiều Bias mới entry? false=cho phép entry dù Intraday ngược/None (tránh miss setup khi trend chuyển muộn)
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)
input int InpMssEodHour = 23; // Giờ EOD theo SERVER time (24h, vd EET broker = 23h ≈ 16:00 ET DST)
+22 -1
View File
@@ -1,5 +1,26 @@
//+------------------------------------------------------------------+
//| ConfirmFvg.mqh — FVG trên Confirm TF (M5) cho MSS entry |
//| ConfirmFvg.mqh — pool FVG trên Confirm TF (M5) cho MSS entry |
//+------------------------------------------------------------------+
//| Pool độc lập với g_ictFvgZones (H1) — chỉ dùng làm entry zone |
//| sau khi MSS lock trên Confirm TF. |
//| |
//| KHÁC với H1 FVG: |
//| - KHÔNG áp dụng size filter (InpFvgMinGap*) — phát hiện M5 FVG |
//| nhỏ là cần thiết cho entry precision |
//| - KHÔNG track PD |
//| - Scan trigger từ ngoài: IctConfirmFvg_ScanNew(sym, tf, side, |
//| sinceTime, force) — chỉ scan FVG hình thành SAU chochTime |
//| |
//| Globals owned: |
//| g_ictConfirmFvgZones[] — array M5 FVG (max InpFvgMaxZones) |
//| g_ictConfirmFvgCount — số M5 FVG |
//| |
//| Public API: |
//| void IctConfirmFvg_Init() |
//| void IctConfirmFvg_UpdateAll(sym, tf) — update state/fill |
//| void IctConfirmFvg_ScanNew(sym, tf, side, sinceTime, force) |
//| int IctConfirmFvg_FindLatestAfter(sinceTime, side) |
//| int IctConfirmFvg_FindById(id) |
//+------------------------------------------------------------------+
#ifndef ICT2026_CONFIRMFVG_MQH
#define ICT2026_CONFIRMFVG_MQH
+156 -5
View File
@@ -1,5 +1,35 @@
//+------------------------------------------------------------------+
//| DailyBias.mqh — Bias rõ (HH-HL/LH-LL) vs sớm (sau CHoCH) |
//| DailyBias.mqh — Daily Bias resolver (D1) |
//+------------------------------------------------------------------+
//| Trả lời câu hỏi: "Hôm nay nên ưu tiên BUY hay SELL?" |
//| |
//| Thứ tự ưu tiên (cao→thấp, v1.175): |
//| 1) D1/D2 pattern (IctDailyBias_ResolveD1D2): |
//| - D[1].close > D[2].high → BULL (breakout) |
//| - D[1].close < D[2].low → BEAR (breakdown) |
//| - D[1] sweep D[2] high + close back → BEAR (sweep bear liq)|
//| - D[1] sweep D[2] low + close back → BULL (sweep bull liq)|
//| 2) Structural HH-HL / LH-LL (IctResolveTrend) |
//| 3) HTF fallback (IctUpdateHTFBias — Previous Day Model) |
//| |
//| Globals owned: |
//| g_ictDailyBias — IctDailyBiasState (kết quả + reason) |
//| g_ictDailyCtx — IctTrendResolveCtx (input cho resolver) |
//| g_ictBiasIntoBullTime — TS Bias chuyển vào BULL (v1.182) |
//| g_ictBiasIntoBearTime — TS Bias chuyển vào BEAR (v1.182) |
//| ⇒ làm cutoff cho IctMss_HasFreshFvgTouch (loại touch xảy ra |
//| trước khi bias xoay sang side hiện tại) |
//| |
//| Public API: |
//| bool IctDailyBias_Init(sym) |
//| bool IctDailyBias_Update(sym) — true khi có nến D1 mới |
//| void IctDailyBias_Get(IctDailyBiasState &out) |
//| bool IctDailyBias_IsBull/IsBear() |
//| ENUM_ICT_BIAS ICT2026_GetDailyBias() |
//| |
//| Internal: |
//| IctDailyBias_TrackTransition() — gọi cuối Update để cập nhật |
//| g_ictBiasInto*Time khi bias xoay side |
//+------------------------------------------------------------------+
#ifndef ICT2026_DAILYBIAS_MQH
#define ICT2026_DAILYBIAS_MQH
@@ -12,6 +42,99 @@
IctDailyBiasState g_ictDailyBias;
IctTrendResolveCtx g_ictDailyCtx;
// Thời điểm Bias chuyển vào mỗi side — dùng để gác "fresh touch":
// FVG touch xảy ra TRƯỚC thời điểm Bias chuyển sang side đó ⇒ KHÔNG hợp lệ.
// Tránh case: bias UP → bear FVG bị chạm (lúc EA bỏ qua) → bias xoay DOWN → EA
// tưởng đã chạm POI từ quá khứ → vào lệnh limit luôn.
datetime g_ictBiasIntoBullTime = 0;
datetime g_ictBiasIntoBearTime = 0;
void IctDailyBias_TrackTransition()
{
static ENUM_ICT_BIAS prev = ICT_BIAS_NONE;
const ENUM_ICT_BIAS cur = g_ictDailyBias.bias;
if(cur == prev)
return;
const datetime now = TimeCurrent();
if(cur == ICT_BIAS_BULL && prev != ICT_BIAS_BULL)
g_ictBiasIntoBullTime = now;
else if(cur == ICT_BIAS_BEAR && prev != ICT_BIAS_BEAR)
g_ictBiasIntoBearTime = now;
prev = cur;
}
// Bias từ pattern D1 vs D2 (ưu tiên cao hơn structural HH-HL/LH-LL).
//
// Thứ tự ưu tiên:
// 1. Breakout/Breakdown rõ ràng:
// - D[1].close > D[2].high → BULL
// - D[1].close < D[2].low → BEAR
// 2. Liquidity sweep (false breakout):
// - D[1].high > D[2].high & D[1].close < D[2].high & close > D[2].low
// → sweep bear liquidity (quét stop trên) → BEAR
// - D[1].low < D[2].low & D[1].close > D[2].low & close < D[2].high
// → sweep bull liquidity (quét stop dưới) → BULL
// 3. Trường hợp đặc biệt: D1 sweep CẢ 2 phía + close kẹt trong range D2
// ⇒ không xác định ⇒ rơi xuống fallback structural
//
// Return: true nếu match 1 trong các pattern (biasOut + reasonOut được set).
bool IctDailyBias_ResolveD1D2(const string sym, const ENUM_TIMEFRAMES tf,
ENUM_ICT_BIAS &biasOut, string &reasonOut)
{
biasOut = ICT_BIAS_NONE;
reasonOut = "";
const double b1H = iHigh(sym, tf, 1);
const double b1L = iLow(sym, tf, 1);
const double b1C = iClose(sym, tf, 1);
const double b2H = iHigh(sym, tf, 2);
const double b2L = iLow(sym, tf, 2);
if(b2H <= 0.0 || b2L <= 0.0 || b1H <= 0.0)
return false;
// 1) Clear breakout / breakdown
if(b1C > b2H)
{
biasOut = ICT_BIAS_BULL;
reasonOut = "D[1].close > D[2].high → Breakout BULL";
return true;
}
if(b1C < b2L)
{
biasOut = ICT_BIAS_BEAR;
reasonOut = "D[1].close < D[2].low → Breakdown BEAR";
return true;
}
// Đến đây: b2L ≤ b1C ≤ b2H (close kẹt trong range D2)
const bool sweepHigh = (b1H > b2H);
const bool sweepLow = (b1L < b2L);
// 2a) Cả 2 phía cùng sweep + close inside ⇒ không xác định
if(sweepHigh && sweepLow)
return false;
// 2b) Sweep bear liquidity
if(sweepHigh)
{
biasOut = ICT_BIAS_BEAR;
reasonOut = "D[1].high > D[2].high & close < D[2].high → sweep bear liq → BEAR";
return true;
}
// 2c) Sweep bull liquidity
if(sweepLow)
{
biasOut = ICT_BIAS_BULL;
reasonOut = "D[1].low < D[2].low & close > D[2].low → sweep bull liq → BULL";
return true;
}
// Inside day hoặc trùng range — không match D1/D2 pattern
return false;
}
ENUM_ICT_BIAS IctBiasFromTrend(const ENUM_ICT_TREND trend)
{
if(trend == ICT_TREND_UP)
@@ -291,13 +414,41 @@ bool IctDailyBias_Update(const string sym, const bool force = false)
IctResolveTrend(sym, tf, sw, pivotStruct, g_ictDailyCtx, 1);
IctDailyBias_SyncFromCtx(sw, pivotStruct);
if(g_ictDailyBias.bias == ICT_BIAS_NONE)
IctDailyBias_ApplyHtfFallback();
else
// ƯU TIÊN 1: D1/D2 pattern (4 cases: breakout, breakdown, 2 sweeps).
// D1/D2 thắng cả structural — override bias nếu match.
ENUM_ICT_BIAS d1d2Bias = ICT_BIAS_NONE;
string d1d2Reason = "";
if(IctDailyBias_ResolveD1D2(sym, tf, d1d2Bias, d1d2Reason))
{
g_ictDailyBias.bias = d1d2Bias;
g_ictDailyBias.biasPhase = ICT_TREND_PHASE_CLEAR;
g_ictDailyBias.reason = d1d2Reason;
g_ictDailyBias.keyLv1 = 0.0;
g_ictDailyBias.keyLv2 = 0.0;
if(sw.IsComplete())
{
const ENUM_ICT_STRUCT keyStruct = (d1d2Bias == ICT_BIAS_BULL)
? ICT_STRUCT_BULL : ICT_STRUCT_BEAR;
IctGetKeyLevels(keyStruct, sw, g_ictDailyBias.keyLv1, g_ictDailyBias.keyLv2);
}
g_ictDailyBias.displayReason = d1d2Reason;
}
// ƯU TIÊN 2: Structural HH-HL/LH-LL (đã set ở SyncFromCtx ở trên).
else if(g_ictDailyBias.bias == ICT_BIAS_BULL || g_ictDailyBias.bias == ICT_BIAS_BEAR)
{
g_ictDailyBias.reason = StringFormat("Structural: %s",
IctBiasText(g_ictDailyBias.bias));
g_ictDailyBias.displayReason = IctBuildDisplayReason(g_ictDailyBias, sym, tf);
}
// ƯU TIÊN 3: HTF fallback (outside day / range).
else
{
IctDailyBias_ApplyHtfFallback();
g_ictDailyBias.displayReason = IctBuildDisplayReason(g_ictDailyBias, sym, tf);
}
g_ictDailyBias.displayReason = IctBuildDisplayReason(g_ictDailyBias, sym, tf);
// Cập nhật timestamp Bias chuyển side (để gác fresh touch sau Bias flip)
IctDailyBias_TrackTransition();
if(InpDebug)
{
+25 -2
View File
@@ -1,6 +1,29 @@
//+------------------------------------------------------------------+
//| Draw.mqh — label swing theo TF chart hiện tại |
//| Chart lớn (D1): b* | Chart H1: b*+i* | Chart M5: b*+i*+H0L1 |
//| Draw.mqh — swing labels trên chart (adaptive theo chart TF) |
//+------------------------------------------------------------------+
//| Vẽ 3 lớp swing labels, tự động ẩn lớp có TF nhỏ hơn chart hiện |
//| tại để không bị nhiễu: |
//| |
//| Chart TF | Vẽ |
//| --------------+-------------------------------------------------|
//| ≥ InpBiasTf | bH0, bH1, bL0, bL1 |
//| ≥ InpIntra | + iH0, iH1, iL0, iL1 |
//| ≥ InpConfirm | + H0, H1, L0, L1 (Confirm TF swing) |
//| |
//| Bật/tắt từng lớp: |
//| InpDrawBiasSwingLabels |
//| InpDrawIntraSwingLabels |
//| InpDrawConfirmLabels |
//| |
//| So sánh: PeriodSeconds(chart) vs PeriodSeconds(InpXxxTf). |
//| |
//| Skip nếu: InpOnlyStatsMode = true |
//| |
//| Object prefix: ICT26_DR_ |
//| |
//| Public API: |
//| void IctDraw_Render(sym) |
//| void IctDraw_DeleteAll() |
//+------------------------------------------------------------------+
#ifndef ICT2026_DRAW_MQH
#define ICT2026_DRAW_MQH
+33 -1
View File
@@ -1,5 +1,37 @@
//+------------------------------------------------------------------+
//| EaState.mqh — State machine tổng: STOP vs SETUP pipeline MSS |
//| EaState.mqh — STATE machine cao cấp cho panel + API export |
//+------------------------------------------------------------------+
//| Map (mss.phase + flags + bias/intraday) → CODE category: |
//| |
//| STOP — không trade được: |
//| STOP_NO_BIAS — bias = NONE |
//| STOP_BIAS_RANGE — bias = RANGE |
//| STOP_INTRADAY_NONE — bias rõ + intraday = NONE |
//| STOP_BIAS_INTRADAY_MISMATCH — bias ≠ intraday |
//| |
//| SETUP — đang setup: |
//| WAIT_FVG_TOUCH — chờ retest H1 POI |
//| FVG_TOUCHED_WAIT_MSS — H1 retest + chờ M5 phá keylv |
//| MSS_OK_WAIT_M5_FVG — MSS lock + chờ M5 FVG |
//| M5_FVG_WAIT_RETRACE — có M5 FVG + chờ hồi fill |
//| READY_FOR_LIMIT — vùng entry, sắp đặt |
//| |
//| TRADE — có order: |
//| LIMIT_ORDER_PENDING — pending limit magic |
//| ON_TRADE — position magic |
//| |
//| Globals owned: |
//| g_ictEaState — IctEaState (code, title, detail) |
//| |
//| Lưu ý category vs code: |
//| Category {STOP/SETUP/TRADE} là "phân nhóm" hiển thị; code là |
//| "vị trí cụ thể trong nhóm". |
//| |
//| Public API: |
//| void IctEaState_Init() / IctEaState_Refresh(sym) |
//| ENUM_ICT_EA_STATE ICT2026_GetEaState() |
//| string ICT2026_GetEaStateCode() |
//| string ICT2026_GetEaStateDetail() |
//+------------------------------------------------------------------+
#ifndef ICT2026_EASTATE_MQH
#define ICT2026_EASTATE_MQH
+66 -1
View File
@@ -1,5 +1,42 @@
//+------------------------------------------------------------------+
//| Fvg.mqh — iTF Fair Value Gap (Available / Used) |
//| Fvg.mqh — FVG detection + state + Premium/Discount (FvgTf = H1) |
//+------------------------------------------------------------------+
//| Quản lý FVG trên `InpFvgTf` (default H1): |
//| - Detect FVG 3 nến A-B-C (Bull: A.High<C.Low; Bear: A.Low>C.High)|
//| - State: AVAILABLE / USED (fill ≥ InpFvgUsedFillPct ⇒ USED) |
//| - PD zone (Premium/Discount) từ 2 pivot swing-1 + swing-2: |
//| swing-1 = pivot gần FVG nhất (trước bar C) |
//| swing-2 = pivot ngược chiều đầu tiên SAU FVG |
//| pdHigh / pdLow / pdEq (giữa) |
//| - `pdComplete` khoá khi swing-2 xác nhận (không cập nhật nữa) |
//| |
//| Globals owned: |
//| g_ictFvgZones[] — array FVG (max InpFvgMaxZones) |
//| g_ictFvgCount — số FVG đang track |
//| |
//| Filters kích thước (chỉ áp dụng `InpFvgTf` — H1, không M5): |
//| InpFvgMinGapATRPct (12%) — gap ≥ % ATR(14) |
//| InpFvgMinGapPoints (0) — gap tuyệt đối |
//| InpFvgMinGapVsBarPct(25%) — gap ≥ % range nến B |
//| |
//| PD filter (Premium/Discount eligibility): |
//| IctFvg_IsEntryRepPd(zone, bias): |
//| - !InpFvgPdEnabled (v1.179) ⇒ true |
//| - else: BEAR overlap Premium / BULL overlap Discount |
//| - (v1.187) bỏ large-FVG bypass — áp PD cho TẤT CẢ FVG |
//| |
//| Touch threshold (Pure/Mixed — v1.167): |
//| IctFvg_RequiredTouchFillPct(zone): |
//| - PD tắt (InpFvgPdEnabled=false) ⇒ Pure (25%) |
//| - FVG hoàn toàn trong vùng PD đúng chiều ⇒ Pure (25%) |
//| - FVG straddle equilibrium ⇒ Mixed (50%) |
//| |
//| Public API (chính): |
//| void IctFvg_Init() / IctFvg_UpdateAll(sym, tf) |
//| void IctFvg_ScanNew(sym, tf, side, force) |
//| int IctFvg_CountAvailable() |
//| void IctFvg_UpdateZoneState(sym, tf, zone) |
//| double IctFvg_AtrFvgTf(sym) — ATR helper (M5/H1 buffers, v1.184)|
//+------------------------------------------------------------------+
#ifndef ICT2026_FVG_MQH
#define ICT2026_FVG_MQH
@@ -454,10 +491,33 @@ ENUM_ICT_PD_ZONE IctFvg_GetRepPd(const IctFvgZone &zone)
return zone.pdZone;
}
// Lấy ATR(InpFvgTf, period) tại bar shift=1 (đã đóng)
double IctFvg_AtrFvgTf(const string sym)
{
const int h = iATR(sym, InpFvgTf, InpFvgAtrPeriod);
if(h == INVALID_HANDLE)
return 0.0;
double buf[];
ArraySetAsSeries(buf, true);
const int got = CopyBuffer(h, 0, 1, 1, buf);
IndicatorRelease(h);
return (got == 1) ? buf[0] : 0.0;
}
bool IctFvg_IsEntryRepPd(const IctFvgZone &zone)
{
// Toggle global: tắt PD ⇒ chấp nhận mọi FVG có pdEq hợp lệ
// (vẫn yêu cầu có Bias rõ ràng để chọn side BEAR/BULL)
if(!InpFvgPdEnabled)
{
if(g_ictDailyBias.bias != ICT_BIAS_BEAR && g_ictDailyBias.bias != ICT_BIAS_BULL)
return false;
return true;
}
if(zone.pdEq <= 0.0)
return false;
const double minPct = MathMax(0.0, MathMin(100.0, InpFvgPdMinOverlapPct));
double pct = 0.0;
if(g_ictDailyBias.bias == ICT_BIAS_BEAR)
@@ -474,11 +534,16 @@ bool IctFvg_IsEntryRepPd(const IctFvgZone &zone)
}
// Touch threshold cho POI:
// - PD toàn cục tắt (InpFvgPdEnabled=false): luôn 25% (không phân biệt vùng)
// - FVG nằm hẳn trong Premium (bear) / Discount (bull) → 25%
// - FVG straddle equilibrium (mixed) → 50%
// - Trả về 100 nếu FVG sai vùng (không thể là POI)
double IctFvg_RequiredTouchFillPct(const IctFvgZone &zone)
{
// Toggle global tắt PD ⇒ dùng touch threshold thấp
if(!InpFvgPdEnabled)
return InpFvgTouchFillPure;
if(zone.pdEq <= 0.0)
return 100.0;
double pct = 0.0;
+112 -4
View File
@@ -1,5 +1,30 @@
//+------------------------------------------------------------------+
//| FvgDraw.mqh — vẽ iTF FVG + Premium/Discount trên chart |
//| FvgDraw.mqh — render FVG + Premium/Discount lên chart |
//+------------------------------------------------------------------+
//| v1.180 — selective rendering (chart không lag với 24+ FVG): |
//| 1) Top-N USED gần nhất (default 3) — sort theo createdTime desc |
//| 2) Top-N AVAILABLE gần nhất (default 3) |
//| 3) Price-near: giá hiện tại trong [lower-N×ATR, upper+N×ATR] |
//| 4) M5 FVG đang dùng cho MSS pipeline — luôn ép vẽ |
//| |
//| Inputs control: |
//| InpDrawFvgZones — bật/tắt vẽ |
//| InpDrawMaxRecentPerState — N FVG gần nhất mỗi state |
//| InpDrawNearAtrMult — bán kính near-price theo ATR |
//| |
//| Render: rectangle (FVG body) + 2 horizontal lines (Premium / |
//| Discount edge). Bull/Bear/Used có màu riêng (InpFvgBull/Bear/Used)|
//| |
//| Skip nếu: InpOnlyStatsMode = true (tester mode) |
//| |
//| Object prefix: |
//| ICT26_FVG_ — body rectangle |
//| ICT26_PD_ — Premium / Discount lines |
//| ICT26_CFVG_ — Confirm TF (M5) FVG body |
//| |
//| Public API: |
//| void IctFvgDraw_Render(sym) |
//| void IctFvgDraw_DeleteAll() |
//+------------------------------------------------------------------+
#ifndef ICT2026_FVGDRAW_MQH
#define ICT2026_FVGDRAW_MQH
@@ -57,6 +82,75 @@ void IctFvgDraw_HLine(const string name, const datetime t1, const datetime t2,
ObjectMove(ch, name, 1, t2, price);
}
// Insertion sort (descending theo createdTime) cho mảng indices nhỏ
void IctFvgDraw_SortIdxByTimeDesc(int &idx[], const int n,
const IctFvgZone &zones[])
{
for(int i = 1; i < n; i++)
{
const int cur = idx[i];
const datetime ct = zones[cur].createdTime;
int j = i - 1;
while(j >= 0 && zones[idx[j]].createdTime < ct)
{
idx[j + 1] = idx[j];
j--;
}
idx[j + 1] = cur;
}
}
// Build mask "có vẽ" cho mảng FVG: top-N USED + top-N AVAILABLE gần nhất,
// cộng các FVG mà price hiện tại gần (≤ N×ATR).
void IctFvgDraw_BuildVisibleMask(const IctFvgZone &zones[], const int count,
const string sym, bool &visible[])
{
ArrayResize(visible, count);
for(int i = 0; i < count; i++) visible[i] = false;
if(count == 0)
return;
const int maxN = MathMax(0, InpDrawMaxRecentPerState);
// Phân loại theo state
int avail[], used[];
ArrayResize(avail, count);
ArrayResize(used, count);
int nA = 0, nU = 0;
for(int i = 0; i < count; i++)
{
if(zones[i].state == ICT_FVG_USED)
used[nU++] = i;
else
avail[nA++] = i;
}
ArrayResize(avail, nA);
ArrayResize(used, nU);
IctFvgDraw_SortIdxByTimeDesc(avail, nA, zones);
IctFvgDraw_SortIdxByTimeDesc(used, nU, zones);
for(int i = 0; i < MathMin(maxN, nA); i++) visible[avail[i]] = true;
for(int i = 0; i < MathMin(maxN, nU); i++) visible[used[i]] = true;
// Mở rộng theo price-near
if(InpDrawNearAtrMult > 0.0)
{
const double atr = IctFvg_AtrFvgTf(sym);
if(atr > 0.0)
{
const double thresh = atr * InpDrawNearAtrMult;
const double px = SymbolInfoDouble(sym, SYMBOL_BID);
for(int i = 0; i < count; i++)
{
if(visible[i]) continue;
if(px >= zones[i].lower - thresh && px <= zones[i].upper + thresh)
visible[i] = true;
}
}
}
}
void IctFvgDraw_Render(const string sym)
{
if(InpOnlyStatsMode)
@@ -69,8 +163,14 @@ void IctFvgDraw_Render(const string sym)
IctFvgDraw_DeleteAll();
// Lọc H1 FVG cho top-N + price-near
bool visH1[];
IctFvgDraw_BuildVisibleMask(g_ictFvgZones, g_ictFvgCount, sym, visH1);
for(int i = 0; i < g_ictFvgCount; i++)
{
if(!visH1[i])
continue;
if(!IctFvg_MatchesBias(g_ictFvgZones[i]))
continue;
@@ -104,8 +204,18 @@ void IctFvgDraw_Render(const string sym)
if(InpDrawConfirmFvg)
{
// Lọc tương tự cho M5 FVG. M5 FVG đang được dùng cho MSS → ép vẽ.
bool visM5[];
IctFvgDraw_BuildVisibleMask(g_ictConfirmFvgZones, g_ictConfirmFvgCount, sym, visM5);
const ulong activeM5 = IctLowTf_MssM5FvgId();
for(int j = 0; j < g_ictConfirmFvgCount; j++)
{
const bool isActive = (activeM5 != 0 && g_ictConfirmFvgZones[j].id == activeM5 &&
IctLowTf_MssPhase() >= ICT_MSS_M5_FVG);
if(!visM5[j] && !isActive)
continue;
const string cid = IntegerToString((long)g_ictConfirmFvgZones[j].id);
const string cpfx = ICT26_CFVG_PFX + cid + "_";
const datetime cStart = g_ictConfirmFvgZones[j].createdTime;
@@ -115,9 +225,7 @@ void IctFvgDraw_Render(const string sym)
clrDarkGreen : clrFireBrick;
if(g_ictConfirmFvgZones[j].state == ICT_FVG_USED)
cClr = InpFvgUsedColor;
if(IctLowTf_MssM5FvgId() == g_ictConfirmFvgZones[j].id &&
IctLowTf_MssPhase() >= ICT_MSS_M5_FVG)
if(isActive)
cClr = (g_ictConfirmFvgZones[j].side == ICT_FVG_BULL) ? clrAqua : clrOrange;
IctFvgDraw_Rect(cpfx + "BOX", cStart, g_ictConfirmFvgZones[j].upper,
+32 -1
View File
@@ -1,5 +1,36 @@
//+------------------------------------------------------------------+
//| IntradayStructure.mqh — trend rõ (HH-HL/LH-LL) vs sớm (CHoCH) |
//| IntradayStructure.mqh — Intraday trend (H1) + IsAllowTrade gate |
//+------------------------------------------------------------------+
//| TF: InpIntradayTf (default H1) |
//| |
//| Phân loại HH-HL / LH-LL bằng "2 đỉnh + 2 đáy gần nhất" trong |
//| InpIntradayRecentBars (default 80 H1 bars) — KHÔNG ghép leg 4 |
//| swing dài như Daily (tránh dùng cấu trúc cũ khi giá đã đổi). |
//| |
//| CHoCH ưu tiên trước HH-HL/LH-LL: nếu H[1].close phá Key cũ → |
//| chuyển sang "sớm" (Bull/Bear sớm) bất kể pivot vẫn còn HH-HL. |
//| |
//| Globals owned: |
//| g_ictIntraday — IctIntradayState (trend, swings, event, |
//| isAllowTrade) |
//| g_ictIntradayCtx — context truyền vào IctResolveTrend |
//| |
//| IsAllowTrade (truth table): |
//| Bias=BULL + Intraday∈{UP, BULL EARLY} ⇒ true |
//| Bias=BEAR + Intraday∈{DOWN, BEAR EARLY} ⇒ true |
//| Intraday=NONE / opposite ⇒ false |
//| |
//| Public API: |
//| bool IctIntraday_Init(sym) |
//| bool IctIntraday_Update(sym) — true khi có nến H1 mới |
//| void IctIntraday_UpdateAllowTrade() — tính lại isAllowTrade |
//| bool IctIntraday_IsAllowTrade() |
//| ENUM_ICT_TREND ICT2026_GetIntradayTrend() |
//| bool IctIntraday_TrendAlignsWithBias(bias, trend) — predicate |
//| |
//| Lưu ý v1.181: isAllowTrade KHÔNG còn dùng để gate MSS pipeline |
//| nữa (mặc định). Pipeline chạy theo Bias, isAllowTrade chỉ gate |
//| entry khi InpMssRequireIntradayAligned=true. |
//+------------------------------------------------------------------+
#ifndef ICT2026_INTRADAYSTRUCTURE_MQH
#define ICT2026_INTRADAYSTRUCTURE_MQH
+19 -1
View File
@@ -1,5 +1,23 @@
//+------------------------------------------------------------------+
//| Journal.mqh — log lý do chặn MSS/entry vào Experts journal |
//| Journal.mqh — log MSS pipeline + entry block reasons (de-dup) |
//+------------------------------------------------------------------+
//| Log lên Experts Journal kèm de-dup (cùng reason consecutive → |
//| skip) để journal không spam khi pipeline đứng yên. |
//| |
//| Skip nếu: InpOnlyStatsMode = true (silent tester mode v1.162) |
//| |
//| 2 channels: |
//| IctMss_JournalPipeline(reason) — phase transition / wait |
//| IctMss_JournalEntryBlock(reason) — lý do block không entry |
//| IctMss_JournalReset() — reset cache khi setup reset |
//| |
//| Globals owned: |
//| g_ictMssJournalLast — cache last pipeline reason |
//| g_ictMssEntryJournalLast — cache last block reason |
//| |
//| Helper: |
//| IctMss_IsEntryReadyReason(reason) — true nếu reason hợp lệ |
//| (không thuộc "block" — e.g. "READY", "Limit placed") |
//+------------------------------------------------------------------+
#ifndef ICT2026_JOURNAL_MQH
#define ICT2026_JOURNAL_MQH
+17 -1
View File
@@ -1,5 +1,21 @@
//+------------------------------------------------------------------+
//| LowTfApi.mqh — state Low TF / MSS (tránh include vòng) |
//| LowTfApi.mqh — shared state cho Low TF pipeline (anti-cycle) |
//+------------------------------------------------------------------+
//| Tách g_ictLowTf khỏi LowTfTrend.mqh để các module MssSetup / |
//| MssEntry / FvgDraw / MssDraw / EaState có thể include shared |
//| state mà KHÔNG tạo vòng phụ thuộc với LowTfTrend.mqh. |
//| |
//| Globals owned: |
//| g_ictLowTf — IctLowTfState { |
//| lastBarTime, lastConfirmBarTime, |
//| mss (IctMssState — phase, h1FvgId, chochLocked, …), |
//| activeCount, availableCount, confirmFvgCount, |
//| displayReason |
//| } |
//| |
//| Helpers chính: |
//| ENUM_ICT_FVG_SIDE IctFvgSideFromBias(bias) |
//| string IctFvgSideText(side) / IctMarketSideText(side) |
//+------------------------------------------------------------------+
#ifndef ICT2026_LOWTFAPI_MQH
#define ICT2026_LOWTFAPI_MQH
+28 -1
View File
@@ -1,5 +1,32 @@
//+------------------------------------------------------------------+
//| LowTfTrend.mqh — Low TF layer: iTF FVG khi IsAllowTrade |
//| LowTfTrend.mqh — Orchestrator cho FVG + MSS pipeline |
//+------------------------------------------------------------------+
//| File này KHÔNG chứa logic core. Chỉ điều phối: |
//| 1) Nến mới `InpFvgTf` (H1) ⇒ IctLowTfTrend_Update: |
//| - IctIntraday_UpdateAllowTrade |
//| - IctFvg_UpdateAll (state + fill + PD) |
//| - IctFvg_ScanNew (FVG thuận Bias) — nếu Bias rõ |
//| - Nến mới `InpConfirmTf` (M5) ⇒ IctConfirmFvg_UpdateAll, |
//| IctMss_Update, IctMssEntry_Update, IctEaState_Refresh |
//| - Render: IctFvgDraw_Render, IctMssDraw_Render |
//| |
//| 2) Tick (giữa nến) ⇒ IctLowTfTrend_TickRefresh: |
//| - IctFvg_UpdateZoneState mỗi FVG (refresh fill ratio) |
//| - IctFvg_UpdateAllPd (PD update khi pivot thay đổi) |
//| - Nếu Bias rõ: IctMss_Update + IctMssEntry_Update on tick |
//| (catch live MSS lock + per-tick checks) |
//| |
//| Pipeline gate (v1.170): chỉ check `g_ictDailyBias.bias != NONE`. |
//| Không gate theo IsAllowTrade (cho phép MSS detect khi intraday |
//| transition). |
//| |
//| Globals dùng: |
//| g_ictLowTf (từ LowTfApi.mqh) — state aggregate |
//| |
//| Public API: |
//| bool IctLowTfTrend_Init(sym) |
//| bool IctLowTfTrend_Update(sym, force=false) |
//| void IctLowTfTrend_TickRefresh(sym) |
//+------------------------------------------------------------------+
#ifndef ICT2026_LOWTFTREND_MQH
#define ICT2026_LOWTFTREND_MQH
+26 -1
View File
@@ -1,5 +1,30 @@
//+------------------------------------------------------------------+
//| MssDraw.mqh — vẽ MSS sau H1 chạm FVG: touch, 38.2%, CHoCH, H0 |
//| MssDraw.mqh — render MSS objects (touch line, keylv, entry/SL/TP)|
//+------------------------------------------------------------------+
//| Vẽ khi phase >= H1_TOUCH && h1FvgId > 0. Hết phase ⇒ DeleteAll. |
//| |
//| Objects: |
//| H1_TOUCH (vline + label) — TS giá chạm H1 FVG (dodger blue) |
//| H1_382 (hline) — mức touch fill % tham khảo |
//| CHOCH (hline + label): |
//| - dotted gold + "L0 live"/"H0 live" khi phase=H1_TOUCH |
//| && !chochLocked |
//| - solid gold + "MSS↓ L0"/"MSS↑ H0" khi chochLocked |
//| MSS_SW (hline) — swing đối diện (SL anchor) — yellow |
//| ENTRY/SL/TP (hline) — khi pendingEntry > 0 + phase >= M5_FVG |
//| |
//| Lưu ý visibility: |
//| - "live" label chỉ vẽ khi UpdateLiveM5Swings tìm được pivot |
//| hợp lệ (đủ cấu trúc 2 đỉnh/đáy SAU touch — v1.165/1.171) |
//| - Phase reset (TP/SL/EOD/timeout/stale/bias flip) ⇒ DeleteAll |
//| |
//| Skip nếu: InpOnlyStatsMode = true |
//| |
//| Object prefix: ICT26_MSS_ |
//| |
//| Public API: |
//| void IctMssDraw_Render(sym) |
//| void IctMssDraw_DeleteAll() |
//+------------------------------------------------------------------+
#ifndef ICT2026_MSSDRAW_MQH
#define ICT2026_MSSDRAW_MQH
+324 -5
View File
@@ -1,5 +1,64 @@
//+------------------------------------------------------------------+
//| MssEntry.mqh — Limit @ M5 FVG edge | SL H0/L0 M5 | TP min RR |
//| MssEntry.mqh — Order management cho MSS pipeline |
//+------------------------------------------------------------------+
//| Trách nhiệm: tính SL/TP/lot, đặt + sửa + huỷ pending limit, |
//| theo dõi position để partial close + BE + cancel. |
//| |
//| Gating (từ ngoài vào trong): |
//| gate1: InpMssTradeEnabled |
//| gate2: phase >= CHOCH && chochLocked && chochKeyLevel > 0 |
//| gate3: !intradayBlock |
//| intradayBlock = InpMssRequireIntradayAligned |
//| && !isAllowTrade (v1.181) |
//| gate4: !InpMssOnePosition || no existing position+pending |
//| |
//| ComputeLevels (v1.164 + v1.184): |
//| side ← (m5Zone.side) hoặc chochBias |
//| SL ← max(H0,H1)+buf (BEAR) / min(L0,L1)-buf (BULL) |
//| buf = InpMssSlSpreadMult × spread |
//| 2 candidates entry: |
//| A) M5 FVG limit price |
//| B) MSS keyLV (chochKeyLevel) |
//| ⇒ chọn cái có risk = |entry-SL| nhỏ hơn (đúng phía thị trường) |
//| TP target = iH0/iL0 (sóng H1): |
//| RR(TP@swing) > InpMssMinRR ⇒ TP @ swing - InpMssTpSpreadMult |
//| else ⇒ TP = entry ± risk × InpMssMinRR (2R fixed) |
//| partialTrigger = swingTp nếu TP gồng xa hơn swing |
//| ⇒ partial 50% + SL→BE khi giá đạt swingTp |
//| |
//| CHECK CUỐI (v1.184): |
//| |entry - market| ≤ InpMssMaxLimitDistAtrMult × ATR(FvgTf) |
//| Quá xa ⇒ skip + mark FVG used + reset state |
//| |
//| Lot sizing: IctMssEntry_VolumeForRisk uses InpMssRiskPct × balance|
//| |
//| Per-tick checks (gọi trong IctMssEntry_Update): |
//| IctMssEntry_CheckEodCancel(sym) — EOD phiên Mỹ |
//| IctMssEntry_CheckPendingTimeout(sym) — timeout 1h |
//| IctMssEntry_CheckTpReachedBeforeFill(sym) — TP chạm trước fill|
//| (v1.185) |
//| IctMssEntry_CheckStaleLimit(sym) — limit xa giá |
//| > N×ATR (v1.184)|
//| IctMssEntry_CheckBreakevenAtRR(sym) — SL→entry @ N×R |
//| (v1.176-7, off) |
//| IctMssEntry_CheckPartialClose(sym) — partial 50% + |
//| SL→BE @ swingTp |
//| |
//| Globals owned: |
//| g_ictMssTrade — CTrade instance dùng cho tất cả order ops |
//| |
//| Lifecycle: |
//| IctMss_OnPositionClosed (gọi từ OnTradeTransaction trong EA): |
//| - MarkM5FvgUsed |
//| - IctMss_ResetState |
//| - set g_ictMssAfterCloseGuard = TimeCurrent |
//| - reset về WAIT_FVG_TOUCH |
//| |
//| Public API (chính): |
//| void IctMssEntry_Init() / IctMssEntry_Update(sym) |
//| bool IctMssEntry_ComputeLevels(sym, m5Zone, &entry, &sl, &tp, |
//| &reasonOut) |
//| void IctMss_OnPositionClosed(sym, reason, netProfit) |
//+------------------------------------------------------------------+
#ifndef ICT2026_MSSENTRY_MQH
#define ICT2026_MSSENTRY_MQH
@@ -64,6 +123,32 @@ double IctMssEntry_LimitPrice(const IctFvgZone &zone)
return 0.0;
}
// Cap khoảng cách entry-giá theo bội số ATR(FvgTf):
// BUY ⇒ distance = Ask - entry (entry phải nằm dưới Ask cho limit buy)
// SELL ⇒ distance = entry - Bid (entry phải nằm trên Bid cho limit sell)
// Trả về true nếu distance > InpMssMaxLimitDistAtrMult × ATR ⇒ "limit quá xa" (chết).
bool IctMssEntry_IsLimitTooFar(const string sym, const double entry, const bool isBuy,
double &distOut, double &thresholdOut)
{
distOut = 0.0;
thresholdOut = 0.0;
if(InpMssMaxLimitDistAtrMult <= 0.0)
return false;
const double atr = IctFvg_AtrFvgTf(sym);
if(atr <= 0.0)
return false;
const double mkt = isBuy ? SymbolInfoDouble(sym, SYMBOL_ASK)
: SymbolInfoDouble(sym, SYMBOL_BID);
if(mkt <= 0.0 || entry <= 0.0)
return false;
distOut = isBuy ? (mkt - entry) : (entry - mkt);
thresholdOut = atr * InpMssMaxLimitDistAtrMult;
return (distOut > thresholdOut + _Point);
}
bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone,
double &entryOut, double &slOut, double &tpOut,
string &reasonOut)
@@ -166,6 +251,18 @@ bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone,
return false;
}
// Cap khoảng cách entry-giá: tránh đặt limit "chết" sau khi MSS confirm muộn
// ⇒ limit phải nằm trong InpMssMaxLimitDistAtrMult × ATR(FvgTf) tính từ giá hiện tại
double tooFarDist = 0.0, tooFarThresh = 0.0;
if(IctMssEntry_IsLimitTooFar(sym, entryOut, isBuy, tooFarDist, tooFarThresh))
{
reasonOut = StringFormat(
"Entry xa giá %.0f pts > %.0f pts (%.1f×ATR) — bỏ qua [src=%s]",
tooFarDist / _Point, tooFarThresh / _Point,
InpMssMaxLimitDistAtrMult, entrySource);
return false;
}
// ── 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;
@@ -352,6 +449,198 @@ void IctMss_OnPendingTimeout(const string sym, const ulong ticket, const int hou
TimeToString(g_ictMssAfterCloseGuard, TIME_DATE | TIME_MINUTES));
}
// Cancel pending + mark FVG used + reset state khi limit rời xa giá vượt cap.
// ⇒ tránh case MSS confirm muộn → limit đặt rất xa hiện tại, không bao giờ khớp.
// Trigger:
// - Bị gọi mỗi tick trong IctMssEntry_Update
// - InpMssCancelStaleLimit phải bật
// - Có pendingTicket hợp lệ (đã đặt limit)
// - (mkt - entry) (BUY) hoặc (entry - mkt) (SELL) > InpMssMaxLimitDistAtrMult × ATR(FvgTf)
void IctMssEntry_CheckStaleLimit(const string sym)
{
if(!InpMssCancelStaleLimit)
return;
if(InpMssMaxLimitDistAtrMult <= 0.0)
return;
if(g_ictLowTf.mss.pendingTicket == 0)
return;
if(!OrderSelect(g_ictLowTf.mss.pendingTicket))
return;
const ENUM_ORDER_TYPE otype = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
if(otype != ORDER_TYPE_BUY_LIMIT && otype != ORDER_TYPE_SELL_LIMIT)
return;
const bool isBuy = (otype == ORDER_TYPE_BUY_LIMIT);
const double entry = OrderGetDouble(ORDER_PRICE_OPEN);
double dist = 0.0, thresh = 0.0;
if(!IctMssEntry_IsLimitTooFar(sym, entry, isBuy, dist, thresh))
return;
const ulong ticket = g_ictLowTf.mss.pendingTicket;
const ulong m5Id = g_ictLowTf.mss.m5FvgId;
const ulong h1Id = g_ictLowTf.mss.h1FvgId;
g_ictMssTrade.OrderDelete(ticket);
g_ictLowTf.mss.pendingTicket = 0;
IctMssEntry_MarkM5FvgUsed(sym, m5Id);
IctMss_ResetState();
g_ictMssAfterCloseGuard = TimeCurrent();
g_ictLowTf.mss.displayReason = StringFormat(
"Limit chết: cách giá %.0f pts > %.0f pts (%.1f×ATR) — cancel #%I64u + reset",
dist / _Point, thresh / _Point, InpMssMaxLimitDistAtrMult, ticket);
if(InpMssLogJournal)
PrintFormat("[ICT2026/MSS] Stale limit cancel %s #%I64u | dist=%.0f pts thresh=%.0f pts | H1 #%I64u | M5 #%I64u → Used | guard=%s | reset → WAIT_FVG_TOUCH",
isBuy ? "BUY" : "SELL", ticket,
dist / _Point, thresh / _Point, h1Id, m5Id,
TimeToString(g_ictMssAfterCloseGuard, TIME_DATE | TIME_MINUTES));
}
// Cancel pending limit khi giá chạm TP TRƯỚC khi limit khớp.
// ⇒ Tránh case: giá chạy thẳng đến TP target, không hồi lại entry, sau đó
// đảo chiều quay về khớp limit ở entry cũ → lệnh chạy ngược, SL.
// Logic:
// BUY limit: nếu Bid ≥ TP ⇒ giá đã chạm TP (lúc fill xong sẽ TP ngay)
// SELL limit: nếu Ask ≤ TP ⇒ giá đã chạm TP
// Sau cancel: mark FVG used + reset (giống stale limit) + guard sau-close.
void IctMssEntry_CheckTpReachedBeforeFill(const string sym)
{
if(!InpMssCancelLimitWhenTpReached)
return;
if(g_ictLowTf.mss.pendingTicket == 0)
return;
if(!OrderSelect(g_ictLowTf.mss.pendingTicket))
return;
const ENUM_ORDER_TYPE otype = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
if(otype != ORDER_TYPE_BUY_LIMIT && otype != ORDER_TYPE_SELL_LIMIT)
return;
const bool isBuy = (otype == ORDER_TYPE_BUY_LIMIT);
const double tp = OrderGetDouble(ORDER_TP);
if(tp <= 0.0)
return;
const double bid = SymbolInfoDouble(sym, SYMBOL_BID);
const double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
const bool reached = isBuy ? (bid >= tp - _Point)
: (ask <= tp + _Point);
if(!reached)
return;
const ulong ticket = g_ictLowTf.mss.pendingTicket;
const ulong m5Id = g_ictLowTf.mss.m5FvgId;
const ulong h1Id = g_ictLowTf.mss.h1FvgId;
const double entry = OrderGetDouble(ORDER_PRICE_OPEN);
g_ictMssTrade.OrderDelete(ticket);
g_ictLowTf.mss.pendingTicket = 0;
IctMssEntry_MarkM5FvgUsed(sym, m5Id);
IctMss_ResetState();
g_ictMssAfterCloseGuard = TimeCurrent();
g_ictLowTf.mss.displayReason = StringFormat(
"Giá chạm TP trước khi khớp limit (TP=%.2f, %s=%.2f) — cancel #%I64u + reset",
tp, isBuy ? "Bid" : "Ask", isBuy ? bid : ask, ticket);
if(InpMssLogJournal)
PrintFormat("[ICT2026/MSS] TP-reached cancel %s #%I64u | entry=%.5f tp=%.5f bid=%.5f ask=%.5f | H1 #%I64u | M5 #%I64u → Used | guard=%s | reset → WAIT_FVG_TOUCH",
isBuy ? "BUY" : "SELL", ticket,
entry, tp, bid, ask, h1Id, m5Id,
TimeToString(g_ictMssAfterCloseGuard, TIME_DATE | TIME_MINUTES));
}
// Dời SL về entry (BE) khi lệnh đi được N×R (mặc định 2R) — bảo toàn vốn sớm.
// - Không partial close (khác với CheckPartialClose ở swing iL0/iH0)
// - Skip nếu BE đã move (beMovedDone) hoặc partial close đã chạy (đã set BE)
void IctMssEntry_CheckBreakevenAtRR(const string sym)
{
if(!InpMssBeEnabled)
return;
if(InpMssBeAtRR <= 0.0)
return;
if(g_ictLowTf.mss.beMovedDone || g_ictLowTf.mss.partialCloseDone)
return;
if(g_ictLowTf.mss.pendingEntry <= 0.0 || g_ictLowTf.mss.pendingSl <= 0.0)
return;
const double entry = g_ictLowTf.mss.pendingEntry;
const double slOrig = g_ictLowTf.mss.pendingSl;
const double risk = MathAbs(entry - slOrig);
if(risk <= 0.0)
return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != sym)
continue;
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMssMagic)
continue;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double openPx = PositionGetDouble(POSITION_PRICE_OPEN);
const double curSl = PositionGetDouble(POSITION_SL);
const double curTp = PositionGetDouble(POSITION_TP);
const double bid = SymbolInfoDouble(sym, SYMBOL_BID);
const double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
bool reached = false;
double trigPx = 0.0;
if(ptype == POSITION_TYPE_BUY)
{
trigPx = openPx + InpMssBeAtRR * risk;
reached = (bid >= trigPx - _Point);
}
else if(ptype == POSITION_TYPE_SELL)
{
trigPx = openPx - InpMssBeAtRR * risk;
reached = (ask <= trigPx + _Point);
}
if(!reached)
return;
// Skip nếu SL hiện tại đã ≥ BE (tránh dời lùi)
bool slAlreadyAtBe = false;
if(ptype == POSITION_TYPE_BUY)
slAlreadyAtBe = (curSl >= openPx - _Point);
else
slAlreadyAtBe = (curSl > 0.0 && curSl <= openPx + _Point);
if(slAlreadyAtBe)
{
g_ictLowTf.mss.beMovedDone = true;
return;
}
g_ictMssTrade.SetExpertMagicNumber(InpMssMagic);
if(!g_ictMssTrade.PositionModify(ticket, openPx, curTp))
{
if(InpMssLogJournal)
PrintFormat("[ICT2026/MSS] BE@%.1fR move fail #%I64u — %d %s",
InpMssBeAtRR, ticket, g_ictMssTrade.ResultRetcode(),
g_ictMssTrade.ResultRetcodeDescription());
return;
}
g_ictLowTf.mss.beMovedDone = true;
if(InpMssLogJournal)
PrintFormat("[ICT2026/MSS] BE@%.1fR #%I64u price=%.2f → SL %.2f→%.2f (entry) | TP %.2f",
InpMssBeAtRR, ticket,
(ptype == POSITION_TYPE_BUY ? bid : ask),
curSl, openPx, curTp);
return;
}
}
// Chốt 50% volume + dời SL về BE khi giá đạt swing iL0/iH0 (chỉ áp dụng khi TP gồng xa hơn swing)
void IctMssEntry_CheckPartialClose(const string sym)
{
@@ -581,6 +870,9 @@ void IctMssEntry_Update(const string sym)
IctMssEntry_CheckEodCancel(sym);
IctMssEntry_CheckPendingTimeout(sym);
IctMssEntry_CheckTpReachedBeforeFill(sym);
IctMssEntry_CheckStaleLimit(sym);
IctMssEntry_CheckBreakevenAtRR(sym);
IctMssEntry_CheckPartialClose(sym);
if(g_ictLowTf.mss.phase == ICT_MSS_IDLE && g_ictLowTf.mss.pendingTicket > 0)
@@ -597,8 +889,14 @@ void IctMssEntry_Update(const string sym)
}
// Cho phép entry từ CHOCH trở lên (không bắt buộc M5 FVG):
// ComputeLevels sẽ chọn entry giữa M5 FVG (nếu có) và MSS keyLV (luôn có sau lock)
if(!g_ictIntraday.isAllowTrade ||
// ComputeLevels sẽ chọn entry giữa M5 FVG (nếu có) và MSS keyLV (luôn có sau lock).
//
// Intraday gate: CHỈ chặn khi InpMssRequireIntradayAligned=true.
// Mặc định (false) ⇒ entry chỉ dựa vào Daily Bias, không quan tâm Intraday trend
// (tránh miss setup khi trend chuyển hướng muộn).
const bool intradayBlock = (InpMssRequireIntradayAligned && !g_ictIntraday.isAllowTrade);
if(intradayBlock ||
g_ictLowTf.mss.phase < ICT_MSS_CHOCH ||
!g_ictLowTf.mss.chochLocked ||
g_ictLowTf.mss.chochKeyLevel <= 0.0)
@@ -610,8 +908,8 @@ void IctMssEntry_Update(const string sym)
}
if(g_ictLowTf.mss.phase >= ICT_MSS_H1_TOUCH)
{
if(!g_ictIntraday.isAllowTrade)
IctMss_JournalEntryBlock("AllowTrade=false");
if(intradayBlock)
IctMss_JournalEntryBlock("Intraday ngược Bias (InpMssRequireIntradayAligned=true)");
else if(g_ictLowTf.mss.phase < ICT_MSS_CHOCH)
IctMss_JournalEntryBlock(g_ictLowTf.mss.displayReason);
else
@@ -648,6 +946,27 @@ void IctMssEntry_Update(const string sym)
IctMssPhaseText(g_ictLowTf.mss.phase),
lvlReason);
IctMss_JournalEntryBlock(g_ictLowTf.mss.displayReason);
// Nếu fail vì "Entry xa giá" + bật cancel-stale ⇒ reset setup ngay
// (tránh stuck CHOCH với entry không bao giờ khớp; chờ POI/touch mới)
if(InpMssCancelStaleLimit && StringFind(lvlReason, "Entry xa giá") == 0)
{
const ulong h1Id = g_ictLowTf.mss.h1FvgId;
const ulong m5Id = g_ictLowTf.mss.m5FvgId;
if(g_ictLowTf.mss.pendingTicket > 0)
{
IctMssEntry_CancelTicket(g_ictLowTf.mss.pendingTicket);
g_ictLowTf.mss.pendingTicket = 0;
}
IctMssEntry_MarkM5FvgUsed(sym, m5Id);
IctMss_ResetState();
g_ictMssAfterCloseGuard = TimeCurrent();
g_ictLowTf.mss.displayReason = StringFormat(
"%s | %s — reset, chờ POI/touch mới", IctMssPhaseText(ICT_MSS_IDLE), lvlReason);
if(InpMssLogJournal)
PrintFormat("[ICT2026/MSS] Skip entry (too far): %s | H1 #%I64u | M5 #%I64u → Used | reset",
lvlReason, h1Id, m5Id);
}
return;
}
+294 -33
View File
@@ -1,10 +1,60 @@
//+------------------------------------------------------------------+
//| MssSetup.mqh — MSS: retest FVG H1 → M5 MSS (phá L0/H0) → M5 FVG → entry |
//| MssSetup.mqh — MSS pipeline (setup phase, không đặt order) |
//+------------------------------------------------------------------+
//| Flow: |
//| IDLE |
//| │ IctMss_SelectNearestH1Poi() ⇒ chọn H1 FVG gần giá nhất |
//| │ IctMss_HasFreshFvgTouch() ⇒ touch hợp lệ |
//| ↓ |
//| H1_TOUCH |
//| │ IctMss_UpdateLiveM5Swings() ⇒ liveL0/H0/L1/H1 structural |
//| │ (v1.171: keylv lock) |
//| │ IctMss_TryLockMss() ⇒ break body M5 qua L0/H0 |
//| │ veto MSS nếu InpMssRequireIntradayAligned && ngược bias |
//| ↓ |
//| CHOCH (locked) |
//| │ IctConfirmFvg_ScanNew() ⇒ tìm M5 FVG sau chochTime |
//| │ IctMss_ZonesOverlapH1() ⇒ M5 FVG gần H1 FVG |
//| ↓ |
//| M5_FVG → ENTRY_FILL → READY (chờ giá hồi vào M5 FVG) |
//| |
//| FVG fresh touch (v1.182) — cutoff bias-aware: |
//| cutoff = max(g_ictMssAfterCloseGuard, |
//| g_ictBiasInto{Bull,Bear}Time) |
//| Touch hợp lệ phải xảy ra SAU cutoff. |
//| |
//| Structural keylv (v1.171) — không phải "pivot mới nhất": |
//| BEAR (pullback UP): |
//| liveH0 = đỉnh CAO NHẤT sau touch (extreme) |
//| liveL0 = đáy gần nhất trước liveH0 (keylv MSS↓) |
//| liveH1 = đỉnh trước liveL0 |
//| BULL (pullback DOWN): |
//| liveL0 = đáy THẤP NHẤT sau touch (extreme) |
//| liveH0 = đỉnh gần nhất trước liveL0 (keylv MSS↑) |
//| liveL1 = đáy trước liveH0 |
//| ⇒ "lock" tự nhiên: pivot SAU extreme không đổi keylv |
//| |
//| MSS lock yêu cầu (v1.165): |
//| - cấu trúc đủ 2 đỉnh/đáy SAU touch |
//| - body M5 phá liveL0 (BEAR) / liveH0 (BULL) |
//| |
//| Globals owned: |
//| g_ictMssAfterCloseGuard — TS sau khi 1 lệnh đóng (TP/SL/timeout/|
//| stale limit): pipeline phải đợi touch fresh sau TS này |
//| |
//| Public API (chính): |
//| void IctMss_Update(sym) |
//| bool IctMss_HasFreshFvgTouch(sym, &h1Zone) |
//| datetime IctMss_GetFvgTouchTime(sym, &h1Zone) |
//| void IctMss_ResetState() |
//| bool IctMss_TryLockMss(sym, tf, h1, bias) |
//| int IctMss_CountH1PoiEligible() |
//+------------------------------------------------------------------+
#ifndef ICT2026_MSSSETUP_MQH
#define ICT2026_MSSSETUP_MQH
#include <ICT2026/ConfirmFvg.mqh>
#include <ICT2026/DailyBias.mqh>
#include <ICT2026/Journal.mqh>
#include <ICT2026/LowTfApi.mqh>
#include <ICT2026/StructureCore.mqh>
@@ -109,16 +159,18 @@ bool IctMss_LivePriceTouchesFvg(const string sym, const IctFvgZone &h1)
return IctMss_PriceInsideFvg(h1, bid) || IctMss_PriceInsideFvg(h1, ask);
}
datetime IctMss_FirstM5TouchInFvg(const string sym, const IctFvgZone &h1)
// Touch M5 đầu tiên sau `since` (0 = bất kỳ)
datetime IctMss_FirstM5TouchInFvg(const string sym, const IctFvgZone &h1, const datetime since = 0)
{
const ENUM_TIMEFRAMES tf = InpConfirmTf;
const int lim = MathMin(InpMssConfirmLookback, iBars(sym, tf) - 2);
const datetime minT = (since > h1.createdTime) ? since : h1.createdTime;
datetime best = 0;
for(int sh = lim; sh >= 1; sh--)
{
const datetime t = iTime(sym, tf, sh);
if(t <= h1.createdTime)
break;
if(t <= minT)
continue;
if(!IctMss_BarOverlapsFvg(h1, iHigh(sym, tf, sh), iLow(sym, tf, sh)))
continue;
if(best == 0 || t < best)
@@ -127,17 +179,41 @@ datetime IctMss_FirstM5TouchInFvg(const string sym, const IctFvgZone &h1)
return best;
}
// Cutoff bias-aware: touch chỉ hợp lệ khi xảy ra SAU thời điểm Bias chuyển vào
// side hiện tại VÀ sau thời điểm lệnh trước đóng.
datetime IctMss_GetFvgTouchCutoff()
{
datetime cutoff = 0;
if(g_ictDailyBias.bias == ICT_BIAS_BULL && g_ictBiasIntoBullTime > 0)
cutoff = g_ictBiasIntoBullTime;
else if(g_ictDailyBias.bias == ICT_BIAS_BEAR && g_ictBiasIntoBearTime > 0)
cutoff = g_ictBiasIntoBearTime;
if(g_ictMssAfterCloseGuard > cutoff)
cutoff = g_ictMssAfterCloseGuard;
return cutoff;
}
datetime IctMss_GetFvgTouchTime(const string sym, IctFvgZone &h1)
{
IctFvg_UpdateZoneState(sym, InpFvgTf, h1);
const datetime cutoff = IctMss_GetFvgTouchCutoff();
datetime t = h1.firstTouchTime;
const datetime tM5 = IctMss_FirstM5TouchInFvg(sym, h1);
// firstTouchTime chỉ hợp lệ nếu xảy ra sau cutoff
datetime t = 0;
if(h1.firstTouchTime > 0 && h1.firstTouchTime >= cutoff)
t = h1.firstTouchTime;
// Quét M5 bars để tìm touch fresh sau cutoff (bao gồm cả re-touch sau bias flip)
const datetime tM5 = IctMss_FirstM5TouchInFvg(sym, h1, cutoff);
if(tM5 > 0 && (t <= 0 || tM5 < t))
t = tM5;
if(t <= 0 && IctMss_LivePriceTouchesFvg(sym, h1))
t = iTime(sym, InpConfirmTf, 0);
{
const datetime curBar = iTime(sym, InpConfirmTf, 0);
if(curBar >= cutoff)
t = curBar;
}
return t;
}
@@ -148,16 +224,15 @@ bool IctMss_HasFvgPriceTouch(const string sym, IctFvgZone &h1)
return (IctMss_GetFvgTouchTime(sym, h1) > 0);
}
// Touch hợp lệ cho POI mới = touch xảy ra SAU thời điểm lệnh trước đóng + đạt FILL threshold theo PD position.
// - FVG nằm hẳn trong vùng đúng chiều → InpFvgTouchFillPure (25%)
// - FVG straddle equilibrium → InpFvgTouchFillMixed (50%)
// Touch hợp lệ cho POI mới — cutoff đã được apply trong `IctMss_GetFvgTouchTime`:
// - touch xảy ra SAU lệnh trước đóng (g_ictMssAfterCloseGuard)
// - touch xảy ra SAU Bias chuyển vào side hiện tại (g_ictBiasInto*Time)
// - đạt FILL threshold theo PD position (Pure 25% / Mixed 50%)
bool IctMss_HasFreshFvgTouch(const string sym, IctFvgZone &h1)
{
const datetime tTouch = IctMss_GetFvgTouchTime(sym, h1);
if(tTouch <= 0)
return false;
if(g_ictMssAfterCloseGuard > 0 && tTouch < g_ictMssAfterCloseGuard)
return false;
const double requiredPct = IctFvg_RequiredTouchFillPct(h1);
if(requiredPct >= 100.0 - 1e-6)
@@ -289,6 +364,29 @@ bool IctMss_HasM5BarInH1Fvg(const string sym, const IctFvgZone &h1,
return false;
}
// Structural keylv — KHÔNG dùng "pivot mới nhất" (nhảy mỗi nến).
// Định nghĩa: keylv = pivot GẦN NHẤT TRƯỚC extreme của pullback ⇒
// tự nhiên "lock" tới khi pullback mở rộng (BEAR: tạo high mới cao hơn;
// BULL: tạo low mới thấp hơn).
//
// BEAR bias (pullback UP, mục tiêu MSS↓ phá L0):
// liveH0 = ĐỈNH CAO NHẤT sau tTouch (extreme đỉnh pullback)
// liveL0 = đáy gần nhất theo time TRƯỚC liveH0 (keylv — "đáy tạo ra đỉnh cao nhất")
// liveH1 = đỉnh gần nhất TRƯỚC liveL0 (đỉnh cũ — đủ cấu trúc 2 đỉnh)
// liveL1 = đáy gần nhất TRƯỚC liveH1 (đáy cũ — đủ cấu trúc 2 đáy)
// SL = max(H0, H1) + buffer
//
// BULL bias (pullback DOWN, mục tiêu MSS↑ phá H0):
// liveL0 = ĐÁY THẤP NHẤT sau tTouch (extreme đáy pullback)
// liveH0 = đỉnh gần nhất theo time TRƯỚC liveL0 (keylv — "đỉnh tạo ra đáy thấp nhất")
// liveL1 = đáy gần nhất TRƯỚC liveH0 (đáy cũ — đủ cấu trúc 2 đáy)
// liveH1 = đỉnh gần nhất TRƯỚC liveL1 (đỉnh cũ — đủ cấu trúc 2 đỉnh)
// SL = min(L0, L1) buffer
//
// Nhờ tham chiếu "TRƯỚC extreme":
// - Đỉnh mới tạo SAU extreme low (bullish bias) ⇒ KHÔNG ảnh hưởng keylv
// - Đáy mới tạo SAU extreme high (bearish bias) ⇒ KHÔNG ảnh hưởng keylv
// ⇒ keylv chỉ "nhảy" khi pullback thực sự mở rộng tạo extreme mới
bool IctMss_UpdateLiveM5Swings(const string sym, const ENUM_ICT_BIAS bias,
const datetime tTouch)
{
@@ -301,38 +399,184 @@ bool IctMss_UpdateLiveM5Swings(const string sym, const ENUM_ICT_BIAS bias,
g_ictLowTf.mss.liveL1Time = 0;
g_ictLowTf.mss.liveH1Time = 0;
IctSwingSet sw;
ENUM_ICT_STRUCT st = ICT_STRUCT_NONE;
if(!IctBuildConfirmSwingSet(sym, sw, st))
if(bias != ICT_BIAS_BEAR && bias != ICT_BIAS_BULL)
return false;
if(bias == ICT_BIAS_BEAR || bias == ICT_BIAS_BULL)
// BẮT BUỘC có tTouch hợp lệ — nếu không pipeline sẽ lấy cả pivot trước touch
// (filter `time < tTouch` không có tác dụng khi tTouch = 0).
if(tTouch <= 0)
return false;
IctSwingPoint highs[], lows[];
IctCollectSwings(sym, InpConfirmTf, InpConfirmSwingRange,
InpConfirmSwingLookback, highs, lows);
const int nH = ArraySize(highs);
const int nL = ArraySize(lows);
if(nH == 0 || nL == 0)
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;
if(sw.hasL1)
// 1) liveH0 = đỉnh cao nhất sau tTouch (extreme của pullback up)
int idxH0 = -1;
double maxHigh = -DBL_MAX;
for(int i = 0; i < nH; i++)
{
g_ictLowTf.mss.liveL1Price = sw.l1.price;
g_ictLowTf.mss.liveL1Time = sw.l1.time;
if(highs[i].time < tTouch) continue;
if(highs[i].price > maxHigh)
{
maxHigh = highs[i].price;
idxH0 = i;
}
}
if(sw.hasH1)
if(idxH0 < 0) return false;
g_ictLowTf.mss.liveH0Price = highs[idxH0].price;
g_ictLowTf.mss.liveH0Time = highs[idxH0].time;
const datetime tH0 = highs[idxH0].time;
// 2) liveL0 = đáy GẦN NHẤT THEO TIME TRƯỚC liveH0 (keylv MSS↓)
int idxL0 = -1;
datetime tL0Best = 0;
for(int i = 0; i < nL; i++)
{
g_ictLowTf.mss.liveH1Price = sw.h1.price;
g_ictLowTf.mss.liveH1Time = sw.h1.time;
if(lows[i].time < tTouch) continue;
if(lows[i].time >= tH0) continue;
if(lows[i].time > tL0Best)
{
tL0Best = lows[i].time;
idxL0 = i;
}
}
if(idxL0 < 0) return false;
g_ictLowTf.mss.liveL0Price = lows[idxL0].price;
g_ictLowTf.mss.liveL0Time = lows[idxL0].time;
const datetime tL0 = lows[idxL0].time;
// 3) liveH1 = đỉnh GẦN NHẤT THEO TIME TRƯỚC liveL0
int idxH1 = -1;
datetime tH1Best = 0;
for(int i = 0; i < nH; i++)
{
if(highs[i].time < tTouch) continue;
if(highs[i].time >= tL0) continue;
if(highs[i].time > tH1Best)
{
tH1Best = highs[i].time;
idxH1 = i;
}
}
if(idxH1 >= 0)
{
g_ictLowTf.mss.liveH1Price = highs[idxH1].price;
g_ictLowTf.mss.liveH1Time = highs[idxH1].time;
}
// 4) liveL1 = đáy GẦN NHẤT THEO TIME TRƯỚC liveH1 (nếu có)
if(idxH1 >= 0)
{
int idxL1 = -1;
datetime tL1Best = 0;
for(int i = 0; i < nL; i++)
{
if(lows[i].time < tTouch) continue;
if(lows[i].time >= tH1Best) continue;
if(lows[i].time > tL1Best)
{
tL1Best = lows[i].time;
idxL1 = i;
}
}
if(idxL1 >= 0)
{
g_ictLowTf.mss.liveL1Price = lows[idxL1].price;
g_ictLowTf.mss.liveL1Time = lows[idxL1].time;
}
}
return true;
}
return false;
// BULL bias (pullback DOWN)
// 1) liveL0 = đáy thấp nhất sau tTouch (extreme của pullback down)
int idxL0 = -1;
double minLow = DBL_MAX;
for(int i = 0; i < nL; i++)
{
if(lows[i].time < tTouch) continue;
if(lows[i].price < minLow)
{
minLow = lows[i].price;
idxL0 = i;
}
}
if(idxL0 < 0) return false;
g_ictLowTf.mss.liveL0Price = lows[idxL0].price;
g_ictLowTf.mss.liveL0Time = lows[idxL0].time;
const datetime tL0 = lows[idxL0].time;
// 2) liveH0 = đỉnh GẦN NHẤT THEO TIME TRƯỚC liveL0 (keylv MSS↑)
int idxH0 = -1;
datetime tH0Best = 0;
for(int i = 0; i < nH; i++)
{
if(highs[i].time < tTouch) continue;
if(highs[i].time >= tL0) continue;
if(highs[i].time > tH0Best)
{
tH0Best = highs[i].time;
idxH0 = i;
}
}
if(idxH0 < 0) return false;
g_ictLowTf.mss.liveH0Price = highs[idxH0].price;
g_ictLowTf.mss.liveH0Time = highs[idxH0].time;
const datetime tH0 = highs[idxH0].time;
// 3) liveL1 = đáy GẦN NHẤT THEO TIME TRƯỚC liveH0
int idxL1 = -1;
datetime tL1Best = 0;
for(int i = 0; i < nL; i++)
{
if(lows[i].time < tTouch) continue;
if(lows[i].time >= tH0) continue;
if(lows[i].time > tL1Best)
{
tL1Best = lows[i].time;
idxL1 = i;
}
}
if(idxL1 >= 0)
{
g_ictLowTf.mss.liveL1Price = lows[idxL1].price;
g_ictLowTf.mss.liveL1Time = lows[idxL1].time;
}
// 4) liveH1 = đỉnh GẦN NHẤT THEO TIME TRƯỚC liveL1 (nếu có)
if(idxL1 >= 0)
{
int idxH1 = -1;
datetime tH1Best = 0;
for(int i = 0; i < nH; i++)
{
if(highs[i].time < tTouch) continue;
if(highs[i].time >= tL1Best) continue;
if(highs[i].time > tH1Best)
{
tH1Best = highs[i].time;
idxH1 = i;
}
}
if(idxH1 >= 0)
{
g_ictLowTf.mss.liveH1Price = highs[idxH1].price;
g_ictLowTf.mss.liveH1Time = highs[idxH1].time;
}
}
return true;
}
// 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.
// v1.186: dùng IctBodyBreakBuffer (N × spread) thay vì _Point — siết để
// một close-through "sát mép" do nhiễu không làm invalidate setup.
bool IctMss_H1BodyInvalidatedSetup(const string sym, const IctFvgZone &h1,
const ENUM_ICT_BIAS bias)
{
@@ -340,10 +584,11 @@ bool IctMss_H1BodyInvalidatedSetup(const string sym, const IctFvgZone &h1,
return false;
const double cls = iClose(sym, InpFvgTf, 1);
const double buf = IctBodyBreakBuffer(sym);
if(bias == ICT_BIAS_BEAR)
return (cls > h1.upper + _Point);
return (cls > h1.upper + buf);
if(bias == ICT_BIAS_BULL)
return (cls < h1.lower - _Point);
return (cls < h1.lower - buf);
return false;
}
@@ -875,6 +1120,22 @@ void IctMss_Update(const string sym)
if(!IctMss_TryLockMss(sym, cTf, g_ictFvgZones[h1Idx], g_ictDailyBias.bias))
return;
// Veto MSS nếu Intraday NGƯỢC Bias — CHỈ áp dụng khi yêu cầu align (mặc định OFF).
// Mặc định: cho phép MSS dù Intraday ngược ⇒ entry sớm hơn khi trend đổi muộn,
// giảm trường hợp "giá chạy tới TP rồi mới khớp limit → quay lại SL".
if(InpMssRequireIntradayAligned &&
!IctIntraday_TrendAlignsWithBias(g_ictDailyBias.bias, g_ictIntraday.trend))
{
IctMss_MarkFvgUsedOnMssSuccess(sym, g_ictLowTf.mss.h1FvgId);
const string sideLbl = (g_ictDailyBias.bias == ICT_BIAS_BEAR) ? "MSS↓" : "MSS↑";
const string trendLbl = IctTrendDisplayShort(g_ictIntraday.trend);
IctMss_ResetState();
g_ictLowTf.mss.displayReason = StringFormat(
"%s khớp khi Intraday=%s ngược Bias — bỏ POI, chờ POI mới",
sideLbl, trendLbl);
return;
}
IctMss_MarkFvgUsedOnMssSuccess(sym, g_ictLowTf.mss.h1FvgId);
const double keyLv = g_ictLowTf.mss.chochKeyLevel;
+34 -2
View File
@@ -1,6 +1,38 @@
//+------------------------------------------------------------------+
//| Panel.mqh — Daily Bias + Intraday (mỗi dòng = 1 OBJ_LABEL) |
//| Bias/Intraday: xanh = Up, đỏ = Down — cùng màu = canh trade |
//| Panel.mqh — overview panel góc trên trái (~10 dòng) |
//+------------------------------------------------------------------+
//| Mỗi dòng = 1 OBJ_LABEL độc lập (OBJ_LABEL không hỗ trợ \n trong |
//| MT5). Stack dọc với spacing InpPanelLineSpacing px. |
//| |
//| Layout (top→bottom): |
//| [CATEGORY] CODE ← EaState |
//| <state title> |
//| <state detail> |
//| ──────────── |
//| Bias: <bias> (xanh=Up, đỏ=Down, vàng=Range, xám=None)|
//| Ly do: <reason> |
//| ──────────── |
//| PERIOD_<intradayTf> |
//| Intraday: <trend> |
//| Ly do: <intraday reason> |
//| IsAllowTrade: <true/false> |
//| AllowEntry: <true/false> |
//| ──────────── |
//| <MSS pipeline displayReason> |
//| ──────────── |
//| Stats: N lệnh | TP X | SL Y |
//| WR XX.X% | Ravg ±N.NNR | Net ±N.NN |
//| |
//| Trigger render: từ ICT_2026.mq5 OnTick khi có nến mới Bias/ |
//| Intraday/LowTF, hoặc force=true ở OnInit. |
//| |
//| Skip nếu: InpOnlyStatsMode = true |
//| |
//| Object prefix: ICT26_PNL_ |
//| |
//| Public API: |
//| void IctPanel_Render(sym) |
//| void IctPanel_DeleteAll() |
//+------------------------------------------------------------------+
#ifndef ICT2026_PANEL_MQH
#define ICT2026_PANEL_MQH
+447 -17
View File
@@ -2,7 +2,7 @@
Expert Advisor ICT 2026 — module hóa theo hướng ICT/SMC. **Không phụ thuộc HyperICT** (chỉ tham khảo ý tưởng swing & key level).
Phiên bản hiện tại: **1.10**
Phiên bản hiện tại: **1.184**
---
@@ -17,13 +17,192 @@ Phiên bản hiện tại: **1.10**
## Mục lục
1. [Cấu trúc thư mục](#folder-structure)
2. [Pipeline Daily Bias](#pipeline)
3. [Logic nghiệp vụ](#business-logic)
4. [Mô tả từng file](#file-reference)
5. [API](#api)
6. [Input](#inputs)
7. [Chưa implement](#roadmap)
8. [Changelog](#changelog)
2. [Current architecture (v1.187) — flow end-to-end](#current-architecture)
3. [Pipeline Daily Bias](#pipeline)
4. [Logic nghiệp vụ](#business-logic)
5. [Mô tả từng file](#file-reference)
6. [API](#api)
7. [Input](#inputs)
8. [Chưa implement](#roadmap)
9. [Changelog](#changelog)
---
<a id="current-architecture"></a>
## Current Architecture (v1.187) — flow end-to-end
> **Nếu bạn chỉ đọc 1 mục, đọc mục này.** Các mục bên dưới là chi tiết từng phần.
### A. Mỗi tick (`OnTick` trong `ICT_2026.mq5`)
```
OnTick(sym)
├─ IctDailyBias_Update(sym) ── D1 nến mới ⇒ recompute bias
│ └─ IctDailyBias_TrackTransition() ⇒ set g_ictBiasInto{Bull,Bear}Time (v1.182)
├─ IctIntraday_Update(sym) ── H1 nến mới ⇒ trend + IsAllowTrade
├─ IctLowTfTrend_Update(sym) ── H1 (FvgTf) nến mới ⇒ FVG scan + MSS pipeline
│ ├─ IctIntraday_UpdateAllowTrade()
│ ├─ IctFvg_UpdateAll(sym, tf) ⇒ Available / Used / PD
│ ├─ IctFvg_ScanNew(...) ⇒ thêm FVG thuận Bias
│ ├─ IctMss_Update(sym) ⇒ phase machine (IDLE→H1_TOUCH→CHOCH→M5_FVG→ENTRY_FILL→READY)
│ ├─ IctMssEntry_Update(sym) ⇒ place/replace/cancel limit
│ └─ IctEaState_Refresh(sym) ⇒ map sang STATE code cho panel
├─ IctLowTfTrend_TickRefresh(sym) ── update fill ratio + PD on every tick
└─ if(!OnlyStatsMode)
├─ IctPanel_Render(sym)
├─ IctDraw_Render(sym)
├─ IctFvgDraw_Render(sym) ⇒ top-N + price-near (v1.180)
└─ IctMssDraw_Render(sym) ⇒ live/locked labels
```
### B. Daily Bias resolution (`DailyBias.mqh`, v1.175)
Thứ tự ưu tiên (cao→thấp):
```
1) D1/D2 pattern (IctDailyBias_ResolveD1D2)
├─ D[1].close > D[2].high → BULL (breakout)
├─ D[1].close < D[2].low → BEAR (breakdown)
├─ D[1].high > D[2].high && close ∈ D[2] → BEAR (sweep bear liq)
└─ D[1].low < D[2].low && close ∈ D[2] → BULL (sweep bull liq)
2) Structural HH-HL / LH-LL (IctResolveTrend)
3) HTF fallback (IctUpdateHTFBias — Previous Day Model)
```
Sau khi resolve: `IctDailyBias_TrackTransition()` cập nhật `g_ictBiasIntoBullTime` / `g_ictBiasIntoBearTime` nếu bias xoay side ⇒ dùng làm cutoff cho FVG fresh touch (xem D bên dưới).
### C. MSS Pipeline (`MssSetup.mqh`)
State machine `g_ictLowTf.mss.phase`:
```
IDLE
│ IctMss_SelectNearestH1Poi() ⇒ chọn H1 FVG gần giá nhất
│ IctMss_HasFreshFvgTouch() ⇒ touch hợp lệ (xem D)
H1_TOUCH ──── (Retest FVG H1 OK)
│ IctMss_UpdateLiveM5Swings() ⇒ liveL0/H0/L1/H1 structural (v1.171)
│ IctMss_TryLockMss() ⇒ M5 phá L0 (BEAR) / H0 (BULL)
│ cấu trúc đầy đủ ≥ 2 đỉnh/đáy SAU touch
│ veto nếu InpMssRequireIntradayAligned && intraday ngược bias (v1.174/1.181)
CHOCH ──── (MSS↑/↓ locked)
│ IctConfirmFvg_ScanNew() ⇒ tìm M5 FVG sau chochTime
│ (M5 FVG optional — entry tại MSS keyLV nếu vắng)
│ IctMss_ZonesOverlapH1() ⇒ M5 FVG phải gần H1 FVG
M5_FVG → ENTRY_FILL → READY ──── (chờ giá hồi vào M5 FVG)
```
Entry gating (`IctMssEntry_Update`):
```
gate1: InpMssTradeEnabled (true)
gate2: phase >= CHOCH && chochLocked && chochKeyLevel > 0
gate3: intradayBlock = InpMssRequireIntradayAligned && !isAllowTrade (default FALSE)
gate4: !InpMssOnePosition || no existing position+pending
```
ComputeLevels (`MssEntry.mqh`, v1.164 + v1.184):
```
side ← (m5 FVG.side) hoặc chochBias
SL ← max(H0,H1)+buffer (BEAR) / min(L0,L1)-buffer (BULL)
buffer = InpMssSlSpreadMult × spread
2 candidates entry:
A) M5 FVG limit price
B) MSS keyLV (chochKeyLevel)
chọn cái có risk = |entry - SL| nhỏ hơn (limit phải đúng phía thị trường)
TP target = iH0/iL0 (sóng H1):
RR(TP@iH0) > InpMssMinRR ⇒ TP @ iH0 - InpMssTpSpreadMult × spread (swing target)
else ⇒ TP = entry ± risk × InpMssMinRR (= 2R fixed)
partialTriggerPrice = swingTp (nếu TP gồng xa hơn swing) ⇒ partial 50% + SL→BE
CHECK CUỐI (v1.184): |entry - market| ≤ InpMssMaxLimitDistAtrMult × ATR(FvgTf)
Quá xa ⇒ skip + mark FVG used + reset state
```
Place limit (`IctMssEntry_PlaceLimit`):
- BUY_LIMIT (BULL) / SELL_LIMIT (BEAR), magic = `InpMssMagic`
- Lưu `pendingTicket`, `pendingPlacedTime`, `pendingEntry/Sl/Tp`, `partialTriggerPrice`
Per-tick checks (xem `IctMssEntry_Update`):
```
IctMssEntry_CheckEodCancel() ── cancel cuối phiên Mỹ
IctMssEntry_CheckPendingTimeout() ── cancel sau N giờ (default 1h)
IctMssEntry_CheckTpReachedBeforeFill() ── cancel nếu giá đã chạm TP (v1.185)
IctMssEntry_CheckStaleLimit() ── cancel nếu rời xa giá > N×ATR (v1.184)
IctMssEntry_CheckBreakevenAtRR() ── dời SL→entry @ N×R (default off)
IctMssEntry_CheckPartialClose() ── partial 50% @ swingTp + SL→BE
```
### D. FVG fresh touch (`MssSetup.mqh`, v1.182)
Touch hợp lệ khi xảy ra **sau** cutoff (latest của):
1. `g_ictMssAfterCloseGuard` (sau TP/SL/manual close / timeout / stale limit)
2. `g_ictBiasIntoBullTime` (nếu bias hiện tại = BULL) hoặc `g_ictBiasIntoBearTime` (nếu BEAR)
```
IctMss_GetFvgTouchTime(sym, h1):
cutoff = IctMss_GetFvgTouchCutoff()
t = h1.firstTouchTime (nếu > cutoff) || 0
tM5 = IctMss_FirstM5TouchInFvg(sym, h1, since=cutoff)
t = min(t, tM5) (lấy sớm nhất sau cutoff)
nếu vẫn 0 && IctMss_LivePriceTouchesFvg(sym, h1) && curBar ≥ cutoff
t = curBar
return t
IctMss_HasFreshFvgTouch(sym, h1):
tTouch = IctMss_GetFvgTouchTime(sym, h1)
nếu tTouch ≤ 0 ⇒ false
requiredPct = IctFvg_RequiredTouchFillPct(h1)
= Pure (25%) nếu FVG hoàn toàn trong vùng PD đúng chiều / lớn / PD off
= Mixed (50%) nếu straddle equilibrium
return h1.maxFillRatio × 100 ≥ requiredPct
```
### E. POI filtering (`Fvg.mqh`)
```
IctFvg_IsEntryRepPd(zone, bias):
nếu !InpFvgPdEnabled ⇒ true (toàn bộ FVG qualify) [v1.179]
else: (v1.187: bỏ large-FVG bypass — áp PD cho TẤT CẢ FVG)
BEAR ⇒ overlap với Premium ≥ InpFvgPdMinOverlapPct (0=any overlap)
BULL ⇒ overlap với Discount tương tự
```
### F. State reset triggers (giai đoạn sau-trade / failsafe)
| Trigger | Hành động | Function |
|---|---|---|
| TP/SL/manual close | mark M5 used + reset + set guard | `IctMss_OnPositionClosed` |
| EOD (cuối phiên Mỹ) | cancel pending + reset | `IctMss_OnEodCancel` |
| Pending timeout (1h) | cancel + mark used + reset + guard | `IctMss_OnPendingTimeout` |
| Stale limit (> N×ATR) | cancel + mark used + reset + guard | `IctMssEntry_CheckStaleLimit` |
| Bias xoay side | cutoff áp dụng cho touch tự động | `IctDailyBias_TrackTransition` |
| H1 body đóng xuyên FVG | FVG → Used, reset | `IctMss_FailFvgH1Body` |
| FVG POI → Used giữa setup | reset | trong `IctMss_Update` |
### G. Tầng gating 2-layer
| Layer | Check | File |
|---|---|---|
| **Pipeline gate** | `g_ictDailyBias.bias != NONE` (Bias rõ) | `IctMss_Update`, `IctLowTfTrend_Update` |
| **Entry gate** | `intradayBlock = InpMssRequireIntradayAligned && !isAllowTrade` | `IctMssEntry_Update` |
Mặc định v1.181: cả 2 layer đều **cho phép** khi Bias rõ, không yêu cầu intraday align.
---
---
@@ -463,16 +642,56 @@ Màu chữ: **Up** = xanh lá, **Down** = đỏ cam, **Range** = vàng, **None**
## Mô tả từng file
### Foundation (types + utils + io)
| File | Trách nhiệm | Globals owned |
|------|-------------|---------------|
| `Types.mqh` | Enums (`ENUM_ICT_BIAS/STRUCT/MS_EVENT/TREND/FVG_*/MSS_PHASE`), structs (`IctSwingPoint`, `IctSwingSet`, `IctDailyBiasState`, `IctIntradayState`, `IctFvgZone`, `IctMssState`, `IctEaState`, `IctLowTfState`) | — |
| `Config.mqh` | Tất cả `input` params (~50 inputs gom 8 groups: Bias / Intraday / Test / Display / Confirm / FVG / MSS / Debug) | — |
| `Journal.mqh` | `IctMss_JournalPipeline`, `IctMss_JournalEntryBlock`, `IctMss_JournalReset` — gated bởi `InpOnlyStatsMode` | `g_ictMssLastJournalReason` |
| `Swing.mqh` | Pivot detection (5-bar fractal), `IctBuildBiasSwingSet`, `IctBuildIntradaySwingSet`, `IctBuildConfirmSwingSet`, fib validate | — |
| `StructureCore.mqh` | `IctBodyBreakAbove/Below`, `IctDetectStructureEvent` (BOS/CHoCH detect trên Key H0/L0) | — |
| `StructureTrend.mqh` | `IctResolveTrend` (rõ HH-HL / LH-LL vs sớm sau CHoCH) — chia sẻ cho cả Daily + Intraday | — |
### Bias & Trend (3 tầng TF)
| File | Trách nhiệm | Globals owned |
|------|-------------|---------------|
| `DailyBias.mqh` | D1 bias resolver theo thứ tự ưu tiên D1/D2 → structural → HTF fallback. Track bias transition (v1.182) | `g_ictDailyBias`, `g_ictDailyCtx`, `g_ictBiasIntoBullTime`, `g_ictBiasIntoBearTime` |
| `IntradayStructure.mqh` | H1 trend (HH-HL / LH-LL với 2 đỉnh + 2 đáy gần nhất trong `InpIntradayRecentBars`), CHoCH ưu tiên trước, `isAllowTrade` (`Bias align Intraday`) | `g_ictIntraday` |
### Low TF (FVG + Confirm)
| File | Trách nhiệm | Globals owned |
|------|-------------|---------------|
| `Fvg.mqh` | Detect FVG 3 nến A-B-C, fill ratio, PD zone từ pivot swing-1/swing-2, ATR helper | `g_ictFvgZones[]`, `g_ictFvgCount` |
| `ConfirmFvg.mqh` | M5 (Confirm TF) FVG pool — scan sau CHoCH | `g_ictConfirmFvgZones[]`, `g_ictConfirmFvgCount` |
| `LowTfApi.mqh` | Helpers truy cập `g_ictLowTf`, FVG side from bias, market side text | `g_ictLowTf` |
| `LowTfTrend.mqh` | Orchestrator: H1 nến mới ⇒ scan FVG, gọi MSS pipeline, refresh tick (PD/fill update) | — (sử dụng `g_ictLowTf`) |
### MSS Pipeline (setup → entry)
| File | Trách nhiệm | Globals owned |
|------|-------------|---------------|
| `MssSetup.mqh` | Phase machine IDLE→H1_TOUCH→CHOCH→M5_FVG→ENTRY_FILL→READY, fresh touch cutoff (v1.182), structural keylv lock (v1.171), MSS lock + invalidation | `g_ictMssAfterCloseGuard` |
| `MssEntry.mqh` | Order management: ComputeLevels (2-candidate min-risk + cap distance), PlaceLimit, partial close, BE@N×R, stale limit, EOD/timeout cancel, OnPositionClosed | `g_ictMssTrade` (CTrade) |
### State + UI
| File | Trách nhiệm | Globals owned |
|------|-------------|---------------|
| `EaState.mqh` | Map `mss.phase` + flags ⇒ EA STATE code (STOP_*/SETUP_*/TRADE_*). Cấp tiêu đề + chi tiết cho panel | `g_ictEaState` |
| `Panel.mqh` | Vẽ panel 8-10 dòng góc trên trái: state, bias, intraday, AllowEntry, MSS reason, Stats line | — |
| `Draw.mqh` | Label swing trên chart (bH0bL1, iH0iL1, H0L1 confirm) — auto theo TF chart | — |
| `FvgDraw.mqh` | Render FVG + PD top-N gần nhất + price-near (v1.180) — bỏ vẽ FVG xa giá để chart không lag | — |
| `MssDraw.mqh` | Vẽ MSS objects: H1 touch line, retest label, live L0/H0 (dotted gold), MSS↑/↓ confirmed (solid), entry/SL/TP lines | — |
| `Stats.mqh` | Quét HistoryDeals theo magic, đếm TP/SL/total/WR/sumR/netProfit (chỉ TP/SL — v1.183) | `g_ictMssStats`, `g_ictMssStatsLastScan` |
### EA root
| File | Trách nhiệm |
|------|-------------|
| `Types.mqh` | `ENUM_ICT_BIAS`, `ENUM_ICT_STRUCT`, `ENUM_ICT_MS_EVENT`, `IctSwingSet`, `IctDailyBiasState` |
| `Config.mqh` | Input: TF, swing range, lookback, Fib, debug |
| `Swing.mqh` | Pivot detection, build swing set, classify HH-HL/LH-LL, key level, Fib validate |
| `StructureCore.mqh` | `IctBodyBreak*`, `IctDetectStructureEvent` |
| `DailyBias.mqh` | `IctUpdateHTFBias`, veto, `g_ictDailyBias` |
| `IntradayStructure.mqh` | `IctIntraday_Update`, trend, `isAllowTrade`, `g_ictIntraday` |
| `Panel.mqh` | Bias + Intraday + IsAllowTrade trên chart |
| `ICT_2026.mq5` | Init/Update Daily + Intraday → panel |
| `Experts/ICT_2026.mq5` | `OnInit` (gọi init từng module), `OnTick` (orchestrator), `OnTradeTransaction` (detect close → call `IctMss_OnPositionClosed`), `OnDeinit` |
---
@@ -566,6 +785,217 @@ ENUM_ICT_BIAS ICT2026_GetDailyBias();
## Changelog
### v1.187 — Bỏ large-FVG bypass: áp PD cho TẤT CẢ FVG
- **Lý do**: feature "dấu chân cá mập" (v1.178) cho FVG lớn vào MSS bất kể Premium/Discount → nhiều setup vào ngược vùng (Premium khi bull, Discount khi bear) → kết quả backtest kém.
- **Thay đổi**:
- **Xoá inputs** khỏi `Config.mqh`: `InpFvgLargeBypassPd`, `InpFvgLargeMinAtrMult`.
- **Xoá helper** `IctFvg_IsLargeFvg(zone, sym)` trong `Fvg.mqh`.
- **Bỏ bypass call** trong `IctFvg_IsEntryRepPd` (mọi FVG phải qua check overlap ≥ `InpFvgPdMinOverlapPct`).
- **Bỏ bypass call** trong `IctFvg_RequiredTouchFillPct` (mọi FVG phải dùng threshold theo overlap thực tế: 25% Pure / 50% Mixed).
- **Giữ lại**:
- Helper `IctFvg_AtrFvgTf(sym)` — vẫn được `MssEntry.mqh` dùng cho stale-limit cap (v1.184) và `FvgDraw.mqh` dùng cho rendering.
- Toggle global `InpFvgPdEnabled` — vẫn dùng được để tắt toàn bộ PD filter (cho A/B test).
- **Hệ quả**: ít POI hơn (FVG lớn nằm sai vùng giờ bị loại), nhưng quality cao hơn (mọi entry đều ở vùng PD đúng chiều bias). Khôi phục hành vi trước v1.178.
### v1.186 — Siết chặt body-break: thân nến đóng vượt level ≥ N × spread
- **Vấn đề**: nến đóng "sát mép" key level/swing (close vượt 1 point) vẫn được tính là body-break ⇒ BOS/CHoCH/MSS-confirm + H1 invalidate kích hoạt nhầm trên nhiễu giá / spike trong spread → setup hủy oan hoặc MSS lock sai keyLv.
- **Input mới** (`Config.mqh`):
- `InpBodyBreakSpreadMult = 10` — body close phải vượt level ≥ N × spread (AskBid hiện tại). Fallback `_Point` khi spread = 0 (thị trường đóng cửa).
- **Logic** (`StructureCore.mqh`):
- Helper mới `IctBodyBreakBuffer(sym) = MathMax(_Point, max(0, AskBid) × InpBodyBreakSpreadMult)`.
- `IctBodyBreakAbove(level)``BodyTop > level + buffer`.
- `IctBodyBreakBelow(level)``BodyBottom < level buffer`.
- **Áp dụng** (mọi callsite dùng `IctBodyBreakAbove/Below`):
- BOS/CHoCH detect daily/intraday (`IctDetectStructureEvent`, `IctDetectTrendSwingEvent`).
- MSS confirm (`IctMss_FindMssBreakBar`, `IctMss_TryLockMss` shift=1).
- H1 invalidation (`IctMss_H1BodyInvalidatedSetup`) cũng dùng `IctBodyBreakBuffer` thay vì `_Point`.
- **Hệ quả**: ít event giả; cấu trúc chỉ flip khi có close-through "có ý nghĩa" (≥ 10 spread, ~510 pip tuỳ symbol). Tránh churn keyLv khi giá lưỡng lự quanh đỉnh/đáy.
### v1.185 — Cancel limit khi giá đã chạm TP trước khi khớp
- **Vấn đề**: setup MSS bull/bear đúng, limit đặt tại M5 FVG / MSS keyLV, nhưng giá chạy thẳng đến TP target (iL0/iH0) mà KHÔNG hồi lại entry → khi giá đảo chiều quay về khớp limit thì "TP move" đã hết, lệnh chạy ngược → SL.
- **Input mới** (`Config.mqh`):
- `InpMssCancelLimitWhenTpReached = true` — bật/tắt feature.
- **Logic** (`MssEntry.mqh``IctMssEntry_CheckTpReachedBeforeFill`):
- Gọi mỗi tick trong `IctMssEntry_Update`, ngay sau `CheckPendingTimeout`, trước `CheckStaleLimit`.
- Có pending `BUY_LIMIT`/`SELL_LIMIT` + TP > 0 → so sánh:
- **BUY**: `Bid ≥ TP Point` ⇒ giá đã chạm TP target
- **SELL**: `Ask ≤ TP + Point` ⇒ giá đã chạm TP target
- Nếu match: `OrderDelete(pendingTicket)` + `MarkM5FvgUsed` + `IctMss_ResetState` + set `g_ictMssAfterCloseGuard` → quay về `WAIT_FVG_TOUCH`.
- **Hệ quả**: không bao giờ khớp lệnh "muộn" sau khi target đã được giá quét. Setup này coi như miss, EA tìm POI mới.
- **So sánh stale-limit (v1.184)**: stale-limit cancel khi giá CHẠY XA entry (vượt N×ATR theo hướng cùng phía); TP-reached cancel khi giá đã ĐẾN TP (vượt qua entry rồi qua TP). Hai cơ chế bổ sung cho nhau.
### v1.181 — Mặc định KHÔNG yêu cầu Intraday cùng chiều Bias
- **Vấn đề**: nhiều setup giá đã chạy tới TP rồi mới khớp limit (vì Intraday chuyển sang thuận Bias muộn) → giá quay lại → SL
- **Lý do**: v1.169-v1.174 yêu cầu Intraday cùng chiều Bias mới entry, dẫn đến miss setup khi Intraday lag
- **Fix**: thêm toggle `InpMssRequireIntradayAligned = false` (mặc định **tắt**)
- **Tắt** (mặc định mới): MSS + entry chạy thuần theo Daily Bias, **không** quan tâm Intraday trend. Pipeline lock MSS + đặt limit ngay khi MSS valid
- **Bật**: behavior cũ — chặn entry + veto MSS lock khi Intraday ngược Bias
- Thay đổi cụ thể:
- `MssSetup.mqh` (v1.174 veto): wrap với `if(InpMssRequireIntradayAligned && ...)`
- `MssEntry.mqh` (gate `isAllowTrade`): thay bằng `intradayBlock = (InpMssRequireIntradayAligned && !isAllowTrade)`
- Trade-off:
- Tắt: nhiều entry hơn, ít miss setup, nhưng có thể vào lệnh khi Intraday đang ngược (phụ thuộc vào MSS quality)
- Bật: chất lượng entry cao hơn (đồng thuận TF), nhưng dễ bị "lag entry"
- Intraday vẫn được tính + hiển thị trên panel — chỉ KHÔNG gate entry nữa (mặc định)
### v1.180 — Tối ưu rendering: top-N FVG/PD + price-near, bỏ qua các FVG xa
- **Vấn đề**: mỗi tick `IctFvgDraw_Render` `DeleteAll` + redraw toàn bộ FVG (24 H1 + N M5), mỗi FVG kèm 3 PD object → ~100+ object operation/tick → chart nặng
- **Fix**: chỉ vẽ tập hợp tối thiểu cần thiết
- **Top-N USED gần nhất** (mặc định 3) — sort theo `createdTime` desc
- **Top-N AVAILABLE gần nhất** (mặc định 3)
- **FVG price-near**: giá hiện tại nằm trong `[lower N×ATR, upper + N×ATR]` (mặc định 2×ATR)
- **M5 FVG đang được dùng cho MSS** luôn ép vẽ (ngay cả khi không nằm trong top/near)
- Inputs mới:
- `InpDrawMaxRecentPerState = 3` — số FVG/PD gần nhất mỗi state (Used/Available)
- `InpDrawNearAtrMult = 2.0` — bán kính "near price" theo ATR(InpFvgTf) (0=tắt)
- Helpers mới trong `FvgDraw.mqh`:
- `IctFvgDraw_SortIdxByTimeDesc(idx[], n, zones[])` — insertion sort indices
- `IctFvgDraw_BuildVisibleMask(zones[], count, sym, &visible[])` — build mask vẽ
- Áp dụng cho cả H1 FVG (kèm PD) và M5 FVG (ConfirmFvg)
- PD array tự động đi theo FVG → giảm tương ứng
- Không ảnh hưởng logic pipeline (chỉ thay đổi rendering)
### v1.179 — Toggle global bật/tắt PD filter (cho A/B test)
- Input mới: `InpFvgPdEnabled = true` — bật/tắt điều kiện Premium/Discount toàn cục
- Khi `false`: mọi FVG đều qualify làm POI (chỉ cần có Bias rõ ràng để chọn side)
- `IctFvg_IsEntryRepPd`: return true ngay
- `IctFvg_RequiredTouchFillPct`: trả về `InpFvgTouchFillPure` (25%) thay vì 50% mixed
- Mặc định `true` ⇒ giữ hành vi cũ
- Mục đích: A/B test "số lượng vs chất lượng":
- `true` = ít POI hơn nhưng chất lượng cao (sai vùng PD bị loại)
- `false` = nhiều POI hơn nhưng có thể trade trong vùng counter (Discount khi BEAR / Premium khi BULL)
- Tương tác:
- Toggle global `InpFvgPdEnabled=true` ⇒ áp PD cho TẤT CẢ FVG (không còn ngoại lệ kể từ v1.187 — large-FVG bypass đã bị bỏ)
- `InpFvgPdEnabled=false` ⇒ bỏ qua PD hoàn toàn cho mọi FVG
### v1.178 — (REMOVED in v1.187) FVG "dấu chân cá mập" bypass PD filter
- ~~FVG height ≥ N × ATR ⇒ bypass PD filter~~ — **đã bỏ** ở v1.187 (xem changelog v1.187).
- Inputs `InpFvgLargeBypassPd``InpFvgLargeMinAtrMult` đã được xoá khỏi `Config.mqh`.
- Helper `IctFvg_IsLargeFvg(zone, sym)` đã bị xoá khỏi `Fvg.mqh`.
- `IctFvg_AtrFvgTf(sym)` **vẫn được giữ** vì còn được dùng bởi `MssEntry.mqh` (stale-limit cap v1.184) và `FvgDraw.mqh`.
### v1.177 — Tách toggle bật/tắt BE@RR (mặc định tắt)
- Input mới: `InpMssBeEnabled = false` — bật/tắt độc lập với ngưỡng RR (mặc định **tắt**)
- `InpMssBeAtRR = 2.0` — vẫn dùng để cấu hình ngưỡng khi `InpMssBeEnabled=true`
- `IctMssEntry_CheckBreakevenAtRR`: thêm `if(!InpMssBeEnabled) return;` ở đầu
- Lý do: tách boolean toggle khỏi giá trị số để config rõ ràng hơn, dễ A/B test BE on/off mà không phải nhớ giá trị cũ
### v1.176 — Dời SL về entry khi đạt 2R (Breakeven sớm)
- Input mới: `InpMssBeAtRR = 2.0` — dời SL về entry khi giá đi được N×R (0=tắt)
- Function mới: `IctMssEntry_CheckBreakevenAtRR(sym)` — check mỗi tick
- State mới: `IctMssState.beMovedDone` (bool) — đánh dấu đã dời BE, tránh dời lặp
- Trigger: `price ≥ entry + N×risk` (BUY) hoặc `price ≤ entry N×risk` (SELL)
- `risk = |pendingEntry pendingSl|` (từ thời điểm đặt limit)
- Hành vi: `PositionModify(ticket, entry, curTp)` — chỉ dời SL, TP giữ nguyên
- Skip cases:
- `InpMssBeAtRR ≤ 0`
- `beMovedDone` đã true
- `partialCloseDone` đã true (partial close ở swing đã set BE)
- SL hiện tại đã ≥ entry (cho BUY) hoặc ≤ entry (cho SELL) — chỉ set flag
- Tương tác với partial close (v1.168):
- Nếu **2R < swing target**: BE@2R chạy trước, dời SL→entry. Khi giá tiếp tục lên swing → partial close vẫn 50% (PositionModify SL=entry là no-op, không lỗi)
- Nếu **2R ≥ swing target**: partial close chạy trước ở swing (50% + SL→BE), khi giá lên 2R thì BE check skip vì `partialCloseDone=true`
### v1.175 — Bias D1/D2 ưu tiên trên structural (thêm sweep liquidity)
- **Thay đổi**: thứ tự ưu tiên xác định bias đảo lại
- **Trước**: structural HH-HL/LH-LL trước → HTF (UP/DOWN/SIDEWAY) fallback
- **Sau**: D1/D2 pattern trước → structural HH-HL/LH-LL fallback → HTF fallback
- **D1/D2 pattern (4 cases)**:
1. `D[1].close > D[2].high`**BULL** (Breakout)
2. `D[1].close < D[2].low`**BEAR** (Breakdown)
3. `D[1].high > D[2].high & close < D[2].high & close > D[2].low`**BEAR** (sweep bear liq — false breakout)
4. `D[1].low < D[2].low & close > D[2].low & close < D[2].high`**BULL** (sweep bull liq — false breakdown)
- **Tie-break**: D1 sweep cả 2 phía + close kẹt trong range D2 ⇒ không xác định ⇒ rơi xuống structural
- Function mới: `IctDailyBias_ResolveD1D2(sym, tf, &biasOut, &reasonOut)` trong `DailyBias.mqh`
- D1/D2 bias luôn `biasPhase = CLEAR` (không "sớm")
- DisplayReason hiển thị trực tiếp pattern match (vd: `"D[1].high > D[2].high & close < D[2].high → sweep bear liq → BEAR"`)
- Lý do: liquidity sweep + close-back là tín hiệu reversal mạnh, structural HH-HL/LH-LL có thể vẫn chưa "xoay" kịp ⇒ D1/D2 phản ánh "bias hôm nay" chuẩn hơn
### v1.174 — Veto MSS khi Intraday ngược Bias tại thời điểm lock
- **Bug**: MSS lock khi Intraday=BEAR / Bias=BULL → keylv ở đáy xa. Khi Intraday đảo về BULL, AllowTrade=true → bot đặt limit ở keylv cũ (xa):
- Giá quay lại khớp = thường hết trend → SL
- Giá tiếp tục đi = không khớp → khoá cơ hội tới khi timeout (1h)
- **Fix**: Ngay sau `IctMss_TryLockMss` thành công, check `IctIntraday_TrendAlignsWithBias(bias, intraday)`:
- Nếu **ngược**`MarkM5FvgUsed` + `IctMss_ResetState` ⇒ về `WAIT_FVG_TOUCH`
- Không transition `phase = ICT_MSS_CHOCH`, không quét M5 FVG
- DisplayReason: `"MSS↑/↓ khớp khi Intraday=<x> ngược Bias — bỏ POI, chờ POI mới"`
- Khác với v1.170 (decouple pipeline-AllowTrade): pipeline vẫn chạy phát hiện MSS, **chỉ veto tại thời điểm lock** nếu intraday lệch hướng — không để MSS dangling chờ AllowTrade
### v1.173 — Timeout pending limit 4h → 1h
- `Config.mqh`: `InpMssPendingExpireHours` mặc định `4 → 1`
- Logic huỷ pending unchanged (vẫn dùng `IctMssEntry_CheckPendingTimeout` ở mỗi tick)
- Sau 1h limit chưa khớp ⇒ `OrderDelete` + `MarkM5FvgUsed` + `IctMss_ResetState` → quay về `WAIT_FVG_TOUCH`
### v1.172 — Guard tTouch ≤ 0 trong UpdateLiveM5Swings
- **Bug v1.171**: filter `if(highs[i].time < tTouch) continue;` không có tác dụng khi `tTouch = 0` (mọi `time >= 0`) ⇒ liveH0/L0 lấy cả pivot **TRƯỚC** thời điểm chạm FVG ⇒ MSS keylv sai
- **Fix**: thêm guard `if(tTouch <= 0) return false;` ở đầu `IctMss_UpdateLiveM5Swings`
- Trường hợp đi vào guard: `h1TouchTime` chưa được set xong (race / GetFvgTouchTime trả 0) — pipeline sẽ hiển thị "M5 trong FVG — chờ pivot L0/H0" thay vì compute sai
- Đảm bảo: `liveH0Time` (BULL) & `liveL0Time` (BEAR) luôn `>= tTouch`
### v1.171 — Lock keylv MSS (chống "H0 nhảy")
- **Vấn đề**: `liveL0/H0` dùng "pivot mới nhất" (`IctBuildConfirmSwingSet`) → mỗi nến mới có swing mới là keylv MSS đổi → label "L0/H0" nhảy liên tục, MSS chạy lệch theo đỉnh/đáy mới hình thành SAU extreme pullback
- **Fix**: `IctMss_UpdateLiveM5Swings` chuyển sang định nghĩa **structural keylv**:
- **BEAR bias** (pullback UP):
- `liveH0` = đỉnh CAO NHẤT sau touch (extreme đỉnh pullback)
- `liveL0` = đáy **gần nhất theo time TRƯỚC liveH0** = *"đáy tạo ra đỉnh cao nhất"* — keylv MSS↓
- `liveH1` = đỉnh gần nhất TRƯỚC `liveL0`
- `liveL1` = đáy gần nhất TRƯỚC `liveH1`
- **BULL bias** (pullback DOWN):
- `liveL0` = đáy THẤP NHẤT sau touch (extreme đáy pullback)
- `liveH0` = đỉnh **gần nhất theo time TRƯỚC liveL0** = *"đỉnh tạo ra đáy thấp nhất"* — keylv MSS↑
- `liveL1` = đáy gần nhất TRƯỚC `liveH0`
- `liveH1` = đỉnh gần nhất TRƯỚC `liveL1`
- **Hệ quả "lock" tự nhiên**: pivot mới hình thành SAU extreme KHÔNG đổi keylv; chỉ khi pullback thực sự tạo extreme mới (BEAR: high cao hơn; BULL: low thấp hơn) thì keylv mới recompute
- **Không đổi**: validation cấu trúc đủ 2 đỉnh/đáy SAU touch vẫn giữ (v1.165), SL stack max(H0,H1) / min(L0,L1) vẫn giữ
- **UI**: label "L0 live" / "H0 live" (gold dotted) bây giờ ổn định, chỉ dịch khi pullback mở rộng
### v1.184 — Cap khoảng cách limit-giá (tránh limit chết)
- **Vấn đề**: MSS confirm muộn khi Intraday đảo chiều mạnh → entry tại MSS keyLV / M5 FVG nằm xa giá hiện tại → limit "chết", không bao giờ khớp; hết phiên hoặc timeout 1h mới hủy → lỡ cơ hội mới.
- **Inputs mới** (`Config.mqh`):
- `InpMssMaxLimitDistAtrMult = 3.0` — cap = N × ATR(`InpFvgTf`). `>cap` ⇒ skip / cancel. `0` = tắt.
- `InpMssCancelStaleLimit = true` — cancel pending + mark FVG used + reset state khi vượt cap (chọn POI khác).
- **Logic**:
- `IctMssEntry_IsLimitTooFar(sym, entry, isBuy, …)` so sánh `|Ask entry|` (BUY) / `|entry Bid|` (SELL) với cap.
- `ComputeLevels` check sau khi pick entry → fail với reason `"Entry xa giá X pts > Y pts (N×ATR)"`.
- `IctMssEntry_Update`: nếu fail vì lý do trên + `InpMssCancelStaleLimit` ⇒ cancel pending + `MarkM5FvgUsed` + `IctMss_ResetState` + set `g_ictMssAfterCloseGuard` → quay về `WAIT_FVG_TOUCH`.
- `IctMssEntry_CheckStaleLimit(sym)` chạy mỗi tick: pending order đã đặt nhưng giá rời xa → cancel + reset (cùng flow).
- **Hệ quả**: nếu MSS confirm muộn (giá đã chạy xa khỏi keyLV/FVG), EA bỏ qua POI hiện tại + chọn POI/touch mới ngay thay vì đặt limit chết hàng giờ.
### v1.183 — Stats chỉ đếm TP/SL, bỏ partial/manual close khỏi total
- `Stats.mqh`: pass-2 skip non-TP/SL deals khỏi `total`, `tpCount`, `slCount`,
`sumR`, `winCount`, `lossCount`. Chỉ giữ `otherCount` để log.
- `netProfit` vẫn cộng dồn TẤT CẢ deals (partial close P&L được phản ánh đúng).
- `AvgR` = `sumR / (TP+SL)` thay vì `/ total` (đồng nhất với cách đếm).
- Panel: `IctMssStats_LineCounts` bỏ phần `khác X` → "Stats: N lệnh | TP X | SL Y".
- **Lý do**: partial close (50% ở swing target) tạo deal `DEAL_REASON_EXPERT` → trước
đây tính vào "khác" nhưng không phản ánh kết quả lệnh. Sau fix: 1 setup = 1 entry
→ 1 TP/SL outcome.
### v1.182 — Fix: chặn FVG touch xảy ra TRƯỚC khi Bias chuyển vào side
- **Bug**: Bias UP → giá đi qua bear FVG (EA bỏ qua vì bias ngược) → bias xoay DOWN → EA pick up `firstTouchTime` cũ (trước khi xoay) → đặt limit ngay, dù chưa có touch fresh sau khi bias đổi side
- **Fix**: thêm `g_ictBiasIntoBullTime` / `g_ictBiasIntoBearTime` (`DailyBias.mqh`) lưu thời điểm Bias chuyển vào BULL/BEAR. `IctDailyBias_TrackTransition()` chạy mỗi `IctDailyBias_Update` để cập nhật.
- **Cutoff bias-aware** (`IctMss_GetFvgTouchCutoff`): touch hợp lệ phải xảy ra SAU `max(BiasInto<side>Time, AfterCloseGuard)`
- `IctMss_GetFvgTouchTime` reject `firstTouchTime` cũ → scan M5 bars sau cutoff (`IctMss_FirstM5TouchInFvg` nhận thêm param `since`) → nếu vẫn 0 → check `LivePriceTouchesFvg` (curBar phải ≥ cutoff)
- **Hệ quả**: Sau Bias flip, FVG side mới chỉ hợp lệ khi có touch NEW (re-touch hoặc giá đang trong gap với current bar sau cutoff). Tránh entry từ context cũ.
- **Tích hợp**: `HasFreshFvgTouch` dùng `GetFvgTouchTime` (đã apply cutoff) → bỏ block check redundant; fill ratio vẫn check Pure 25% / Mixed 50%
### v1.142 — Fix Stats đếm thiếu lệnh
- Bug v1.141: `IctMssStats_ComputeR` gọi `HistorySelectByPosition` **bên trong vòng quét history toàn cục**
@@ -597,7 +1027,7 @@ ENUM_ICT_BIAS ICT2026_GetDailyBias();
### v1.160 — Pending timeout 4h → reset chờ FVG mới
- Input: `InpMssPendingExpireHours = 4` (mặc định, 0 = không timeout)
- Input: `InpMssPendingExpireHours = 1` (mặc định, 0 = không timeout)
- Tracked `pendingPlacedTime` trong `IctMssState`
- Mỗi tick (`IctMssEntry_CheckPendingTimeout`): nếu `TimeCurrent() - pendingPlacedTime ≥ N×3600s` hoặc order biến mất (broker expire) → `OrderDelete` + `MarkM5FvgUsed` + `IctMss_ResetState` + set `g_ictMssAfterCloseGuard`
- Hành vi giống TP/SL close: reset về `WAIT_FVG_TOUCH`, chỉ accept H1 FVG có touch sau timeout
+53 -17
View File
@@ -1,5 +1,34 @@
//+------------------------------------------------------------------+
//| Stats.mqh — thống kê lệnh MSS từ history (theo magic) |
//| Stats.mqh — thống kê lệnh MSS từ HistoryDeals (theo magic) |
//+------------------------------------------------------------------+
//| Quét HistoryDeals filter symbol + magic = InpMssMagic. Compute: |
//| total / tpCount / slCount / otherCount |
//| winCount / lossCount / sumR / netProfit |
//| |
//| v1.183 — chỉ TP/SL được đếm vào total/win/loss/sumR/tp/sl. Partial|
//| close / manual / EA-close → otherCount (chỉ log, không vào panel).|
//| netProfit cộng dồn TẤT CẢ deals (kể cả partial — P&L thực). |
//| AvgR = sumR / (tpCount + slCount) — đồng nhất với cách đếm. |
//| |
//| 2-pass scan (v1.142 fix): |
//| Pass 1: chỉ collect ticket + reason + net (global selection còn |
//| nguyên, không gọi HistorySelectByPosition) |
//| Pass 2: tính R cho từng ticket (mỗi call thay selection) |
//| |
//| R computation (ComputeR): |
//| R = (closePrice - entryPrice) / (entryPrice - slPrice) for BUY |
//| = (entryPrice - closePrice) / (slPrice - entryPrice) for SELL|
//| entryPrice + slPrice lấy từ DEAL_ENTRY_IN + ORDER_SL của |
//| position. |
//| |
//| Globals owned: |
//| g_ictMssStats — IctMssStats struct |
//| g_ictMssStatsLastScan — TS scan gần nhất (debounce) |
//| |
//| Public API: |
//| void IctMssStats_Recompute(sym, magic) |
//| double IctMssStats_WinratePct() / IctMssStats_AvgR() |
//| string IctMssStats_LineCounts() / IctMssStats_LinePerf() |
//+------------------------------------------------------------------+
#ifndef ICT2026_STATS_MQH
#define ICT2026_STATS_MQH
@@ -114,22 +143,29 @@ void IctMssStats_Recompute(const string sym, const ulong magic)
cached++;
}
// Pass 2: tính R cho từng deal (mỗi lần gọi sẽ thay selection — không ảnh hưởng vì đã cache xong).
// Pass 2: chỉ tính TP/SL vào stats. Partial close / manual close
// không phải kết quả lệnh ⇒ skip total/tp/sl/win/loss/sumR.
// netProfit vẫn cộng dồn TẤT CẢ deals để phản ánh đúng P&L thực tế.
for(int k = 0; k < cached; k++)
{
const double r = IctMssStats_ComputeR(outTickets[k]);
g_ictMssStats.total++;
g_ictMssStats.netProfit += nets[k];
g_ictMssStats.sumR += r;
const ENUM_DEAL_REASON rs = (ENUM_DEAL_REASON)reasons[k];
if(rs == DEAL_REASON_TP)
g_ictMssStats.tpCount++;
else if(rs == DEAL_REASON_SL)
g_ictMssStats.slCount++;
else
const bool isTp = (rs == DEAL_REASON_TP);
const bool isSl = (rs == DEAL_REASON_SL);
if(!isTp && !isSl)
{
g_ictMssStats.otherCount++;
continue;
}
const double r = IctMssStats_ComputeR(outTickets[k]);
g_ictMssStats.total++;
g_ictMssStats.sumR += r;
if(isTp)
g_ictMssStats.tpCount++;
else
g_ictMssStats.slCount++;
if(nets[k] > 0.0)
g_ictMssStats.winCount++;
@@ -140,7 +176,7 @@ void IctMssStats_Recompute(const string sym, const ulong magic)
g_ictMssStatsLastScan = TimeCurrent();
if(InpMssLogJournal)
PrintFormat("[ICT2026/Stats] Recompute %s magic=%I64u | total=%d TP=%d SL=%d khác=%d | net=%.2f sumR=%.2f",
PrintFormat("[ICT2026/Stats] Recompute %s magic=%I64u | total=%d TP=%d SL=%d (skip khác=%d) | net=%.2f sumR=%.2f",
sym, magic,
g_ictMssStats.total, g_ictMssStats.tpCount,
g_ictMssStats.slCount, g_ictMssStats.otherCount,
@@ -157,18 +193,18 @@ double IctMssStats_WinratePct()
double IctMssStats_AvgR()
{
if(g_ictMssStats.total <= 0)
const int decided = g_ictMssStats.tpCount + g_ictMssStats.slCount;
if(decided <= 0)
return 0.0;
return g_ictMssStats.sumR / (double)g_ictMssStats.total;
return g_ictMssStats.sumR / (double)decided;
}
string IctMssStats_LineCounts()
{
return StringFormat("Stats: %d lệnh | TP %d | SL %d | khác %d",
return StringFormat("Stats: %d lệnh | TP %d | SL %d",
g_ictMssStats.total,
g_ictMssStats.tpCount,
g_ictMssStats.slCount,
g_ictMssStats.otherCount);
g_ictMssStats.slCount);
}
string IctMssStats_LinePerf()
+48 -3
View File
@@ -1,5 +1,37 @@
//+------------------------------------------------------------------+
//| StructureCore.mqh — body break & BOS/CHoCH detect (dùng chung) |
//| StructureCore.mqh — body break primitives + BOS/CHoCH detector |
//+------------------------------------------------------------------+
//| Helpers cho cả Daily + Intraday — phá level dùng THÂN NẾN (body), |
//| không dùng wick. |
//| |
//| IctBodyTop / IctBodyBot — body top/bot của nến shift |
//| IctBodyBreakBuffer(sym) — N × spread (≥ _Point) |
//| IctBodyBreakAbove(level) — body close > level + buffer |
//| IctBodyBreakBelow(level) — body close < level buffer |
//| |
//| Buffer (v1.186): siết chặt — thân nến phải đóng vượt level ≥ |
//| InpBodyBreakSpreadMult × (AskBid) (mặc định 10 spread). |
//| Áp dụng cho mọi callsite: BOS/CHoCH (Daily, Intraday), MSS |
//| confirm, H1 FVG body-invalidate. |
//| |
//| BOS vs CHoCH (cùng rule, khác bản chất): |
//| BOS = phá Key tiếp diễn xu hướng |
//| CHoCH= phá Key NGƯỢC xu hướng (đảo chiều) |
//| |
//| Bear (LH-LL): Key1=H0, Key2=L0 |
//| - Phá H0 ⇒ CHoCH (chuyển sang Bull sớm) |
//| - Phá L0 ⇒ BOS |
//| Bull (HH-HL): Key1=L0, Key2=H0 |
//| - Phá L0 ⇒ CHoCH (chuyển sang Bear sớm) |
//| - Phá H0 ⇒ BOS |
//| |
//| Detection: trên bar đã đóng (shift=1) — KHÔNG dùng bar đang chạy. |
//| |
//| Public API: |
//| double IctBodyTop/Bot(sym, tf, shift) |
//| bool IctBodyBreakAbove/Below(sym, tf, shift, level) |
//| ENUM_ICT_MS_EVENT IctDetectStructureEvent(sym, tf, &set, |
//| structural, shift) |
//+------------------------------------------------------------------+
#ifndef ICT2026_STRUCTURECORE_MQH
#define ICT2026_STRUCTURECORE_MQH
@@ -16,16 +48,29 @@ double IctBodyBottom(const string sym, const ENUM_TIMEFRAMES tf, const int shift
return MathMin(iOpen(sym, tf, shift), iClose(sym, tf, shift));
}
// Buffer xác nhận phá level: N × spread hiện tại (fallback _Point khi spread = 0).
// Siết chặt body-break để loại các close-through "sát mép" do biến động nhỏ/spike.
// N = InpBodyBreakSpreadMult (mặc định 10).
double IctBodyBreakBuffer(const string sym)
{
const double sp = MathMax(0.0,
SymbolInfoDouble(sym, SYMBOL_ASK) -
SymbolInfoDouble(sym, SYMBOL_BID));
const double unit = (sp > 0.0) ? sp : _Point;
const double mult = MathMax(0.0, (double)InpBodyBreakSpreadMult);
return MathMax(_Point, unit * mult);
}
bool IctBodyBreakAbove(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const double level)
{
return IctBodyTop(sym, tf, shift) > level + _Point;
return IctBodyTop(sym, tf, shift) > level + IctBodyBreakBuffer(sym);
}
bool IctBodyBreakBelow(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const double level)
{
return IctBodyBottom(sym, tf, shift) < level - _Point;
return IctBodyBottom(sym, tf, shift) < level - IctBodyBreakBuffer(sym);
}
// BOS = Continue (Key LV2) | CHoCH = Reversal (Key LV1)
+24 -1
View File
@@ -1,5 +1,28 @@
//+------------------------------------------------------------------+
//| StructureTrend.mqh — trend rõ (HH-HL/LH-LL) vs sớm (sau CHoCH) |
//| StructureTrend.mqh — phân giải trend "" vs "sớm" |
//+------------------------------------------------------------------+
//| Module dùng chung cho cả Daily + Intraday — chia 2 phase: |
//| |
//| CLEAR ("rõ ràng") — HH-HL hoặc LH-LL pivot xác nhận |
//| ⇒ trend = UP / DOWN |
//| |
//| EARLY ("sớm") — sau CHoCH nhưng cấu trúc mới chưa rõ |
//| ⇒ trend = UP_EARLY / DOWN_EARLY (Bull/Bear sớm) |
//| giữ phase EARLY cho đến khi: |
//| - Pivot xác nhận HH-HL/LH-LL → CLEAR |
//| - CHoCH ngược → đổi phase EARLY ngược |
//| |
//| CHoCH ưu tiên trước pivot: nếu pivot vẫn LH-LL nhưng H[1] body |
//| phá H0 ⇒ trend = Bull sớm (không giữ Down). |
//| |
//| Mixed (không HH-HL không LH-LL) + không EARLY ⇒ NONE. |
//| |
//| Lưu ý "rõ" vs "sớm" do user tự đặt tên trong tiếng Việt — code |
//| dùng enum ENUM_ICT_TREND_PHASE = {CLEAR, EARLY}. |
//| |
//| Public API: |
//| bool IctResolveTrend(&ctx, &trendOut, &phaseOut, &eventOut) |
//| ctx = {sym, tf, swings, prevStructural, prevTrend, ...} |
//+------------------------------------------------------------------+
#ifndef ICT2026_STRUCTURETREND_MQH
#define ICT2026_STRUCTURETREND_MQH
+35 -1
View File
@@ -1,5 +1,39 @@
//+------------------------------------------------------------------+
//| Swing.mqh — pivot & swing set H0L1 (tham khảo HyperICT) |
//| Swing.mqh — pivot detection + swing set 4-point (H0/L0/H1/L1) |
//+------------------------------------------------------------------+
//| Foundation module — không phụ thuộc bất kỳ file nào khác ngoài |
//| Types.mqh. |
//| |
//| Pivot definition (N-bar fractal, N = swingRange): |
//| Swing High at shift k = High[k] > High[k±i] for i=1..N |
//| Swing Low at shift k = Low[k] < Low[k±i] for i=1..N |
//| |
//| Build swing set (4-point leg): |
//| IctBuildBiasSwingSet — H0/L0/H1/L1 ghép theo leg time |
//| IctBuildIntradaySwingSet — 2 đỉnh + 2 đáy gần nhất (không leg)|
//| IctBuildConfirmSwingSet — như intraday cho M5 |
//| |
//| Classification (IctClassifyStructure): |
//| H0 > H1 && L0 > L1 ⇒ HH-HL (bull) |
//| H0 < H1 && L0 < L1 ⇒ LH-LL (bear) |
//| else ⇒ MIXED |
//| |
//| Key level: |
//| Bull: keyLv1 = L0, keyLv2 = H0 |
//| Bear: keyLv1 = H0, keyLv2 = L0 |
//| |
//| Fib validation (tùy chọn): |
//| Sóng hồi H1→L0 ≥ InpFibMinRatio × sóng L1→H1 |
//| |
//| Public API: |
//| bool IctIsSwingHigh/Low(sym, tf, shift, range) |
//| bool IctBuildBiasSwingSet(sym, tf, &set, range, lookback) |
//| bool IctBuildIntradaySwingSet(sym, tf, &set, range, lookback, |
//| recentBars) |
//| bool IctBuildConfirmSwingSet(sym, tf, &set, range, lookback, |
//| recentBars) |
//| ENUM_ICT_STRUCT IctClassifyStructure(&set) |
//| bool IctValidateFib(&set, minRatio) |
//+------------------------------------------------------------------+
#ifndef ICT2026_SWING_MQH
#define ICT2026_SWING_MQH
+44 -1
View File
@@ -1,5 +1,46 @@
//+------------------------------------------------------------------+
//| Types.mqh — ICT 2026 data model |
//| Types.mqh — ICT 2026 data model (foundation layer) |
//+------------------------------------------------------------------+
//| Mục đích: |
//| File này CHỈ CHỨA enums + structs + một vài hằng số. KHÔNG có |
//| function logic. Bất kỳ file nào trong ICT2026 module đều có thể |
//| include trực tiếp file này mà không lo cycle dependency. |
//| |
//| Enums chính: |
//| ENUM_ICT_BIAS : BULL / BEAR / NONE / RANGE |
//| ENUM_ICT_STRUCT : HH_HL / LH_LL / MIXED / UNKNOWN |
//| ENUM_ICT_MS_EVENT : NONE / BOS / CHOCH |
//| ENUM_ICT_TREND : UP / DOWN / NONE (+ EARLY phase) |
//| ENUM_ICT_FVG_SIDE : BULL / BEAR / NONE |
//| ENUM_ICT_FVG_STATE : AVAILABLE / USED |
//| ENUM_ICT_MSS_PHASE : IDLE → H1_TOUCH → CHOCH → M5_FVG → |
//| ENTRY_FILL → READY |
//| ENUM_ICT_HTF_BIAS : UP / DOWN / SIDEWAY / NONE (PreviousDay)|
//| ENUM_ICT_TREND_PHASE : CLEAR (HH-HL/LH-LL rõ) / EARLY (sau |
//| CHoCH chưa rõ) |
//| |
//| Structs chính: |
//| IctSwingPoint : 1 pivot {price, time, shift} |
//| IctSwingSet : 4 pivot H0/L0/H1/L1 + flags isComplete |
//| IctTrendResolveCtx : input for IctResolveTrend |
//| IctDailyBiasState : bias, structural, lastEvent, pdh/pdl/eq, |
//| keyLv1/keyLv2, swings, reason, htf |
//| IctIntradayState : trend, isAllowTrade, swings, lastEvent |
//| IctFvgZone : side, state, lower/upper, createdTime, |
//| firstTouchTime, maxFillRatio, pdHigh/Low,|
//| pdEq, pdComplete, pdSwing1Set, id, ... |
//| IctMssState : phase, h1FvgId, h1TouchTime, h1WatchFvgId,|
//| chochLocked, chochBias, chochKeyLevel, |
//| chochKeyTime, chochTime, slSwingPrice, |
//| liveL0/H0/L1/H1 price+time, |
//| m5FvgId, m5FvgTime, pendingTicket, |
//| pendingEntry/Sl/Tp, pendingPlacedTime, |
//| partialTriggerPrice, partialCloseDone, |
//| beMovedDone, displayReason |
//| IctEaState : code (STATE enum), title, detail |
//| IctLowTfState : lastBarTime, lastConfirmBarTime, mss, |
//| activeCount, availableCount, |
//| confirmFvgCount, displayReason |
//+------------------------------------------------------------------+
#ifndef ICT2026_TYPES_MQH
#define ICT2026_TYPES_MQH
@@ -232,6 +273,7 @@ struct IctMssState
double pendingTp;
double partialTriggerPrice;
bool partialCloseDone;
bool beMovedDone;
double liveL0Price;
double liveH0Price;
datetime liveL0Time;
@@ -264,6 +306,7 @@ struct IctMssState
pendingTp = 0.0;
partialTriggerPrice = 0.0;
partialCloseDone = false;
beMovedDone = false;
liveL0Price = 0.0;
liveH0Price = 0.0;
liveL0Time = 0;