From 82331ebdd89b9cf00f7a314b29c79615d20049aa Mon Sep 17 00:00:00 2001 From: Bell Date: Wed, 3 Dec 2025 19:39:38 +0700 Subject: [PATCH] EA ICT version 1 --- Experts/EA_ICT.mq5 | 441 +++++++++++++++++++++++++++++++++++++++++++++ Experts/EA_ICT.txt | 18 ++ 2 files changed, 459 insertions(+) create mode 100644 Experts/EA_ICT.mq5 create mode 100644 Experts/EA_ICT.txt diff --git a/Experts/EA_ICT.mq5 b/Experts/EA_ICT.mq5 new file mode 100644 index 0000000..b89a0f5 --- /dev/null +++ b/Experts/EA_ICT.mq5 @@ -0,0 +1,441 @@ +//+------------------------------------------------------------------+ +//| EA ICT-style: Daily Bias -> H1 FVG -> M5 pullback entries | +//| - Xác định Daily bias theo yêu cầu của bạn | +//| - Tìm FVG trên H1 thuận chiều với daily bias (phương pháp đơn giản) +//| - Khi giá hồi về H1 FVG, chuyển xuống M5: tìm M5 FVG + MSS (đơn giản) +//| - Đặt BuyLimit / SellLimit tại M5 FVG, SL tính theo đáy/đỉnh pullback +//| - Lot được tính sao cho rủi ro entry->SL = 1% equity (tùy biến RiskPerTrade) +//| - TP = entry + 3 * (entry - SL) (R:R = 1:3) +//+------------------------------------------------------------------+ +#property strict +#include +CTrade trade; + +//--- Inputs +input double RiskPerTrade = 1.0; // % equity risk per trade (mặc định 1%) +input int MagicNumber = 33333; +input int H1_FVG_lookback = 200; // bars to scan for H1 FVG +input int M5_lookback = 200; // bars to scan for M5 FVG/MSS +input double MinADX = 10.0; // optional ADX filter (không bắt buộc) +input int ADXPeriod = 14; +input double MaxAcceptableSpread = 200; // points +input double RR = 3.0; // Risk:Reward multiplier (TP = RR * risk distance) // points + +//--- Internal structs +struct Zone { double top; double bottom; int from_index; int to_index; }; + +enum DailyBias { BIAS_UNKNOWN=0, BIAS_UP=1, BIAS_DOWN=-1 }; + +//--- Utility forward declarations +DailyBias DetermineDailyBias(); +int FindH1FVGs(DailyBias bias, Zone &foundZone); +int FindM5FVGAtZone(Zone &h1zone, DailyBias bias, Zone &m5zone); +bool DetectM5MSS(int &mssType, double &mssPrice); // returns 1 for bullish MSS (break to upside), -1 for bearish +double CalculateLotForRisk(double entryPrice, double stopPrice); +void PlaceLimitOrder(int side, double price, double sl, double tp, double lot); + +//+------------------------------------------------------------------+ +int OnInit() +{ + Print("EA ICT-style initialized"); + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +void OnTick() +{ + // chỉ xử lý trên M5 khi có bar mới + static datetime lastBarTime=0; + datetime t = iTime(_Symbol, PERIOD_M5, 0); + if(t==lastBarTime) return; + lastBarTime = t; + + // spread check + double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); + double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); + double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); + double spreadPts = (ask-bid)/point; + if(spreadPts > MaxAcceptableSpread) { PrintFormat("Spread too high: %.1f pts", spreadPts); return; } + + // 1) xác định daily bias + DailyBias bias = DetermineDailyBias(); + if(bias==BIAS_UNKNOWN) { Print("Daily bias unknown -> skip"); return; } + PrintFormat("Daily bias = %d", bias); + + // 2) tìm H1 FVG thuận chiều với bias + Zone h1zone; bool foundH1 = (FindH1FVGs(bias, h1zone) > 0); + if(!foundH1) { Print("No H1 FVG found in bias direction"); return; } + PrintFormat("Found H1 FVG: top=%.5f bottom=%.5f from=%d to=%d", h1zone.top, h1zone.bottom, h1zone.from_index, h1zone.to_index); + + // 3) khi giá hiện tại đã từng (hoặc đang) thuộc H1 FVG region -> tìm M5 FVG inside that H1 zone + Zone m5zone; bool foundM5 = (FindM5FVGAtZone(h1zone,bias,m5zone) > 0); + if(!foundM5) { Print("No M5 FVG inside H1 FVG -> skip"); return; } + PrintFormat("Found M5 FVG: top=%.5f bottom=%.5f", m5zone.top, m5zone.bottom); + + // 4) xác nhận MSS trên M5 (đơn giản: break of structure recent swing) + int mssType=0; double mssPrice=0; + if(!DetectM5MSS(mssType,mssPrice)) { Print("No M5 MSS detected -> skip"); return; } + PrintFormat("M5 MSS type=%d price=%.5f", mssType, mssPrice); + + // 5) chuẩn bị entry: nếu bias up -> place buy limit at bottom of m5zone; if bias down -> sell limit at top + double entryPrice = (bias==BIAS_UP) ? m5zone.bottom : m5zone.top; + + // compute SL: if buy -> SL = lowest low of the pullback swing on M5 (we approximate by minimum low in zone window) + double sl=0,tp=0; + if(bias==BIAS_UP) + { + // find lowest low in recent M5 bars inside/near the m5zone range + double swingLow = DBL_MAX; + for(int i=0;i= entryPrice) { Print("Computed SL >= entry -> skip"); return; } + double dist = entryPrice - sl; + tp = entryPrice + RR * dist; // R:R=1:3 + } + else + { + double swingHigh = -DBL_MAX; + for(int i=0;i swingHigh) swingHigh = high; } + sl = swingHigh + 5*point; + if(sl <= entryPrice) { Print("Computed SL <= entry -> skip"); return; } + double dist = sl - entryPrice; + tp = entryPrice - RR * dist; + } + + // 6) tính lot theo risk = RiskPerTrade% equity cho khoảng cách entry->SL + double lot = CalculateLotForRisk(entryPrice, sl); + if(lot <= 0) { Print("Calculated lot <=0 -> skip"); return; } + + // 7) đặt pending limit + int side = (bias==BIAS_UP) ? POSITION_TYPE_BUY : POSITION_TYPE_SELL; + // Check if EA already has active position or pending order (only 1 at a time) + if(HasActiveOrders()) + { + Print("Already have an active position or pending order for this EA -> skip placing another"); + } + else + { + PlaceLimitOrder(side, entryPrice, sl, tp, lot); + } +} + +//+------------------------------------------------------------------+ +DailyBias DetermineDailyBias() +{ + // theo yêu cầu: xét 2 cây D1 đã đóng gần nhất (không tính nến hiện tại -> shift 1 và 2) + // nếu D[1].close > D[2].high -> up. nếu D[1].close < D[2].low -> down. nếu D[1] nằm trong D[2] thì bỏ qua D[1] và dùng D2 & D3 + + int idx1 = 1; // D[1] + int idx2 = 2; // D[2] + int tries = 0; + while(tries < 5) + { + double close1 = iClose(_Symbol,PERIOD_D1,idx1); + double high2 = iHigh(_Symbol,PERIOD_D1,idx2); + double low2 = iLow(_Symbol,PERIOD_D1,idx2); + + if(close1 > high2) return BIAS_UP; + if(close1 < low2) return BIAS_DOWN; + + // close1 inside candle2 -> shift window down (use D2 & D3) + idx1++; idx2++; tries++; + // ensure there are bars + if(idx2 > 200) break; + } + return BIAS_UNKNOWN; +} + +//+------------------------------------------------------------------+ +int FindH1FVGs(DailyBias bias, Zone &foundZone) +{ + // Phương pháp đơn giản: + // Tìm gap "Fair Value Gap" kiểu: giữa 2 candle (i and i+2) có khoảng trống + // Bullish FVG (hỗ trợ): low[i] > high[i+2] -> vùng FVG là (high[i+2], low[i]) + // Bearish FVG (kháng cự): high[i] < low[i+2] -> vùng FVG là (high[i], low[i+2]) + + int limit = H1_FVG_lookback; + for(int i=1;i high_i2 + SymbolInfoDouble(_Symbol,SYMBOL_POINT)*0.0) // allow equality + { + foundZone.top = low_i; + foundZone.bottom = high_i2; + foundZone.from_index = i+2; + foundZone.to_index = i; + return(1); + } + } + else if(bias==BIAS_DOWN) + { + if(high_i < low_i2 - SymbolInfoDouble(_Symbol,SYMBOL_POINT)*0.0) + { + foundZone.top = high_i2; // caution: for clarity we set top>bottom + foundZone.bottom = low_i; + // normalize so top>bottom + double t = MathMax(high_i, low_i2); + double b = MathMin(high_i, low_i2); + foundZone.top = t; foundZone.bottom = b; + foundZone.from_index = i+2; + foundZone.to_index = i; + return(1); + } + } + } + return(0); +} + +//+------------------------------------------------------------------+ +int FindM5FVGAtZone(Zone &h1zone, DailyBias bias, Zone &m5zone) +{ + // Scan M5 recent bars. We look for small FVGs within the price range of H1 FVG + int limit = M5_lookback; + for(int i=1;i high_i2) + { + double top = low_i; + double bottom = high_i2; + // check overlap with H1 zone + if(bottom <= h1zone.top && top >= h1zone.bottom) + { + m5zone.top = top; m5zone.bottom = bottom; return 1; + } + } + } + else if(bias==BIAS_DOWN) + { + if(high_i < low_i2) + { + double top = low_i2; double bottom = high_i; + if(bottom <= h1zone.top && top >= h1zone.bottom) + { + m5zone.top = top; m5zone.bottom = bottom; return 1; + } + } + } + } + return 0; +} + +//+------------------------------------------------------------------+ +bool DetectM5MSS(int &mssType, double &mssPrice) +{ + // Rất đơn giản: nếu price vừa break swing high -> bullish MSS (return 1) + // nếu price just break swing low -> bearish MSS (return -1) + // Implementation: compute last 3 swing highs and lows and check current candle + + // get last swing high (local maxima) and swing low (local minima) in M5 + double lastSwingHigh = -DBL_MAX; int idxHigh=-1; + double lastSwingLow = DBL_MAX; int idxLow=-1; + int look = 50; + for(int i=2;i lastSwingHigh) { lastSwingHigh=h; idxHigh=i; } + if(l < lastSwingLow) { lastSwingLow=l; idxLow=i; } + } + + double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); + double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); + + // bullish MSS detection: current price (ask) > lastSwingHigh + if(ask > lastSwingHigh) + { + mssType = 1; mssPrice = lastSwingHigh; return true; + } + if(bid < lastSwingLow) + { + mssType = -1; mssPrice = lastSwingLow; return true; + } + return false; +} + +//+------------------------------------------------------------------+ +double CalculateLotForRisk(double entryPrice, double stopPrice) +{ + // Tính lot sao cho khoảng cách entry->SL tương ứng RiskPerTrade% equity + double equity = AccountInfoDouble(ACCOUNT_EQUITY); + double riskMoney = equity * (RiskPerTrade/100.0); + + double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); + double tickValue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); + double tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE); + if(point<=0 || tickValue<=0 || tickSize<=0) { Print("Invalid symbol params for lot calc"); return 0; } + + double stopPoints = MathAbs(entryPrice - stopPrice)/point; + if(stopPoints <= 0) return 0; + + double valuePerPoint = tickValue * (point / tickSize); + double rawLot = riskMoney / (stopPoints * valuePerPoint); + + double minLot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); + double lotStep= SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); + double maxLot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); + if(lotStep<=0) lotStep=0.01; + + double n = MathFloor(rawLot / lotStep); + double lot = n * lotStep; + if(lot < minLot) lot = minLot; + if(lot > maxLot) lot = maxLot; + + lot = NormalizeDouble(lot,2); + PrintFormat("CalcLot: entry=%.5f stop=%.5f stopPts=%.1f rawLot=%.4f finalLot=%.2f", entryPrice, stopPrice, stopPoints, rawLot, lot); + return lot; +} + +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| HasActiveOrders: kiểm tra xem EA đã có position hoặc pending order | +//| - Trả về true nếu tồn tại position mở hoặc pending order cùng MagicNumber trên symbol +//+------------------------------------------------------------------+ +bool HasActiveOrders() +{ + // check open positions + for(int i=0;i= 0; i--) + { + string name = ObjectName(0, i); + if(StringFind(name, "ICT_ENTRY_") == 0 || + StringFind(name, "ICT_SL_") == 0 || + StringFind(name, "ICT_TP_") == 0) + { + ObjectDelete(0, name); + } + } + + Print("EA deinitialized — cleaned objects."); +} +//+------------------------------------------------------------------+ + +// NOTES / CAVEATS: +// - Đây là bản mẫu triển khai logic theo mô tả của bạn, nhưng có nhiều điểm được đơn giản hóa +// (phát hiện FVG và MSS là dạng heuristic đơn giản). Nên backtest kỹ và điều chỉnh +// - Bạn có thể muốn vẽ các zone (OBJ_RECTANGLE) để debug và quan sát H1/M5 FVG +// - Tinh chỉnh: lookback, cách xác định FVG, buffer SL, ADX filter, điều kiện trước khi đặt lệnh +// - EA hiện đặt 1 pending limit khi điều kiện thỏa. Nó không kiểm tra overlap với các pending/positions hiện tại +// - Hãy chạy trên demo/backtest trước khi dùng real + diff --git a/Experts/EA_ICT.txt b/Experts/EA_ICT.txt new file mode 100644 index 0000000..4cea920 --- /dev/null +++ b/Experts/EA_ICT.txt @@ -0,0 +1,18 @@ +Bước 1: Xác định Daily bias: +- Xét 2 cây nến D1 đã đóng cửa gần nhất (Không tính nến hiện tại) ++ Nếu nến D[1] đóng cửa cao hơn giá đóng cửa cao nhất của nến D[2] ngày hôm trước (D[2] tính cả râu nến) -> xu hướng ngày hôm nay (D[0]) uptrend ++ Nếu nến D[1] đóng cửa thấp hơn giá đóng cửa thấp nhất của nến D[2] ngày hôm trước (D[2] tính cả râu nến) -> xu hướng ngày hôm nay (D[0]) downtrend ++ Nếu nến D[1] đóng cửa bên trong nến D[2], không cao hơn và không thấp hơn -> Bỏ qua D[1], dùng nến D[2] và D[3] để xác định xu hướng ngày hôm nay theo cách tương tự + +Bước 2: Xác định FVG của khung H1 thuận chiều với daily bias và đánh dấu lại +- Nếu Daily Bias là uptrend -> Tìm uptrend FVG +- Nếu Daily Bias là downtrend -> Tìm downtrend FVG + +Bước 3: Vào lệnh pullback khi có xác nhận tín hiệu tại khung m5 khi giá hồi về H1 FVG ở bước 2 như sau: +- Xu hướng hồi của khung M5 bị phá vỡ (Xuất hiện market structure shift, key level bị phá) +- Khung m5 xuất hiện M5 FVG thuận chiều với H1 trend +- Vào 1 lệnh buy limit hoặc sell limit thuận chiều xu hướng của H1 tại M5 FVG +- Tính toán lot size để đặt SL: ++ Nếu buy limit: Tính toán lot size vào lệnh sao cho khoảng cách từ entry tới đáy thấp nhất của sóng hồi m5 vừa bằng 1% equity ++ Nếu sell limit: Tính toán lot size vào lệnh sao cho khoảng cách từ entry tới đỉnh cao nhất của sóng hồi m5 vừa bằng 1% equity +- R:R = 1:3 \ No newline at end of file