diff --git a/Experts/EA_ICT_CL.mq5 b/Experts/EA_ICT_CL.mq5 index 968d0ae..c4b2c9e 100644 --- a/Experts/EA_ICT_CL.mq5 +++ b/Experts/EA_ICT_CL.mq5 @@ -17,67 +17,67 @@ //| | //| Drawing: objects KHÔNG BAO GIỜ bị xóa, chỉ update | //+------------------------------------------------------------------+ -#property copyright "Bell's ICT EA" -#property version "4.30" +#property copyright "Bell's ICT EA" // Bản quyền EA +#property version "4.30" // Phiên bản -#define MAX_FVG_POOL 30 +#define MAX_FVG_POOL 30 // Số FVG tối đa trong pool //==================================================== // LAYER 1 – Config + Types (no dependencies) //==================================================== -#include "EA_ICT_CL/Config.mqh" -#include "EA_ICT_CL/State.mqh" +#include "EA_ICT_CL/Config.mqh" // Inputs và cấu hình +#include "EA_ICT_CL/State.mqh" // Enums, structs (EAState, BiasContext, FVGRecord...) //==================================================== // LAYER 2 – Utility modules (depend on types + inputs) //==================================================== -#include "EA_ICT_CL/Utils.mqh" -#include "EA_ICT_CL/Logging.mqh" -#include "EA_ICT_CL/Market.mqh" -#include "EA_ICT_CL/Indicators.mqh" -#include "EA_ICT_CL/Sessions.mqh" -#include "EA_ICT_CL/Filters.mqh" -#include "EA_ICT_CL/Risk.mqh" -#include "EA_ICT_CL/Orders.mqh" -#include "EA_ICT_CL/Swing.mqh" -#include "EA_ICT_CL/Trailing.mqh" +#include "EA_ICT_CL/Utils.mqh" // Clamp, RoundToStep, IsNewBar +#include "EA_ICT_CL/Logging.mqh" // LogPrint, LogPrintF +#include "EA_ICT_CL/Market.mqh" // GetBid, GetAsk, GetPoint, IsTradeAllowedNow +#include "EA_ICT_CL/Indicators.mqh" // CopyBufferSafe, CopyTimeSafe +#include "EA_ICT_CL/Sessions.mqh" // IsHourInRange, GetUTCHour +#include "EA_ICT_CL/Filters.mqh" // Filter_MaxSpreadPoints +#include "EA_ICT_CL/Risk.mqh" // NormalizeVolume, LotsFromRiskMoneyAndSLPoints +#include "EA_ICT_CL/Orders.mqh" // NormalizePrice, BuildOrderComment +#include "EA_ICT_CL/Swing.mqh" // MyBarShift, IsSwingHighAt, ScanSwingStructure, ResolveTrendFromSwings +#include "EA_ICT_CL/Trailing.mqh" // Placeholder trailing/BE //==================================================== // GLOBALS //==================================================== -EAState g_State = EA_IDLE; -BlockReason g_BlockReason = BLOCK_NONE; +EAState g_State = EA_IDLE; // Trạng thái EA hiện tại +BlockReason g_BlockReason = BLOCK_NONE; // Lý do chặn trade (session/daily loss/bias...) -BiasContext g_Bias; -TFTrendContext g_MiddleTrend; -TFTrendContext g_TriggerTrend; -DailyRiskContext g_DailyRisk; +BiasContext g_Bias; // D1 bias (UP/DOWN/SIDEWAY/NONE) +TFTrendContext g_MiddleTrend; // H1 swing + trend + MSS +TFTrendContext g_TriggerTrend; // M5 swing + trend +DailyRiskContext g_DailyRisk; // Balance đầu ngày, limit hit -FVGRecord g_FVGPool[MAX_FVG_POOL]; -int g_FVGCount = 0; -int g_NextFVGId = 0; -int g_ActiveFVGIdx = -1; -int g_TradeBarIndex = -1; +FVGRecord g_FVGPool[MAX_FVG_POOL]; // Pool các FVG đã quét +int g_FVGCount = 0; // Số FVG trong pool +int g_NextFVGId = 0; // ID gán cho FVG mới +int g_ActiveFVGIdx = -1; // Chỉ số FVG đang theo dõi (-1 = không có) +int g_TradeBarIndex = -1; // Bar index khi vào lệnh (dùng cho timeout...) -OrderPlan g_OrderPlan; -ulong g_PendingTicket = 0; +OrderPlan g_OrderPlan; // Kế hoạch lệnh (entry, SL, TP, lot) +ulong g_PendingTicket = 0; // Ticket lệnh pending đã gửi -const string PREFIX_SWING_MIDDLE = "SwingMiddle_"; // H1 swing arrows/labels -const string PREFIX_SWING_TRIGGER = "SwingTrigger_"; // M5 swing arrows/labels -const string PREFIX_MSS_MARKER = "MSSMarker_"; // MSS break markers -const string PREFIX_FVG_POOL = "FVGPool_"; // FVG rectangles -const string PREFIX_ORDER_VISUAL = "OrderVisual_"; // Entry/SL/TP visualization -const string PREFIX_DEBUG_PANEL = "DebugPanel_"; // Top-left info labels +const string PREFIX_SWING_MIDDLE = "SwingMiddle_"; // Tiền tố object H1 swing +const string PREFIX_SWING_TRIGGER = "SwingTrigger_"; // Tiền tố object M5 swing +const string PREFIX_MSS_MARKER = "MSSMarker_"; // Tiền tố marker MSS +const string PREFIX_FVG_POOL = "FVGPool_"; // Tiền tố hình FVG +const string PREFIX_ORDER_VISUAL = "OrderVisual_"; // Tiền tố vùng Entry/SL/TP +const string PREFIX_DEBUG_PANEL = "DebugPanel_"; // Tiền tố panel debug góc trái //==================================================== // LAYER 3 – Logic modules (depend on globals) //==================================================== -#include "EA_ICT_CL/Contexts.mqh" -#include "EA_ICT_CL/Guards.mqh" -#include "EA_ICT_CL/Signals_BOS_FVG_OB.mqh" -#include "EA_ICT_CL/Trade.mqh" -#include "EA_ICT_CL/StateMachine.mqh" -#include "EA_ICT_CL/Drawing.mqh" +#include "EA_ICT_CL/Contexts.mqh" // UpdateBiasContext, UpdateTFTrendContext, UpdateAllContexts +#include "EA_ICT_CL/Guards.mqh" // IsSessionAllowed, EvaluateGuards +#include "EA_ICT_CL/Signals_BOS_FVG_OB.mqh" // ScanAndRegisterFVGs, UpdateFVGStatuses, GetBestActiveFVGIdx +#include "EA_ICT_CL/Trade.mqh" // BuildOrderPlan, ExecuteLimitOrder +#include "EA_ICT_CL/StateMachine.mqh" // TransitionTo, RunStateMachine, OnStateIdle... +#include "EA_ICT_CL/Drawing.mqh" // DrawVisuals, DrawFVGPool, DrawContextDebug... //+------------------------------------------------------------------+ //| EA LIFECYCLE | @@ -85,38 +85,38 @@ const string PREFIX_DEBUG_PANEL = "DebugPanel_"; // Top-left info labels int OnInit() { - ZeroMemory(g_Bias); ZeroMemory(g_MiddleTrend); ZeroMemory(g_TriggerTrend); - ZeroMemory(g_DailyRisk); ZeroMemory(g_OrderPlan); - for (int i = 0; i < MAX_FVG_POOL; i++) ZeroMemory(g_FVGPool[i]); - g_FVGCount = 0; g_NextFVGId = 0; - g_ActiveFVGIdx = -1; g_TradeBarIndex = -1; g_PendingTicket = 0; - g_State = EA_IDLE; g_BlockReason = BLOCK_NONE; + ZeroMemory(g_Bias); ZeroMemory(g_MiddleTrend); ZeroMemory(g_TriggerTrend); // Xóa context + ZeroMemory(g_DailyRisk); ZeroMemory(g_OrderPlan); // Xóa risk + order plan + for (int i = 0; i < MAX_FVG_POOL; i++) ZeroMemory(g_FVGPool[i]); // Xóa toàn bộ pool FVG + g_FVGCount = 0; g_NextFVGId = 0; // Reset đếm FVG + g_ActiveFVGIdx = -1; g_TradeBarIndex = -1; g_PendingTicket = 0; // Không FVG active, không pending + g_State = EA_IDLE; g_BlockReason = BLOCK_NONE; // State = IDLE, không block - UpdateAllContexts(); - ScanAndRegisterFVGs(); - DrawVisuals(); + UpdateAllContexts(); // Cập nhật D1 bias, H1/M5 trend, daily risk + ScanAndRegisterFVGs(); // Quét H1 và đăng ký FVG vào pool + DrawVisuals(); // Vẽ swing, FVG, debug panel (nếu bật) PrintFormat("✅ ICT EA v4.3 | Bias=%s | H1=%s | M5=%s | Pool=%d", EnumToString(g_Bias.bias), EnumToString(g_MiddleTrend.trend), EnumToString(g_TriggerTrend.trend), g_FVGCount); - return INIT_SUCCEEDED; + return INIT_SUCCEEDED; // Khởi tạo thành công } void OnTick() { - UpdateAllContexts(); - if (EvaluateGuards()) - RunStateMachine(); + UpdateAllContexts(); // Mỗi tick: cập nhật bias, H1/M5 swing, daily risk, MSS (nếu WAIT_TRIGGER) + if (EvaluateGuards()) // Kiểm tra session, daily loss, bias, alignment + RunStateMachine(); // Chạy state machine (Idle/WaitTouch/WaitTrigger/InTrade) else if (g_State != EA_IDLE) - ResetToIdle(EnumToString(g_BlockReason)); - if (InpDebugDraw) DrawVisuals(); + ResetToIdle(EnumToString(g_BlockReason)); // Đang không IDLE mà fail guard → reset về IDLE + if (InpDebugDraw) DrawVisuals(); // Vẽ lại nếu bật debug draw } void OnDeinit(const int reason) { - ObjectsDeleteAll(0, PREFIX_SWING_MIDDLE); - ObjectsDeleteAll(0, PREFIX_SWING_TRIGGER); - ObjectsDeleteAll(0, PREFIX_DEBUG_PANEL); - ChartRedraw(0); - PrintFormat("ICT EA v4.3 deinit | reason=%d | pool=%d", reason, g_FVGCount); + ObjectsDeleteAll(0, PREFIX_SWING_MIDDLE); // Xóa toàn bộ object H1 swing + ObjectsDeleteAll(0, PREFIX_SWING_TRIGGER); // Xóa toàn bộ object M5 swing + ObjectsDeleteAll(0, PREFIX_DEBUG_PANEL); // Xóa panel debug (FVG/order objects giữ lại theo thiết kế) + ChartRedraw(0); // Vẽ lại chart + PrintFormat("ICT EA v4.3 deinit | reason=%d | pool=%d", reason, g_FVGCount); // Log thoát } diff --git a/Experts/EA_ICT_CL/Config.mqh b/Experts/EA_ICT_CL/Config.mqh index dfef400..b314037 100644 --- a/Experts/EA_ICT_CL/Config.mqh +++ b/Experts/EA_ICT_CL/Config.mqh @@ -1,40 +1,38 @@ #ifndef EA_ICT_CL__CONFIG_MQH -#define EA_ICT_CL__CONFIG_MQH +#define EA_ICT_CL__CONFIG_MQH // Tránh include trùng -// Module: Config -// This file now hosts EA inputs. Keep other types (enums/structs/globals) in the .mq5 -// until you intentionally migrate them to State/Config to avoid redefinition. +// Module: Config – toàn bộ input của EA //==================================================== // INPUTS //==================================================== -input ENUM_TIMEFRAMES InpBiasTF = PERIOD_D1; // Bias TF (HTF) -input ENUM_TIMEFRAMES InpMiddleTF = PERIOD_H1; // FVG + trend TF (MTF) -input ENUM_TIMEFRAMES InpTriggerTF = PERIOD_M5; // Entry confirmation (LTF) +input ENUM_TIMEFRAMES InpBiasTF = PERIOD_D1; // Timeframe xác định bias (D1) +input ENUM_TIMEFRAMES InpMiddleTF = PERIOD_H1; // TF quét FVG + trend (H1) +input ENUM_TIMEFRAMES InpTriggerTF = PERIOD_M5; // TF xác nhận entry bằng MSS (M5) -input double InpRiskPercent = 1.0; // Risk % per trade -input double InpRiskReward = 2.0; // TP/SL ratio -input double InpMaxDailyLossPct = 3.0; // Max daily loss % +input double InpRiskPercent = 1.0; // % balance risk mỗi lệnh +input double InpRiskReward = 2.0; // Tỷ lệ R:R (TP = entry ± R*riskDist) +input double InpMaxDailyLossPct = 3.0; // % lỗ tối đa trong ngày → dừng trade -input int InpLondonStartHour = 8; // London open (UTC) -input int InpLondonEndHour = 17; // London close (UTC) -input int InpNYStartHour = 13; // NY open (UTC) -input int InpNYEndHour = 22; // NY close (UTC) +input int InpLondonStartHour = 8; // Giờ mở London (UTC) +input int InpLondonEndHour = 17; // Giờ đóng London (UTC) +input int InpNYStartHour = 13; // Giờ mở NY (UTC) +input int InpNYEndHour = 22; // Giờ đóng NY (UTC) -input int InpSwingRange = 3; // Bars each side for swing confirm -input int InpSwingLookback = 50; // MiddleTF swing scan bars -input int InpTriggerSwingLookback = 30; // TriggerTF swing scan bars +input int InpSwingRange = 3; // Số nến mỗi bên để xác nhận swing high/low +input int InpSwingLookback = 50; // Số bar quét swing trên MiddleTF (H1) +input int InpTriggerSwingLookback = 30; // Số bar quét swing trên TriggerTF (M5) -input int InpFVGMaxAliveMin = 4320; // Max FVG lifetime (min) = 72h (cả PENDING + TOUCHED) -input int InpFVGScanBars = 50; // MiddleTF bars to scan for FVGs -input double InpFVGMinBodyPct = 60.0; // Mid-candle min body % -input int InpMSSMinDepthPts = 30; // MSS min swing depth (points): |tH0-tL0| phải >= giá trị này +input int InpFVGMaxAliveMin = 4320; // Thời gian sống FVG tối đa (phút), 4320 = 72h +input int InpFVGScanBars = 50; // Số bar H1 quét tìm FVG +input double InpFVGMinBodyPct = 60.0; // Nến giữa FVG: body % tối thiểu (strong candle) +input int InpMSSMinDepthPts = 30; // Độ sâu swing M5 tối thiểu (point) mới chấp nhận MSS -input long InpMagicNumber = 20250308; // EA magic number -input int InpSlippage = 5; // Max slippage (points) +input long InpMagicNumber = 20250308; // Magic number cho lệnh EA +input int InpSlippage = 5; // Slippage tối đa (point) khi gửi lệnh -input bool InpDebugLog = true; // Journal logging -input bool InpDebugDraw = true; // Chart drawing +input bool InpDebugLog = true; // Bật log ra Journal +input bool InpDebugDraw = true; // Bật vẽ swing/FVG/order/debug panel #endif // EA_ICT_CL__CONFIG_MQH diff --git a/Experts/EA_ICT_CL/Contexts.mqh b/Experts/EA_ICT_CL/Contexts.mqh index f4d2959..0a1b6a9 100644 --- a/Experts/EA_ICT_CL/Contexts.mqh +++ b/Experts/EA_ICT_CL/Contexts.mqh @@ -1,40 +1,37 @@ #ifndef EA_ICT_CL__CONTEXTS_MQH -#define EA_ICT_CL__CONTEXTS_MQH +#define EA_ICT_CL__CONTEXTS_MQH // Tránh include trùng -// Module: Contexts -// Context updaters extracted from EA_ICT_CL.mq5 (Section 2 – Context updaters). -// NOTE: These functions currently use EA globals (g_*) and existing structs. -// Include this file AFTER structs + globals are declared in EA_ICT_CL.mq5. +// Module: Contexts – cập nhật D1 bias, H1/M5 trend (swing + MSS), daily risk inline HTFBias ResolveBias(double b1H, double b1L, double b1C, double b2H, double b2L) { - if (b1C > b2H) return BIAS_UP; - if (b1C < b2L) return BIAS_DOWN; - if (b1H > b2H && b1C < b2H) return BIAS_DOWN; - if (b1L < b2L && b1C > b2L) return BIAS_UP; - return BIAS_SIDEWAY; + if (b1C > b2H) return BIAS_UP; // Close bar 1 trên range bar 2 → tăng + if (b1C < b2L) return BIAS_DOWN; // Close bar 1 dưới range bar 2 → giảm + if (b1H > b2H && b1C < b2H) return BIAS_DOWN; // Phá cao rồi đóng dưới → bearish + if (b1L < b2L && b1C > b2L) return BIAS_UP; // Phá thấp rồi đóng trên → bullish + return BIAS_SIDEWAY; // Còn lại = sideway } inline void UpdateBiasContext() { - datetime t0 = iTime(_Symbol, InpBiasTF, 0); - if (t0 == g_Bias.lastBarTime) return; + datetime t0 = iTime(_Symbol, InpBiasTF, 0); // Bar D1 hiện tại (đang hình thành) + if (t0 == g_Bias.lastBarTime) return; // Đã xử lý bar này rồi → bỏ qua g_Bias.lastBarTime = t0; - if (Bars(_Symbol, InpBiasTF) < 4) { g_Bias.bias = BIAS_NONE; return; } + if (Bars(_Symbol, InpBiasTF) < 4) { g_Bias.bias = BIAS_NONE; return; } // Không đủ dữ liệu - double b1H = iHigh (_Symbol, InpBiasTF, 1); + double b1H = iHigh (_Symbol, InpBiasTF, 1); // Bar D1 vừa đóng double b1L = iLow (_Symbol, InpBiasTF, 1); double b1C = iClose(_Symbol, InpBiasTF, 1); - double b2H = iHigh (_Symbol, InpBiasTF, 2); + double b2H = iHigh (_Symbol, InpBiasTF, 2); // Bar D1 trước đó double b2L = iLow (_Symbol, InpBiasTF, 2); HTFBias prev = g_Bias.bias; - g_Bias.bias = ResolveBias(b1H, b1L, b1C, b2H, b2L); - g_Bias.rangeHigh = (g_Bias.bias == BIAS_SIDEWAY) ? b2H : 0; + g_Bias.bias = ResolveBias(b1H, b1L, b1C, b2H, b2L); // Tính bias + g_Bias.rangeHigh = (g_Bias.bias == BIAS_SIDEWAY) ? b2H : 0; // Vùng sideway g_Bias.rangeLow = (g_Bias.bias == BIAS_SIDEWAY) ? b2L : 0; - if (InpDebugLog && g_Bias.bias != prev) + if (InpDebugLog && g_Bias.bias != prev) // Chỉ log khi bias đổi PrintFormat("[BIAS] %s → %s | b1[H=%.5f L=%.5f C=%.5f] b2[H=%.5f L=%.5f]", EnumToString(prev), EnumToString(g_Bias.bias), b1H, b1L, b1C, b2H, b2L); } @@ -42,12 +39,13 @@ inline void UpdateBiasContext() inline void UpdateTFTrendContext(ENUM_TIMEFRAMES tf, int lookback, TFTrendContext &ctx) { datetime t0 = iTime(_Symbol, tf, 0); - if (t0 == ctx.lastBarTime) return; + if (t0 == ctx.lastBarTime) return; // Bar chưa đổi → không cập nhật ctx.lastBarTime = t0; - double bar1C = iClose(_Symbol, tf, 1); + double bar1C = iClose(_Symbol, tf, 1); // Close bar vừa đóng datetime bar1T = iTime (_Symbol, tf, 1); + // Chỉ detect MSS khi đang WAIT_TRIGGER, trên M5, đã có h0/l0, H1 trend rõ if (tf == InpTriggerTF && g_State == EA_WAIT_TRIGGER && ctx.h0 > 0 && ctx.l0 > 0 @@ -57,14 +55,14 @@ inline void UpdateTFTrendContext(ENUM_TIMEFRAMES tf, int lookback, TFTrendContex MarketDir breakDir = DIR_NONE; double entryLevel = 0, slLevel = 0; - if (g_MiddleTrend.trend == DIR_UP && bar1C > ctx.h0) + if (g_MiddleTrend.trend == DIR_UP && bar1C > ctx.h0) // H1 uptrend + M5 close phá tH0 = bull MSS { mssHit = true; breakDir = DIR_UP; - entryLevel = ctx.h0; - slLevel = ctx.l0; + entryLevel = ctx.h0; // Entry = tH0 + slLevel = ctx.l0; // SL = tL0 } - else if (g_MiddleTrend.trend == DIR_DOWN && bar1C < ctx.l0) + else if (g_MiddleTrend.trend == DIR_DOWN && bar1C < ctx.l0) // Bear MSS { mssHit = true; breakDir = DIR_DOWN; @@ -72,10 +70,10 @@ inline void UpdateTFTrendContext(ENUM_TIMEFRAMES tf, int lookback, TFTrendContex slLevel = ctx.h0; } - if (mssHit && bar1T != ctx.lastMssTime) + if (mssHit && bar1T != ctx.lastMssTime) // MSS mới (chưa ghi nhận bar này) { - double swingDepth = MathAbs(ctx.h0 - ctx.l0) / _Point; - if (swingDepth < InpMSSMinDepthPts) + double swingDepth = MathAbs(ctx.h0 - ctx.l0) / _Point; // Độ sâu swing (point) + if (swingDepth < InpMSSMinDepthPts) // Quá nông → bỏ qua { if (InpDebugLog) PrintFormat("[M5 MSS SKIP] depth=%.0f pts < %d | H0=%.5f L0=%.5f | %s", @@ -83,10 +81,10 @@ inline void UpdateTFTrendContext(ENUM_TIMEFRAMES tf, int lookback, TFTrendContex } else { - ctx.lastMssTime = bar1T; - ctx.lastMssLevel = entryLevel; - ctx.lastMssBreak = breakDir; - ctx.mssSLSwing = slLevel; + ctx.lastMssTime = bar1T; // Ghi nhận thời điểm MSS + ctx.lastMssLevel = entryLevel; // Giá entry + ctx.lastMssBreak = breakDir; // UP/DOWN + ctx.mssSLSwing = slLevel; // Giá SL (tL0 hoặc tH0) if (InpDebugLog) PrintFormat("[M5 MSS] %s | entry=%.5f SL=%.5f depth=%.0fpts | close=%.5f | H0=%.5f L0=%.5f | %s", @@ -100,15 +98,15 @@ inline void UpdateTFTrendContext(ENUM_TIMEFRAMES tf, int lookback, TFTrendContex double h0, h1, l0, l1; int idxH0, idxH1, idxL0, idxL1; if (!ScanSwingStructure(tf, lookback, h0, h1, idxH0, idxH1, l0, l1, idxL0, idxL1)) - { ctx.trend = DIR_NONE; return; } + { ctx.trend = DIR_NONE; return; } // Không đủ swing - ctx.h0 = h0; ctx.idxH0 = idxH0; + ctx.h0 = h0; ctx.idxH0 = idxH0; // Gán swing vào context ctx.h1 = h1; ctx.idxH1 = idxH1; ctx.l0 = l0; ctx.idxL0 = idxL0; ctx.l1 = l1; ctx.idxL1 = idxL1; MarketDir prev = ctx.trend; - ResolveTrendFromSwings(tf, h0, h1, l0, l1, ctx.trend, ctx.keyLevel); + ResolveTrendFromSwings(tf, h0, h1, l0, l1, ctx.trend, ctx.keyLevel); // Suy trend + key level if (InpDebugLog && ctx.trend != prev) PrintFormat("[%s TREND] %s → %s | H0=%.5f H1=%.5f L0=%.5f L1=%.5f | KL=%.5f", @@ -118,20 +116,20 @@ inline void UpdateTFTrendContext(ENUM_TIMEFRAMES tf, int lookback, TFTrendContex inline void UpdateDailyRiskContext() { - datetime today = iTime(_Symbol, PERIOD_D1, 0); - if (today != g_DailyRisk.dayStartTime) + datetime today = iTime(_Symbol, PERIOD_D1, 0); // Mốc D1 hiện tại + if (today != g_DailyRisk.dayStartTime) // Sang ngày mới { g_DailyRisk.dayStartTime = today; - g_DailyRisk.startBalance = AccountInfoDouble(ACCOUNT_BALANCE); - g_DailyRisk.limitHit = false; + g_DailyRisk.startBalance = AccountInfoDouble(ACCOUNT_BALANCE); // Balance đầu ngày + g_DailyRisk.limitHit = false; // Reset cờ chạm limit if (InpDebugLog) PrintFormat("[DAILY RISK] New day | start=%.2f", g_DailyRisk.startBalance); } - if (g_DailyRisk.limitHit) return; + if (g_DailyRisk.limitHit) return; // Đã chạm limit → không cập nhật gì thêm g_DailyRisk.currentBalance = AccountInfoDouble(ACCOUNT_BALANCE); double lostPct = (g_DailyRisk.startBalance - g_DailyRisk.currentBalance) - / g_DailyRisk.startBalance * 100.0; - if (lostPct >= InpMaxDailyLossPct) + / g_DailyRisk.startBalance * 100.0; // % lỗ so với đầu ngày + if (lostPct >= InpMaxDailyLossPct) // Vượt ngưỡng max daily loss { g_DailyRisk.limitHit = true; PrintFormat("[DAILY RISK] ⛔ Limit hit | lost=%.2f%% | bal=%.2f", @@ -141,10 +139,10 @@ inline void UpdateDailyRiskContext() inline void UpdateAllContexts() { - UpdateDailyRiskContext(); - UpdateBiasContext(); - UpdateTFTrendContext(InpMiddleTF, InpSwingLookback, g_MiddleTrend); - UpdateTFTrendContext(InpTriggerTF, InpTriggerSwingLookback, g_TriggerTrend); + UpdateDailyRiskContext(); // Cập nhật balance, limit hit + UpdateBiasContext(); // D1 bias + UpdateTFTrendContext(InpMiddleTF, InpSwingLookback, g_MiddleTrend); // H1 swing + trend + (MSS không ở đây) + UpdateTFTrendContext(InpTriggerTF, InpTriggerSwingLookback, g_TriggerTrend); // M5 swing + trend + MSS } #endif // EA_ICT_CL__CONTEXTS_MQH diff --git a/Experts/EA_ICT_CL/Filters.mqh b/Experts/EA_ICT_CL/Filters.mqh index 0fdbc6d..bc0a435 100644 --- a/Experts/EA_ICT_CL/Filters.mqh +++ b/Experts/EA_ICT_CL/Filters.mqh @@ -1,16 +1,15 @@ #ifndef EA_ICT_CL__FILTERS_MQH -#define EA_ICT_CL__FILTERS_MQH +#define EA_ICT_CL__FILTERS_MQH // Tránh include trùng -// Module: Filters -// Put spread/news/day-of-week/max-trades filters here. +// Module: Filters – lọc spread (có thể mở rộng news, max trades...) inline bool Filter_MaxSpreadPoints(const string symbol, const double max_spread_points) { - if (max_spread_points <= 0) return true; + if (max_spread_points <= 0) return true; // Không giới hạn → luôn pass const double pt = SymbolInfoDouble(symbol, SYMBOL_POINT); - if (pt <= 0.0) return true; - const double spread_pts = (SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID)) / pt; - return (spread_pts <= max_spread_points); + if (pt <= 0.0) return true; // Point lỗi → bỏ qua check + const double spread_pts = (SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID)) / pt; // Spread theo point + return (spread_pts <= max_spread_points); // Pass khi spread <= ngưỡng } #endif // EA_ICT_CL__FILTERS_MQH diff --git a/Experts/EA_ICT_CL/Guards.mqh b/Experts/EA_ICT_CL/Guards.mqh index cc1e29b..6bc572d 100644 --- a/Experts/EA_ICT_CL/Guards.mqh +++ b/Experts/EA_ICT_CL/Guards.mqh @@ -1,29 +1,27 @@ #ifndef EA_ICT_CL__GUARDS_MQH -#define EA_ICT_CL__GUARDS_MQH +#define EA_ICT_CL__GUARDS_MQH // Tránh include trùng -// Module: Guards -// Pre-trade guard checks extracted from EA_ICT_CL.mq5 (Section 3 – Guards). -// NOTE: Uses EA globals (g_*) and inputs. Include AFTER globals exist. +// Module: Guards – điều kiện trước khi cho chạy state machine (session, daily loss, bias, alignment) -inline bool IsSessionAllowed() { return true; /* TODO: implement session filter using InpLondon/NY hours */ } -inline bool IsDailyLossOK() { return !g_DailyRisk.limitHit; } -inline bool IsBiasValid() { return g_Bias.bias == BIAS_UP || g_Bias.bias == BIAS_DOWN; } +inline bool IsSessionAllowed() { return true; /* TODO: lọc giờ London/NY theo InpLondonStartHour/End, InpNY... */ } +inline bool IsDailyLossOK() { return !g_DailyRisk.limitHit; } // Chưa chạm max daily loss +inline bool IsBiasValid() { return g_Bias.bias == BIAS_UP || g_Bias.bias == BIAS_DOWN; } // Bias rõ (không NONE/SIDEWAY) inline bool IsMiddleTrendAligned() { - if (g_MiddleTrend.trend == DIR_NONE) return false; + if (g_MiddleTrend.trend == DIR_NONE) return false; // H1 không có trend → không align return (g_Bias.bias == BIAS_UP && g_MiddleTrend.trend == DIR_UP) || - (g_Bias.bias == BIAS_DOWN && g_MiddleTrend.trend == DIR_DOWN); + (g_Bias.bias == BIAS_DOWN && g_MiddleTrend.trend == DIR_DOWN); // D1 và H1 cùng chiều } inline bool EvaluateGuards() { g_BlockReason = BLOCK_NONE; - if (!IsSessionAllowed()) { g_BlockReason = BLOCK_SESSION; return false; } - if (!IsDailyLossOK()) { g_BlockReason = BLOCK_DAILY_LOSS; return false; } - if (!IsBiasValid()) { g_BlockReason = BLOCK_NO_BIAS; return false; } - if (!IsMiddleTrendAligned()) { g_BlockReason = BLOCK_BIAS_MISMATCH; return false; } - return true; + if (!IsSessionAllowed()) { g_BlockReason = BLOCK_SESSION; return false; } // Ngoài giờ trade + if (!IsDailyLossOK()) { g_BlockReason = BLOCK_DAILY_LOSS; return false; } // Đã chạm limit lỗ + if (!IsBiasValid()) { g_BlockReason = BLOCK_NO_BIAS; return false; } // Không có bias + if (!IsMiddleTrendAligned()) { g_BlockReason = BLOCK_BIAS_MISMATCH; return false; } // D1 ≠ H1 + return true; // Tất cả pass } #endif // EA_ICT_CL__GUARDS_MQH diff --git a/Experts/EA_ICT_CL/Indicators.mqh b/Experts/EA_ICT_CL/Indicators.mqh index 7a49c8b..f5ec0cd 100644 --- a/Experts/EA_ICT_CL/Indicators.mqh +++ b/Experts/EA_ICT_CL/Indicators.mqh @@ -1,22 +1,21 @@ #ifndef EA_ICT_CL__INDICATORS_MQH -#define EA_ICT_CL__INDICATORS_MQH +#define EA_ICT_CL__INDICATORS_MQH // Tránh include trùng -// Module: Indicators -// Keep indicator handle lifecycle helpers here. This file is safe to include now. +// Module: Indicators – copy dữ liệu buffer/time an toàn (series index 0 = mới nhất) inline bool CopyBufferSafe(const int handle, const int buffer, const int start_pos, const int count, double &out[]) { - if (handle == INVALID_HANDLE) return false; - ArraySetAsSeries(out, true); - const int copied = CopyBuffer(handle, buffer, start_pos, count, out); - return (copied == count); + if (handle == INVALID_HANDLE) return false; // Handle không hợp lệ + ArraySetAsSeries(out, true); // Index 0 = bar mới nhất + const int copied = CopyBuffer(handle, buffer, start_pos, count, out); // Copy dữ liệu indicator + return (copied == count); // Thành công khi copy đủ count phần tử } inline bool CopyTimeSafe(const string symbol, const ENUM_TIMEFRAMES tf, const int start_pos, const int count, datetime &out[]) { - ArraySetAsSeries(out, true); - const int copied = CopyTime(symbol, tf, start_pos, count, out); - return (copied == count); + ArraySetAsSeries(out, true); // Index 0 = bar mới nhất + const int copied = CopyTime(symbol, tf, start_pos, count, out); // Copy thời gian mở bar + return (copied == count); // Thành công khi copy đủ count } #endif // EA_ICT_CL__INDICATORS_MQH diff --git a/Experts/EA_ICT_CL/Logging.mqh b/Experts/EA_ICT_CL/Logging.mqh index 5117339..a690206 100644 --- a/Experts/EA_ICT_CL/Logging.mqh +++ b/Experts/EA_ICT_CL/Logging.mqh @@ -1,18 +1,18 @@ #ifndef EA_ICT_CL__LOGGING_MQH -#define EA_ICT_CL__LOGGING_MQH +#define EA_ICT_CL__LOGGING_MQH // Tránh include trùng -// Module: Logging +// Module: Logging – in log ra Journal khi bật InpDebugLog inline void LogPrint(const bool enabled, const string msg) { - if (!enabled) return; - Print(msg); + if (!enabled) return; // Tắt log thì thoát + Print(msg); // In chuỗi ra Experts/Journal } inline void LogPrintF(const bool enabled, const string fmt, const string a0 = "", const string a1 = "", const string a2 = "") { - if (!enabled) return; - Print(StringFormat(fmt, a0, a1, a2)); + if (!enabled) return; // Tắt log thì thoát + Print(StringFormat(fmt, a0, a1, a2)); // Format rồi in (tối đa 3 tham số string) } #endif // EA_ICT_CL__LOGGING_MQH diff --git a/Experts/EA_ICT_CL/Market.mqh b/Experts/EA_ICT_CL/Market.mqh index 32ec74b..cd47186 100644 --- a/Experts/EA_ICT_CL/Market.mqh +++ b/Experts/EA_ICT_CL/Market.mqh @@ -1,29 +1,28 @@ #ifndef EA_ICT_CL__MARKET_MQH -#define EA_ICT_CL__MARKET_MQH +#define EA_ICT_CL__MARKET_MQH // Tránh include trùng -// Module: Market -// Market/tick/contract-spec helpers live here. +// Module: Market – giá, point, spread, điều kiện trade -inline double GetBid(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_BID); } -inline double GetAsk(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_ASK); } -inline double GetPoint(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_POINT); } -inline int GetDigits(const string symbol) { return (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); } +inline double GetBid(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_BID); } // Giá bid +inline double GetAsk(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_ASK); } // Giá ask +inline double GetPoint(const string symbol) { return SymbolInfoDouble(symbol, SYMBOL_POINT); } // Kích thước 1 point +inline int GetDigits(const string symbol) { return (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); } // Số chữ số thập phân inline double GetSpreadPoints(const string symbol) { - const double bid = GetBid(symbol); - const double ask = GetAsk(symbol); - const double pt = GetPoint(symbol); - if (pt <= 0.0) return 0.0; - return (ask - bid) / pt; + const double bid = GetBid(symbol); // Lấy bid + const double ask = GetAsk(symbol); // Lấy ask + const double pt = GetPoint(symbol); // Lấy point + if (pt <= 0.0) return 0.0; // Tránh chia 0 + return (ask - bid) / pt; // Spread quy đổi ra số point } inline bool IsTradeAllowedNow(const string symbol) { - if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return false; - if (!MQLInfoInteger(MQL_TRADE_ALLOWED)) return false; - if (!SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE)) return false; - return true; + if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return false; // Terminal có cho trade không + if (!MQLInfoInteger(MQL_TRADE_ALLOWED)) return false; // EA có quyền trade không + if (!SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE)) return false; // Symbol có cho trade không + return true; // Đủ điều kiện } #endif // EA_ICT_CL__MARKET_MQH diff --git a/Experts/EA_ICT_CL/Orders.mqh b/Experts/EA_ICT_CL/Orders.mqh index 116c706..bb15e07 100644 --- a/Experts/EA_ICT_CL/Orders.mqh +++ b/Experts/EA_ICT_CL/Orders.mqh @@ -1,18 +1,17 @@ #ifndef EA_ICT_CL__ORDERS_MQH -#define EA_ICT_CL__ORDERS_MQH +#define EA_ICT_CL__ORDERS_MQH // Tránh include trùng -// Module: Orders -// Helpers for building request fields, normalize price, magic/comment conventions. +// Module: Orders – chuẩn hóa giá, tạo comment lệnh (magic, fvg_id) inline double NormalizePrice(const string symbol, const double price) { - const int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); - return NormalizeDouble(price, digits); + const int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); // Số chữ số thập phân của symbol + return NormalizeDouble(price, digits); // Làm tròn giá theo digits } inline string BuildOrderComment(const long magic, const int fvg_id, const string tag = "ICT") { - return StringFormat("%s|mg=%I64d|fvg=%d", tag, magic, fvg_id); + return StringFormat("%s|mg=%I64d|fvg=%d", tag, magic, fvg_id); // Comment để trace EA + FVG } #endif // EA_ICT_CL__ORDERS_MQH diff --git a/Experts/EA_ICT_CL/README.md b/Experts/EA_ICT_CL/README.md index 2e5840b..127f7fc 100644 --- a/Experts/EA_ICT_CL/README.md +++ b/Experts/EA_ICT_CL/README.md @@ -1,134 +1,117 @@ -# EA_ICT_CL – Kiến trúc & luồng chạy +# EA_ICT_CL – Kiến trúc & cấu trúc file -File chính: `Experts/EA_ICT_CL.mq5` -Thư mục module: `Experts/EA_ICT_CL/` (các `.mqh`) +**EA chính:** `Experts/EA_ICT_CL.mq5` +**Thư mục module:** `Experts/EA_ICT_CL/` (các file `.mqh`) -## 1) Mục tiêu thiết kế +--- -- `EA_ICT_CL.mq5` nên là **file “glue”**: `OnInit/OnDeinit/OnTick/...` + gọi hàm module. -- Mỗi `.mqh` chỉ gánh **một trách nhiệm**: config/state/signals/risk/trade/filters/… -- Migrate **từng phần nhỏ** để tránh trùng định nghĩa (enum/struct/global) và dễ debug. +## 1. Cấu trúc thư mục và chức năng từng file -## 2) Luồng chạy tổng thể (theo mô tả hiện tại trong header v4.3) +### File chính (entry point) -### Bối cảnh timeframe +| File | Vai trò | +|------|--------| +| **`EA_ICT_CL.mq5`** | File “glue”: khai báo globals, include 3 tầng module, và chỉ chứa `OnInit()`, `OnTick()`, `OnDeinit()`. Không chứa logic nghiệp vụ. | -- **BiasTF (D1)**: xác định bias (UP/DOWN/SIDEWAY/NONE) -- **MiddleTF (H1)**: xác định trend + quét FVG thuận xu hướng -- **TriggerTF (M5)**: xác nhận entry bằng **MSS** (market structure shift) +### Layer 1 – Cấu hình & kiểu dữ liệu (include đầu tiên) + +| File | Chức năng | +|------|-----------| +| **`Config.mqh`** | Toàn bộ **input** của EA: timeframe (Bias/Middle/Trigger), risk %, session giờ (London/NY), swing/FVG/MSS params, magic, slippage, debug flags. | +| **`State.mqh`** | **Enums** và **struct**: `EAState`, `HTFBias`, `MarketDir`, `BlockReason`, `FVGStatus`; struct `BiasContext`, `TFTrendContext`, `FVGRecord`, `OrderPlan`, `DailyRiskContext`. Không chứa biến global. | + +### Layer 2 – Tiện ích (sau Config + State) + +| File | Chức năng | +|------|-----------| +| **`Utils.mqh`** | Hàm helper thuần: `ClampDouble`, `ClampInt`, `RoundToStep`, `IsNewBar`. | +| **`Logging.mqh`** | Wrapper log bật/tắt: `LogPrint`, `LogPrintF` (gọi khi `InpDebugLog` bật). | +| **`Market.mqh`** | Helper market: `GetBid`, `GetAsk`, `GetPoint`, `GetDigits`, `GetSpreadPoints`, `IsTradeAllowedNow`. | +| **`Indicators.mqh`** | Wrapper an toàn: `CopyBufferSafe`, `CopyTimeSafe` cho dữ liệu series. | +| **`Sessions.mqh`** | Lọc theo giờ: `IsHourInRange`, `GetUTCHour` (dùng cho session London/NY). | +| **`Filters.mqh`** | Bộ lọc chung: `Filter_MaxSpreadPoints` (có thể mở rộng news, max trades/day). | +| **`Risk.mqh`** | Risk & volume: `NormalizeVolume`, `LotsFromRiskMoneyAndSLPoints`. | +| **`Orders.mqh`** | Chuẩn hoá order: `NormalizePrice`, `BuildOrderComment`. | +| **`Swing.mqh`** | **Swing structure**: `MyBarShift`, `IsSwingHighAt`, `IsSwingLowAt`, `ScanSwingStructure`, `ResolveTrendFromSwings`. | +| **`Trailing.mqh`** | Placeholder cho logic trailing / breakeven / partial close (chưa implement). | + +### Layer 3 – Logic nghiệp vụ (sau khi đã khai báo globals trong `.mq5`) + +| File | Chức năng | +|------|-----------| +| **`Contexts.mqh`** | Cập nhật context: `ResolveBias`, `UpdateBiasContext`, `UpdateTFTrendContext`, `UpdateDailyRiskContext`, `UpdateAllContexts`. Dùng globals `g_Bias`, `g_MiddleTrend`, `g_TriggerTrend`, `g_DailyRisk`. | +| **`Guards.mqh`** | Điều kiện trước khi trade: `IsSessionAllowed`, `IsDailyLossOK`, `IsBiasValid`, `IsMiddleTrendAligned`, `EvaluateGuards`. Set `g_BlockReason` khi block. | +| **`Signals_BOS_FVG_OB.mqh`** | **FVG**: `IsCandleStrong`, `IsFVGInPool`, `ScanAndRegisterFVGs`, `UpdateFVGStatuses`, `GetBestActiveFVGIdx`. Quản lý pool FVG và trạng thái PENDING/TOUCHED/USED. | +| **`Trade.mqh`** | **Order plan & execution**: `CalcLotFromRisk`, `BuildOrderPlan`, `ExecuteLimitOrder`. Gửi lệnh pending limit, dùng `g_OrderPlan`. | +| **`StateMachine.mqh`** | **State machine**: `TransitionTo`, `ResetToIdle`, `OnStateIdle`, `OnStateWaitTouch`, `OnStateWaitTrigger`, `OnStateInTrade`, `RunStateMachine`. Điều phối theo `g_State`. | +| **`Drawing.mqh`** | **Vẽ trên chart**: `DrawOneSwingPoint`, `DrawMiddleSwingPoints`, `DrawTriggerSwingPoints`, `DrawMSSMarker`, `DrawMSSMarkers`, `DrawOrderVisualization`, `DrawOneFVGRecord`, `DrawFVGPool`, `DrawContextDebug`, `DrawVisuals`. Dùng prefix `PREFIX_SWING_MIDDLE`, `PREFIX_ORDER_VISUAL`, v.v. | + +--- + +## 2. Thứ tự include trong `EA_ICT_CL.mq5` + +``` +Layer 1: Config.mqh → State.mqh +Layer 2: Utils, Logging, Market, Indicators, Sessions, Filters, Risk, Orders, Swing, Trailing +Globals: g_State, g_Bias, g_MiddleTrend, g_TriggerTrend, g_DailyRisk, g_FVGPool, g_OrderPlan, PREFIX_* +Layer 3: Contexts.mqh → Guards.mqh → Signals_BOS_FVG_OB.mqh → Trade.mqh → StateMachine.mqh → Drawing.mqh +``` + +Layer 3 phải include **sau** khi globals và prefix đã được khai báo trong `.mq5`. + +--- + +## 3. Luồng chạy tổng thể (v4.3) + +### Timeframe + +- **BiasTF (D1):** xác định bias (UP/DOWN/SIDEWAY/NONE). +- **MiddleTF (H1):** trend + quét FVG thuận xu hướng. +- **TriggerTF (M5):** xác nhận entry bằng MSS (market structure shift). ### State machine -EA đang dùng state machine: +| State | Mục đích | +|-------|----------| +| **EA_IDLE** | Chọn FVG “tốt nhất” (ưu tiên TOUCHED, rồi PENDING mới nhất). | +| **EA_WAIT_TOUCH** | Chờ giá retrace vào vùng FVG (H1). | +| **EA_WAIT_TRIGGER** | Đã touch FVG → chờ M5 MSS (chỉ detect MSS trong state này). | +| **EA_IN_TRADE** | Theo dõi pending/position; reset khi fill, cancel hoặc đóng lệnh. | -- **`EA_IDLE`** - - Mục tiêu: tìm/đặt “FVG tốt nhất” (MiddleTF) để theo dõi. -- **`EA_WAIT_TOUCH`** - - Mục tiêu: chờ giá retrace và **touch vào vùng FVG** (H1). -- **`EA_WAIT_TRIGGER`** - - Mục tiêu: khi đã touch FVG, chờ **M5 MSS** xác nhận đảo chiều theo trend. - - v4.3: MSS chỉ detect khi state này. -- **`EA_IN_TRADE`** - - Mục tiêu: quản lý pending/position (trailing/timeout/cleanup tuỳ EA). +### Luồng quyết định (mỗi tick) -### Decision flow “từ trên xuống” +1. **UpdateAllContexts()** – Cập nhật D1 bias, H1/M5 swing (và MSS nếu đang WAIT_TRIGGER). +2. **EvaluateGuards()** – Kiểm tra session, daily loss, bias, alignment H1/D1. +3. Nếu pass → **RunStateMachine()**: `UpdateFVGStatuses()` + `ScanAndRegisterFVGs()` + xử lý theo state (Idle → chọn FVG; WaitTouch → chờ touch/switch FVG; WaitTrigger → khi FVG triggered thì BuildOrderPlan + ExecuteLimitOrder; InTrade → theo dõi order/position). +4. Nếu **InpDebugDraw** bật → **DrawVisuals()** (swing, MSS, FVG pool, order, debug panel). -1) **D1 Bias** - - Xác định `HTFBias` (UP/DOWN/SIDEWAY/NONE). -2) **H1 Trend / Swing structure** - - Xác định `MarketDir` (UP/DOWN/NONE) và các swing gần nhất: `h0/h1/l0/l1`. -3) **Quét H1 FVG** - - Tạo/duy trì pool FVG (`g_FVGPool`) tối đa `MAX_FVG_POOL`. - - FVG có lifecycle: `PENDING → TOUCHED → USED`. -4) **Touch FVG** - - Khi bid/ask đi vào vùng gap → `touchTime` được set, chuyển state sang chờ trigger. -5) **M5 MSS (chỉ khi `EA_WAIT_TRIGGER`)** - - Nếu H1 UP: bull MSS khi close phá **OLD** `tH0` → entry = `tH0`, SL = `tL0`. - - Nếu H1 DOWN: bear MSS khi close phá **OLD** `tL0` → entry = `tL0`, SL = `tH0`. -6) **Lập kế hoạch lệnh (OrderPlan)** - - `direction`: +1 BUY LIMIT / -1 SELL LIMIT - - `entry`, `stopLoss`, `takeProfit`, `lot` -7) **Risk sizing** - - Lot theo `%risk` và khoảng SL (points). - - Chặn trade nếu chạm max daily loss (`InpMaxDailyLossPct`). -8) **Gửi pending limit** - - Gắn `magic` và comment có `fvg_id` để trace. +### Entry / SL / TP -## 3) Các folder/file module (vai trò + khi nào dùng) +- **Entry:** giá swing bị phá (tH0 cho buy, tL0 cho sell). +- **SL:** swing đối diện (tL0 cho buy, tH0 cho sell) + buffer 2 point. +- **TP:** Entry ± `InpRiskReward` × |Entry − SL|. -Thư mục: `Experts/EA_ICT_CL/` +--- -- **`Config.mqh`** - - Nơi đưa `input`, `enum` và hằng số cấu hình. - - Khi migrate: chuyển dần `input ...` và các enum khỏi `.mq5` sang đây (rồi include). +## 4. Đặt tên object trên chart (prefix) -- **`State.mqh`** - - Nơi đưa `struct` contexts (bias/trend/fvg/orderplan/daily risk) + state machine. - - Khi migrate: chuyển dần các `struct`, `g_*` globals sang đây để module nào cũng dùng chung. +Các hằng trong `EA_ICT_CL.mq5` dùng cho Drawing: -- **`Utils.mqh`** - - Helper thuần: clamp/round/newbar/time formatting… - - Ưu tiên migrate các hàm nhỏ, không phụ thuộc EA logic (vd `MyBarShift` cũng có thể đặt vào đây hoặc `Market/Indicators`). +- `PREFIX_SWING_MIDDLE` – H1 swing (MiddleH0/L0…). +- `PREFIX_SWING_TRIGGER` – M5 swing (TriggerH0/L0…). +- `PREFIX_MSS_MARKER` – Điểm đánh dấu MSS. +- `PREFIX_FVG_POOL` – Các hình chữ nhật FVG. +- `PREFIX_ORDER_VISUAL` – Vùng/label Entry/SL/TP. +- `PREFIX_DEBUG_PANEL` – Bảng thông tin góc trái (Bias, H1/M5, Risk, State, Pool, Order). -- **`Logging.mqh`** - - Chuẩn hoá log bật/tắt theo `InpDebugLog`. - - Có thể mở rộng: prefix theo symbol/tf/state, ghi file, debug panel. +--- -- **`Market.mqh`** - - Helper về symbol specs (digits/point/spread), trade allowed, quote access… - -- **`Sessions.mqh`** - - Helper session/time filter (London/NY theo UTC). - - Khi migrate: đưa logic “được trade trong giờ nào?” sang đây. - -- **`Filters.mqh`** - - Các bộ lọc: spread, news (nếu có), day-of-week, max trades/day, cooldown… - -- **`Indicators.mqh`** - - Wrapper CopyBuffer/CopyTime, quản lý handles nếu bạn dùng iMA/iATR/… - -- **`Signals_BOS_FVG_OB.mqh`** - - Nơi tập trung **logic tín hiệu**: BOS/MSS/FVG/OB… và build `OrderPlan`. - - Mục tiêu: module này “pure” nhất có thể (input/state snapshot → plan). - -- **`Risk.mqh`** - - Tính lot, normalize volume, kiểm soát daily drawdown. - -- **`Orders.mqh`** - - Chuẩn hoá normalize price, build comment, mapping direction→order type… - -- **`Trade.mqh`** - - Các hàm “side-effect”: `OrderSend`, modify, cancel, close. - -- **`Trailing.mqh`** - - Trailing/BE/partial close (nếu có). - -## 4) Gợi ý thứ tự migrate (an toàn, ít rủi ro nhất) - -Để tránh trùng định nghĩa và lỗi include: - -1) **Move helpers**: `MyBarShift`, swing helper functions → `Utils.mqh` (hoặc tách `Swing.mqh` nếu bạn muốn). -2) **Move trade wrappers**: các đoạn `MqlTradeRequest/Result` → `Trade.mqh` + helper normalize → `Orders.mqh`. -3) **Move risk**: tính lot / daily loss → `Risk.mqh`. -4) **Move filters/session** → `Sessions.mqh`, `Filters.mqh`. -5) **Cuối cùng mới move types**: enums/struct/global sang `Config.mqh` + `State.mqh`. - -> Lưu ý: Chỉ include module nào khi bạn đã chuyển phần tương ứng khỏi `.mq5` (để tránh “redefinition”). - -## 5) Quy ước include trong `EA_ICT_CL.mq5` (gợi ý) - -Khi bắt đầu migrate, thường include theo thứ tự: - -1. `Config.mqh` -2. `State.mqh` -3. `Utils.mqh` -4. `Logging.mqh` -5. `Market.mqh` -6. `Indicators.mqh` -7. `Sessions.mqh` / `Filters.mqh` -8. `Risk.mqh` -9. `Orders.mqh` -10. `Trade.mqh` -11. `Signals_BOS_FVG_OB.mqh` -12. `Trailing.mqh` +## 5. Mở rộng / chỉnh sửa gợi ý +- **Thêm input:** chỉnh trong `Config.mqh`. +- **Thêm enum/struct:** chỉnh trong `State.mqh`. +- **Logic FVG / chọn FVG:** chỉnh trong `Signals_BOS_FVG_OB.mqh`. +- **Tính lot / gửi lệnh:** chỉnh trong `Trade.mqh`. +- **Chuyển state / xử lý từng state:** chỉnh trong `StateMachine.mqh`. +- **Trailing / breakeven:** bổ sung trong `Trailing.mqh` và gọi từ state IN_TRADE (trong `StateMachine.mqh` hoặc từ `OnTick`). +- **Vẽ thêm object:** chỉnh trong `Drawing.mqh`, dùng đúng prefix để `OnDeinit` có thể dọn theo prefix nếu cần. diff --git a/Experts/EA_ICT_CL/Risk.mqh b/Experts/EA_ICT_CL/Risk.mqh index 9baddda..8d72507 100644 --- a/Experts/EA_ICT_CL/Risk.mqh +++ b/Experts/EA_ICT_CL/Risk.mqh @@ -1,40 +1,39 @@ #ifndef EA_ICT_CL__RISK_MQH -#define EA_ICT_CL__RISK_MQH +#define EA_ICT_CL__RISK_MQH // Tránh include trùng -// Module: Risk -// Put risk sizing and daily loss logic here. +// Module: Risk – chuẩn hóa volume, tính lot từ tiền risk và SL (point) inline double NormalizeVolume(const string symbol, const double vol) { - const double vmin = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); - const double vmax = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); - const double vstep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); + const double vmin = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); // Lot tối thiểu + const double vmax = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); // Lot tối đa + const double vstep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); // Bước lot double v = vol; - if (vstep > 0.0) v = MathFloor(v / vstep) * vstep; - if (v < vmin) v = vmin; - if (v > vmax) v = vmax; + if (vstep > 0.0) v = MathFloor(v / vstep) * vstep; // Làm tròn xuống theo step + if (v < vmin) v = vmin; // Không nhỏ hơn min + if (v > vmax) v = vmax; // Không lớn hơn max return v; } inline double LotsFromRiskMoneyAndSLPoints(const string symbol, const double risk_money, const double sl_points) { - if (risk_money <= 0.0) return 0.0; - if (sl_points <= 0.0) return 0.0; + if (risk_money <= 0.0) return 0.0; // Không risk → 0 lot + if (sl_points <= 0.0) return 0.0; // SL = 0 → không tính được double tick_value = 0.0, tick_size = 0.0; - if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE, tick_value)) return 0.0; - if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tick_size)) return 0.0; + if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE, tick_value)) return 0.0; // Giá trị 1 tick/lot + if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tick_size)) return 0.0; // Kích thước tick if (tick_value <= 0.0 || tick_size <= 0.0) return 0.0; const double point = SymbolInfoDouble(symbol, SYMBOL_POINT); if (point <= 0.0) return 0.0; - // Money per 1 lot per point move: + // Tiền mất trên 1 point khi di chuyển 1 lot: const double money_per_point_1lot = tick_value * (point / tick_size); if (money_per_point_1lot <= 0.0) return 0.0; - const double lots = risk_money / (sl_points * money_per_point_1lot); - return NormalizeVolume(symbol, lots); + const double lots = risk_money / (sl_points * money_per_point_1lot); // Lot = risk_money / (sl_points * $/point) + return NormalizeVolume(symbol, lots); // Chuẩn hóa theo min/max/step } #endif // EA_ICT_CL__RISK_MQH diff --git a/Experts/EA_ICT_CL/Sessions.mqh b/Experts/EA_ICT_CL/Sessions.mqh index 33ec846..97ab5b9 100644 --- a/Experts/EA_ICT_CL/Sessions.mqh +++ b/Experts/EA_ICT_CL/Sessions.mqh @@ -1,24 +1,21 @@ #ifndef EA_ICT_CL__SESSIONS_MQH -#define EA_ICT_CL__SESSIONS_MQH +#define EA_ICT_CL__SESSIONS_MQH // Tránh include trùng -// Module: Sessions -// Time/session filters live here (UTC-based if your EA uses UTC inputs). +// Module: Sessions – kiểm tra giờ trong khoảng (UTC), dùng cho London/NY inline bool IsHourInRange(const int hour, const int start_hour, const int end_hour) { - // Handles normal and overnight windows: - // - start < end: [start, end) - // - start > end: [start, 24) U [0, end) - if (start_hour == end_hour) return true; - if (start_hour < end_hour) return (hour >= start_hour && hour < end_hour); - return (hour >= start_hour || hour < end_hour); + // start < end: [start, end); start > end: qua đêm [start,24) U [0,end) + if (start_hour == end_hour) return true; // Cả ngày + if (start_hour < end_hour) return (hour >= start_hour && hour < end_hour); // Cùng ngày + return (hour >= start_hour || hour < end_hour); // Qua nửa đêm } inline int GetUTCHour(const datetime t) { MqlDateTime dt; - TimeToStruct(t, dt); - return dt.hour; + TimeToStruct(t, dt); // Chuyển datetime sang struct (năm, tháng, giờ...) + return dt.hour; // Trả về giờ (0–23) } #endif // EA_ICT_CL__SESSIONS_MQH diff --git a/Experts/EA_ICT_CL/Signals_BOS_FVG_OB.mqh b/Experts/EA_ICT_CL/Signals_BOS_FVG_OB.mqh index cb6bb15..6f49f38 100644 --- a/Experts/EA_ICT_CL/Signals_BOS_FVG_OB.mqh +++ b/Experts/EA_ICT_CL/Signals_BOS_FVG_OB.mqh @@ -1,10 +1,7 @@ #ifndef EA_ICT_CL__SIGNALS_BOS_FVG_OB_MQH -#define EA_ICT_CL__SIGNALS_BOS_FVG_OB_MQH +#define EA_ICT_CL__SIGNALS_BOS_FVG_OB_MQH // Tránh include trùng -// Module: Signals (FVG) -// Extracted from EA_ICT_CL.mq5 (Sections 5, 6, 7, 8). -// FVG helpers, scan/register, status update, best selector. -// NOTE: Uses EA globals (g_*) and inputs. Include AFTER globals exist. +// Module: Signals (FVG) – helper FVG, quét/đăng ký pool, cập nhật trạng thái, chọn FVG tốt nhất //+------------------------------------------------------------------+ //| SECTION 5 – FVG HELPERS | @@ -15,14 +12,14 @@ inline bool IsCandleStrong(ENUM_TIMEFRAMES tf, int i) double h = iHigh(_Symbol, tf, i), l = iLow(_Symbol, tf, i); double o = iOpen(_Symbol, tf, i), c = iClose(_Symbol, tf, i); double range = h - l; - if (range < _Point) return false; - return (MathAbs(c - o) / range * 100.0) >= InpFVGMinBodyPct; + if (range < _Point) return false; // Tránh chia 0 + return (MathAbs(c - o) / range * 100.0) >= InpFVGMinBodyPct; // Body % >= ngưỡng = nến mạnh } inline bool IsFVGInPool(datetime created) { for (int j = 0; j < g_FVGCount; j++) - if (g_FVGPool[j].createdTime == created) return true; + if (g_FVGPool[j].createdTime == created) return true; // Đã có FVG cùng thời điểm tạo return false; } diff --git a/Experts/EA_ICT_CL/State.mqh b/Experts/EA_ICT_CL/State.mqh index 6b41be9..bff1d8b 100644 --- a/Experts/EA_ICT_CL/State.mqh +++ b/Experts/EA_ICT_CL/State.mqh @@ -1,33 +1,31 @@ #ifndef EA_ICT_CL__STATE_MQH -#define EA_ICT_CL__STATE_MQH +#define EA_ICT_CL__STATE_MQH // Tránh include trùng -// Module: State -// Core EA types: enums + structs used across all modules. -// Extracted from EA_ICT_CL.mq5 (enums + structs sections). +// Module: State – enums và struct dùng chung toàn EA //==================================================== // ENUMS //==================================================== enum EAState { - EA_IDLE, - EA_WAIT_TOUCH, - EA_WAIT_TRIGGER, - EA_IN_TRADE + EA_IDLE, // Chờ chọn FVG tốt nhất + EA_WAIT_TOUCH, // Chờ giá chạm vào vùng FVG + EA_WAIT_TRIGGER, // Đã touch FVG, chờ M5 MSS + EA_IN_TRADE // Đã gửi pending / đang giữ position }; -enum HTFBias { BIAS_NONE=0, BIAS_UP=1, BIAS_DOWN=-1, BIAS_SIDEWAY=2 }; -enum MarketDir { DIR_NONE=0, DIR_UP=1, DIR_DOWN=-1 }; +enum HTFBias { BIAS_NONE=0, BIAS_UP=1, BIAS_DOWN=-1, BIAS_SIDEWAY=2 }; // Bias D1 +enum MarketDir { DIR_NONE=0, DIR_UP=1, DIR_DOWN=-1 }; // Hướng trend H1/M5 enum BlockReason { - BLOCK_NONE, BLOCK_SESSION, BLOCK_DAILY_LOSS, - BLOCK_BIAS_MISMATCH, BLOCK_NO_BIAS + BLOCK_NONE, BLOCK_SESSION, BLOCK_DAILY_LOSS, // Không chặn / chặn session / chặn daily loss + BLOCK_BIAS_MISMATCH, BLOCK_NO_BIAS // Bias không khớp H1 / không có bias }; enum FVGStatus { - FVG_PENDING, - FVG_TOUCHED, - FVG_USED + FVG_PENDING, // Chưa bị giá chạm + FVG_TOUCHED, // Đã chạm, chờ MSS + FVG_USED // Đã dùng (broken/expired/triggered) }; //==================================================== @@ -35,55 +33,55 @@ enum FVGStatus //==================================================== struct BiasContext { - HTFBias bias; - double rangeHigh, rangeLow; - datetime lastBarTime; + HTFBias bias; // UP/DOWN/SIDEWAY/NONE + double rangeHigh, rangeLow; // Vùng sideway (nếu có) + datetime lastBarTime; // Thời gian bar D1 đã xử lý (tránh cập nhật trùng) }; struct TFTrendContext { - MarketDir trend; - double h0, h1, l0, l1; - int idxH0, idxH1, idxL0, idxL1; - double keyLevel; - datetime lastBarTime; + MarketDir trend; // DIR_UP / DIR_DOWN / DIR_NONE + double h0, h1, l0, l1; // Giá 2 swing high, 2 swing low gần nhất + int idxH0, idxH1, idxL0, idxL1; // Chỉ số bar tương ứng + double keyLevel; // Mức key (l0 hoặc h0 tùy trend) + datetime lastBarTime; // Bar đã cập nhật (tránh cập nhật lại mỗi tick) - datetime lastMssTime; - double lastMssLevel; - MarketDir lastMssBreak; - double mssSLSwing; + datetime lastMssTime; // Thời gian MSS gần nhất (chỉ M5) + double lastMssLevel; // Giá entry MSS (tH0 hoặc tL0) + MarketDir lastMssBreak; // Hướng break (UP/DOWN) + double mssSLSwing; // Giá SL tương ứng (tL0 hoặc tH0) }; struct FVGRecord { - int id; - FVGStatus status; - int usedCase; - MarketDir direction; - double high, low, mid; - datetime createdTime; - datetime touchTime; - datetime usedTime; - MarketDir triggerTrendAtTouch; + int id; // ID FVG trong pool + FVGStatus status; // PENDING / TOUCHED / USED + int usedCase; // 0=expired, 1=broken, 2=triggered by MSS + MarketDir direction; // DIR_UP (bull FVG) / DIR_DOWN (bear FVG) + double high, low, mid; // Vùng gap và điểm giữa + datetime createdTime; // Thời điểm tạo FVG + datetime touchTime; // Thời điểm giá chạm (nếu TOUCHED) + datetime usedTime; // Thời điểm chuyển USED + MarketDir triggerTrendAtTouch; // Trend M5 lúc touch (debug) - datetime mssTime; - double mssEntry; - double mssSL; + datetime mssTime; // Thời điểm MSS (nếu usedCase==2) + double mssEntry; // Entry level từ MSS + double mssSL; // SL level từ MSS }; struct OrderPlan { - bool valid; - int direction; - double entry, stopLoss, takeProfit, lot; - int parentFVGId; + bool valid; // Plan có hợp lệ không + int direction; // +1 buy, -1 sell + double entry, stopLoss, takeProfit, lot; // Giá và khối lượng + int parentFVGId; // ID FVG gốc (để trace) }; struct DailyRiskContext { - double startBalance, currentBalance; - datetime dayStartTime; - bool limitHit; + double startBalance, currentBalance; // Balance đầu ngày và hiện tại + datetime dayStartTime; // Mốc đầu ngày D1 + bool limitHit; // Đã chạm max daily loss chưa }; #endif // EA_ICT_CL__STATE_MQH diff --git a/Experts/EA_ICT_CL/Swing.mqh b/Experts/EA_ICT_CL/Swing.mqh index 4ac0db2..8762d17 100644 --- a/Experts/EA_ICT_CL/Swing.mqh +++ b/Experts/EA_ICT_CL/Swing.mqh @@ -1,35 +1,31 @@ #ifndef EA_ICT_CL__SWING_MQH -#define EA_ICT_CL__SWING_MQH +#define EA_ICT_CL__SWING_MQH // Tránh include trùng -// Module: Swing -// Extracted from EA_ICT_CL.mq5 (Section 1 – Swing helpers). -// Depends on: -// - InpSwingRange (input) -// - MarketDir enum values DIR_UP/DIR_DOWN/DIR_NONE +// Module: Swing – tìm bar shift theo time, swing high/low, quét cấu trúc swing, suy trend //+------------------------------------------------------------------+ -//| MQL5 HELPER: iBarShift replacement | +//| MyBarShift: tìm chỉ số bar (series) ứng với thời gian time | //+------------------------------------------------------------------+ inline int MyBarShift(string symbol, ENUM_TIMEFRAMES tf, datetime time, bool exact = false) { datetime arr[]; - int maxCopy = MathMin(Bars(symbol, tf), 5000); + int maxCopy = MathMin(Bars(symbol, tf), 5000); // Giới hạn copy int copied = CopyTime(symbol, tf, 0, maxCopy, arr); if (copied <= 0) return -1; - for (int i = copied - 1; i >= 0; i--) + for (int i = copied - 1; i >= 0; i--) // Duyệt từ bar cũ → mới { if (arr[i] <= time) - return copied - 1 - i; + return copied - 1 - i; // Index trong series (0 = mới nhất) } - return exact ? -1 : copied - 1; + return exact ? -1 : copied - 1; // exact: không tìm thấy = -1; không exact: trả về bar xa nhất } inline bool IsSwingHighAt(ENUM_TIMEFRAMES tf, int i) { - double p = iHigh(_Symbol, tf, i); - for (int k = 1; k <= InpSwingRange; k++) - if (iHigh(_Symbol, tf, i-k) >= p || iHigh(_Symbol, tf, i+k) >= p) return false; + double p = iHigh(_Symbol, tf, i); // High tại bar i + for (int k = 1; k <= InpSwingRange; k++) // So với InpSwingRange bar mỗi bên + if (iHigh(_Symbol, tf, i-k) >= p || iHigh(_Symbol, tf, i+k) >= p) return false; // Có high cao hơn → không phải swing high return true; } @@ -46,20 +42,20 @@ inline bool ScanSwingStructure( double &h0, double &h1, int &idxH0, int &idxH1, double &l0, double &l1, int &idxL0, int &idxL1) { - int maxBar = MathMin(lookback, Bars(_Symbol, tf) - InpSwingRange - 2); - double highs[2]; int hiIdx[2]; int hc = 0; - double lows [2]; int loIdx[2]; int lc = 0; + int maxBar = MathMin(lookback, Bars(_Symbol, tf) - InpSwingRange - 2); // Giới hạn quét + double highs[2]; int hiIdx[2]; int hc = 0; // 2 swing high gần nhất + double lows [2]; int loIdx[2]; int lc = 0; // 2 swing low gần nhất - for (int i = InpSwingRange + 1; i <= maxBar; i++) + for (int i = InpSwingRange + 1; i <= maxBar; i++) // Từ bar gần hiện tại trở lại { if (hc < 2 && IsSwingHighAt(tf, i)) { highs[hc] = iHigh(_Symbol, tf, i); hiIdx[hc] = i; hc++; } if (lc < 2 && IsSwingLowAt (tf, i)) { lows [lc] = iLow (_Symbol, tf, i); loIdx[lc] = i; lc++; } - if (hc == 2 && lc == 2) break; + if (hc == 2 && lc == 2) break; // Đủ 2 high + 2 low thì dừng } - if (hc < 2 || lc < 2) return false; + if (hc < 2 || lc < 2) return false; // Không đủ swing - h0 = highs[0]; idxH0 = hiIdx[0]; - h1 = highs[1]; idxH1 = hiIdx[1]; + h0 = highs[0]; idxH0 = hiIdx[0]; // h0 = swing high gần nhất (index nhỏ) + h1 = highs[1]; idxH1 = hiIdx[1]; // h1 = swing high cũ hơn l0 = lows [0]; idxL0 = loIdx[0]; l1 = lows [1]; idxL1 = loIdx[1]; return true; @@ -70,10 +66,10 @@ inline void ResolveTrendFromSwings( double h0, double h1, double l0, double l1, MarketDir &trend, double &keyLevel) { - double c1 = iClose(_Symbol, tf, 1); - if (h0 > h1 && l0 > l1 && c1 > l0) { trend = DIR_UP; keyLevel = l0; } - else if (h0 < h1 && l0 < l1 && c1 < h0) { trend = DIR_DOWN; keyLevel = h0; } - else { trend = DIR_NONE; keyLevel = 0; } + double c1 = iClose(_Symbol, tf, 1); // Close bar 1 (gần nhất đã đóng) + if (h0 > h1 && l0 > l1 && c1 > l0) { trend = DIR_UP; keyLevel = l0; } // HH + HL, giá trên L0 → uptrend + else if (h0 < h1 && l0 < l1 && c1 < h0) { trend = DIR_DOWN; keyLevel = h0; } // LH + LL, giá dưới H0 → downtrend + else { trend = DIR_NONE; keyLevel = 0; } // Không rõ } #endif // EA_ICT_CL__SWING_MQH diff --git a/Experts/EA_ICT_CL/Trailing.mqh b/Experts/EA_ICT_CL/Trailing.mqh index c278f2e..52cb3ab 100644 --- a/Experts/EA_ICT_CL/Trailing.mqh +++ b/Experts/EA_ICT_CL/Trailing.mqh @@ -1,8 +1,7 @@ #ifndef EA_ICT_CL__TRAILING_MQH -#define EA_ICT_CL__TRAILING_MQH +#define EA_ICT_CL__TRAILING_MQH // Tránh include trùng -// Module: Trailing -// Put trailing/BE/partial-close logic here once you migrate it. +// Module: Trailing – placeholder cho trailing stop / breakeven / partial close (chưa implement) #endif // EA_ICT_CL__TRAILING_MQH diff --git a/Experts/EA_ICT_CL/Utils.mqh b/Experts/EA_ICT_CL/Utils.mqh index a4bd5bd..1164c3f 100644 --- a/Experts/EA_ICT_CL/Utils.mqh +++ b/Experts/EA_ICT_CL/Utils.mqh @@ -1,14 +1,13 @@ #ifndef EA_ICT_CL__UTILS_MQH -#define EA_ICT_CL__UTILS_MQH +#define EA_ICT_CL__UTILS_MQH // Tránh include trùng -// Module: Utils -// Put small, dependency-free helpers here (time, rounding, clamps, formatting). +// Module: Utils – helper không phụ thuộc logic EA (clamp, round, new bar) inline double ClampDouble(const double v, const double lo, const double hi) { - if (v < lo) return lo; - if (v > hi) return hi; - return v; + if (v < lo) return lo; // Nhỏ hơn min → trả về min + if (v > hi) return hi; // Lớn hơn max → trả về max + return v; // Nằm trong [lo,hi] → giữ nguyên } inline int ClampInt(const int v, const int lo, const int hi) @@ -20,13 +19,13 @@ inline int ClampInt(const int v, const int lo, const int hi) inline double RoundToStep(const double value, const double step) { - if (step <= 0.0) return value; - return MathRound(value / step) * step; + if (step <= 0.0) return value; // Step không hợp lệ → không làm tròn + return MathRound(value / step) * step; // Làm tròn theo bước step } inline bool IsNewBar(const datetime last_bar_time, const datetime current_bar_time) { - return (current_bar_time != 0 && current_bar_time != last_bar_time); + return (current_bar_time != 0 && current_bar_time != last_bar_time); // Bar mới khi thời gian bar đổi } #endif // EA_ICT_CL__UTILS_MQH