diff --git a/Experts/BellPriceActionWithEma50EA.ex5 b/Experts/BellPriceActionWithEma50EA.ex5 index a9ce463..a1f4e7e 100644 Binary files a/Experts/BellPriceActionWithEma50EA.ex5 and b/Experts/BellPriceActionWithEma50EA.ex5 differ diff --git a/Experts/BellPriceActionWithEma50EA.mq5 b/Experts/BellPriceActionWithEma50EA.mq5 index 9f42282..c2be5b9 100644 Binary files a/Experts/BellPriceActionWithEma50EA.mq5 and b/Experts/BellPriceActionWithEma50EA.mq5 differ diff --git a/Experts/ICT.mq5 b/Experts/ICT.mq5 index 32f89c4..381cbc9 100644 --- a/Experts/ICT.mq5 +++ b/Experts/ICT.mq5 @@ -39,9 +39,9 @@ input double trailingStopStartR = 2; // Advanced moving SL: bắt đầu kích input double trailOffsetR = 1.5; // Advanced moving SL: khoảng cách SL so với giá hiện tại // Cấu hình Swing -input int htfSwingRange = 3; // D1/H4: chắc trend -input int mtfSwingRange = 3; // H1: BOS rõ -input int ltfSwingRange = 4; // M5: giảm fake MSS +input int htfSwingRange = 1; // D1/H4: chắc trend +input int mtfSwingRange = 2; // H1: BOS rõ +input int ltfSwingRange = 2; // M5: giảm fake MSS input int MaxSwingKeep = 2; // Số đỉnh/đáy gần nhất cần lưu (bạn yêu cầu 2) // Struct pending entry (single slot) @@ -86,9 +86,9 @@ input bool PrintToExperts = false; // không dùng — logs đã được c // slot 0 = HTF, 1 = MTF, 2 = LTF int TrendTF[3]; // 1 = up, -1 = down, 0 = sideway / unknown -input ENUM_TIMEFRAMES HighTF = PERIOD_H4; // Khung thời gian cao -input ENUM_TIMEFRAMES MiddleTF = PERIOD_M15; // Khung thời gian trung bình (dùng để tìm FVG) -input ENUM_TIMEFRAMES LowTF = PERIOD_M1; // Khung thời gian thấp (Tìm điểm vào lệnh) +input ENUM_TIMEFRAMES HighTF = PERIOD_W1; // Khung thời gian cao +input ENUM_TIMEFRAMES MiddleTF = PERIOD_H4; // Khung thời gian trung bình (dùng để tìm FVG) +input ENUM_TIMEFRAMES LowTF = PERIOD_M15; // Khung thời gian thấp (Tìm điểm vào lệnh) // --- multi-TF swing storage (slots) --- // slot 0 = HighTF, slot 1 = MiddleTF, slot 2 = LowTF diff --git a/Experts/XAU_H1_M5_Pullback.mq5 b/Experts/XAU_H1_M5_Pullback.mq5 index bb23fd0..b1f51d8 100644 --- a/Experts/XAU_H1_M5_Pullback.mq5 +++ b/Experts/XAU_H1_M5_Pullback.mq5 @@ -9,13 +9,32 @@ //==================================================== // INPUT PARAMETERS //==================================================== - +input ENUM_TIMEFRAMES BiasTimeframe = PERIOD_D1; // TF bias cao hơn Trend TF input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H1; // Timeframe xác định trend input ENUM_TIMEFRAMES TriggerTimeframe = PERIOD_M5; // Timeframe kích hoạt logic input int SwingDetectionRange = 2; // Số nến trái/phải xác định swing input int StructureScanLookbackBars = 150; // Số nến quét structure +input int OBMaxAliveMinutes = 180; // OB sống tối đa 180 phút + +//==================================================== +// SESSION FILTER (SERVER TIME) +//==================================================== + +// London Session +input int LondonStartHour = 8; +input int LondonEndHour = 17; +input int LondonAvoidLastMin = 60; // tránh 60 phút cuối phiên + +// New York Session +input int NewYorkStartHour = 13; +input int NewYorkEndHour = 22; +input int NewYorkAvoidLastMin = 60; // tránh 60 phút cuối phiên + +input double RiskPercent = 1.0; // % vốn rủi ro mỗi lệnh +input double RiskReward = 2.0; // R:R = 1:2 + //==================================================== // DATA STRUCTURE //==================================================== @@ -45,20 +64,162 @@ struct TrendState //==================================================== // GLOBAL STATE //==================================================== - TrendState g_TrendState; datetime g_LastTriggerBarTime = 0; +enum ENUM_HTF_BIAS +{ + HTF_BIAS_NONE = 0, + HTF_BIAS_UP, + HTF_BIAS_DOWN, + HTF_BIAS_SIDEWAY +}; + +struct HTFBiasState +{ + ENUM_HTF_BIAS bias; + + double rangeHigh; + double rangeLow; + + datetime lastUpdateTime; +}; + +HTFBiasState g_HTFBias; + +struct OBWatchState +{ + bool active; // đang theo dõi OB + int direction; // 1 = buy, -1 = sell + bool used; // OB đã được sử dụng (entry xong) + double obHigh; + double obLow; + datetime createdTime; +}; + +OBWatchState g_OBWatch; + +struct OrderPlan +{ + bool valid; + + int direction; // 1 = buy, -1 = sell + double entry; + double stopLoss; + double takeProfit; + double lot; + int sourceSwingIndex; // cây nến tạo swing bị phá +}; + +struct TriggerTFStructure +{ + bool valid; + + int direction; // 1 = up, -1 = down + double lastSwingHigh; + double lastSwingLow; + + int lastSwingHighIndex; + int lastSwingLowIndex; + + double breakLevel; // level sẽ bị phá + double newKeyLevel; // 🔥 key level mới sau break +}; + +TriggerTFStructure g_TriggerTF; + +void UpdateHTFBias( + ENUM_TIMEFRAMES biasTf, + HTFBiasState &state +) { + datetime tf0Time = iTime(_Symbol, biasTf, 0); + if(tf0Time == state.lastUpdateTime) + return; + + state.lastUpdateTime = tf0Time; + + // --- Bar 1 và Bar 2 đã đóng + double b1Open = iOpen (_Symbol, biasTf, 1); + double b1Close = iClose(_Symbol, biasTf, 1); + double b1High = iHigh (_Symbol, biasTf, 1); + double b1Low = iLow (_Symbol, biasTf, 1); + + double b2High = iHigh (_Symbol, biasTf, 2); + double b2Low = iLow (_Symbol, biasTf, 2); + + state.rangeHigh = 0; + state.rangeLow = 0; + + // ===== UP BIAS ===== + if(b1Close > b2High) + { + state.bias = HTF_BIAS_UP; + return; + } + + // ===== DOWN BIAS ===== + if(b1Close < b2Low) + { + state.bias = HTF_BIAS_DOWN; + return; + } + + // ===== SIDEWAY: inside bar ===== + bool insideBar = + b1High <= b2High && + b1Low >= b2Low; + + if(insideBar) + { + state.bias = HTF_BIAS_SIDEWAY; + state.rangeHigh = b2High; + state.rangeLow = b2Low; + return; + } + + // ===== SIDEWAY: indecision ===== + bool indecision = + b1Close < b2High && + b1Close > b2Low; + + if(indecision) + { + state.bias = HTF_BIAS_SIDEWAY; + state.rangeHigh = b1High; + state.rangeLow = b1Low; + return; + } + + state.bias = HTF_BIAS_NONE; +} + +bool IsHTFBiasAligned( + ENUM_HTF_BIAS bias, + int trendDirection +) +{ + if(bias == HTF_BIAS_UP && trendDirection == 1) return true; + if(bias == HTF_BIAS_DOWN && trendDirection == -1) return true; + + return false; +} + //==================================================== // INITIALIZATION //==================================================== - int OnInit() { ZeroMemory(g_TrendState); g_TrendState.trendDirection = 0; g_TrendState.lastStructureUpdateTime = 0; + g_HTFBias.bias = HTF_BIAS_NONE; + g_HTFBias.rangeHigh = 0; + g_HTFBias.rangeLow = 0; + g_HTFBias.lastUpdateTime = 0; + + g_OBWatch.active = false; + Print("ICT Early Trend Detection EA initialized successfully"); return INIT_SUCCEEDED; } @@ -314,11 +475,522 @@ void DrawTrendSummaryLabel(const TrendState &state) // MAIN LOOP //==================================================== +//==================================================== +// SESSION GUARD +//==================================================== + +bool IsWithinSession( + int currentMinutes, + int sessionStartHour, + int sessionEndHour, + int avoidLastMinutes +) +{ + int sessionStartMin = sessionStartHour * 60; + int sessionEndMin = sessionEndHour * 60; + + // Trong phiên + if(currentMinutes < sessionStartMin || + currentMinutes >= sessionEndMin) + return false; + + // Tránh cuối phiên + if(currentMinutes >= (sessionEndMin - avoidLastMinutes)) + return false; + + return true; +} + +bool IsTradingSessionAllowed() +{ + datetime now = TimeCurrent(); + + MqlDateTime tm; + TimeToStruct(now, tm); + + int currentMinutes = tm.hour * 60 + tm.min; + + if(IsWithinSession(currentMinutes, + LondonStartHour, + LondonEndHour, + LondonAvoidLastMin)) + return true; + + if(IsWithinSession(currentMinutes, + NewYorkStartHour, + NewYorkEndHour, + NewYorkAvoidLastMin)) + return true; + + return false; +} + +bool HasPriceTouchedOB( + int direction, + double obHigh, + double obLow, + double price +) { + if(direction == 1) // buy + return price <= obHigh && price >= obLow; + + if(direction == -1) // sell + return price >= obLow && price <= obHigh; + + return false; +} + +bool FindTrendOrderBlock( + ENUM_TIMEFRAMES timeframe, + const TrendState &trend, + double &obHigh, + double &obLow, + int &obCandleIndex +) +{ + obHigh = 0; + obLow = 0; + obCandleIndex = -1; + + // ===== UP TREND ===== + if(trend.trendDirection == 1) + { + // Bắt đầu từ swing low gần nhất (key level) + for(int i = trend.latestSwingLowIndex - 1; i >= 1; i--) + { + double open = iOpen (_Symbol, timeframe, i); + double close = iClose(_Symbol, timeframe, i); + + // Nến giảm + if(close < open) + { + obHigh = iHigh(_Symbol, timeframe, i); + obLow = iLow (_Symbol, timeframe, i); + obCandleIndex = i; + return true; + } + } + } + + // ===== DOWN TREND ===== + if(trend.trendDirection == -1) + { + // Bắt đầu từ swing high gần nhất (key level) + for(int i = trend.latestSwingHighIndex - 1; i >= 1; i--) + { + double open = iOpen (_Symbol, timeframe, i); + double close = iClose(_Symbol, timeframe, i); + + // Nến tăng + if(close > open) + { + obHigh = iHigh(_Symbol, timeframe, i); + obLow = iLow (_Symbol, timeframe, i); + obCandleIndex = i; + return true; + } + } + } + + return false; +} + +void ResetOBWatch() +{ + g_OBWatch.active = false; + g_OBWatch.used = false; + g_OBWatch.direction = 0; + g_OBWatch.obHigh = 0; + g_OBWatch.obLow = 0; + g_OBWatch.createdTime = 0; +} + +bool IsOBInvalidated( + int direction, + double obHigh, + double obLow, + double price +) { + if(direction == 1) // buy + return price < obLow; + + if(direction == -1) // sell + return price > obHigh; + + return false; +} + +void ResetTriggerTFStructure() +{ + ZeroMemory(g_TriggerTF); +} + +bool IsTriggerTFStructureBreak( + const TriggerTFStructure &ts, + int trendDirection, + ENUM_TIMEFRAMES tf +) +{ + if(!ts.valid) return false; + + double close = iClose(_Symbol, tf, 1); + + if(trendDirection == 1) + return close > ts.breakLevel; + + if(trendDirection == -1) + return close < ts.breakLevel; + + return false; +} + +void UpdateTriggerTFStructure( + ENUM_TIMEFRAMES tf, + TriggerTFStructure &ts +) { + double sh, sl; + int shi, sli; + + bool foundHigh = false; + bool foundLow = false; + + for(int i = SwingDetectionRange + 1; i < StructureScanLookbackBars; i++) + { + if(!foundHigh && IsSwingHighAtBar(tf, i)) + { + sh = iHigh(_Symbol, tf, i); + shi = i; + foundHigh = true; + } + + if(!foundLow && IsSwingLowAtBar(tf, i)) + { + sl = iLow(_Symbol, tf, i); + sli = i; + foundLow = true; + } + + if(foundHigh && foundLow) break; + } + + if(!foundHigh || !foundLow) return; + + ts.lastSwingHigh = sh; + ts.lastSwingLow = sl; + ts.lastSwingHighIndex = shi; + ts.lastSwingLowIndex = sli; + + ts.direction = g_TrendState.trendDirection; + ts.valid = true; + + if(ts.direction == 1) + { + ts.breakLevel = ts.lastSwingHigh; + ts.newKeyLevel = ts.lastSwingLow; + } + else + { + ts.breakLevel = ts.lastSwingLow; + ts.newKeyLevel = ts.lastSwingHigh; + } +} + +void TryActivateOBWatch( + const TrendState &trend, + double currentPrice +) { + if(g_OBWatch.used) return; + double obHigh, obLow; + int obIndex; + + if(!FindTrendOrderBlock(TrendTimeframe, + trend, + obHigh, + obLow, + obIndex)) + return; + + if(!HasPriceTouchedOB(trend.trendDirection, + obHigh, + obLow, + currentPrice)) + return; + + g_OBWatch.active = true; + ResetTriggerTFStructure(); + g_OBWatch.direction = trend.trendDirection; + g_OBWatch.obHigh = obHigh; + g_OBWatch.obLow = obLow; + g_OBWatch.createdTime = TimeCurrent(); + + // Debug (optional) + PrintFormat( + "OB WATCH ACTIVATED | dir=%d | OB[%.2f - %.2f]", + g_OBWatch.direction, + g_OBWatch.obLow, + g_OBWatch.obHigh + ); +} + +void HandleOBWatching( + const TrendState &trend, + double currentPrice +) +{ + // 1️⃣ Bias hoặc trend không còn hợp lệ + if(!IsHTFBiasAligned(g_HTFBias.bias, trend.trendDirection)) + { + ResetOBWatch(); + return; + } + + // 2️⃣ OB quá hạn + if(IsOBExpired(g_OBWatch)) + { + ResetOBWatch(); + return; + } + + // 3️⃣ Giá đóng nến xuyên qua OB + if(IsOBCloseBreak(g_OBWatch.direction, + g_OBWatch.obHigh, + g_OBWatch.obLow)) + { + ResetOBWatch(); + return; + } + + // 4️⃣ OB bị phá sâu (tick safety) + if(IsOBInvalidated(g_OBWatch.direction, + g_OBWatch.obHigh, + g_OBWatch.obLow, + currentPrice)) + { + ResetOBWatch(); + return; + } + + // ================================================= + // 🔥 TRIGGER TF STRUCTURE LOGIC (CHỖ QUAN TRỌNG) + // ================================================= + if(!g_TriggerTF.valid) + { + UpdateTriggerTFStructure(TriggerTimeframe, g_TriggerTF); + } + + if(IsTriggerTFStructureBreak(g_TriggerTF, + trend.trendDirection, + TriggerTimeframe)) + { + OrderPlan plan = + BuildOrderPlanFromTriggerTF(g_TriggerTF, + TriggerTimeframe); + + if(plan.valid) + { + plan.lot = CalculateRiskLot(plan.entry, plan.stopLoss); + + if(plan.lot <= 0) + return; + + ExecuteOrder(plan); + + PrintFormat( + "🎯 ENTRY CONFIRMED | dir=%d | entry=%.2f | SL=%.2f | TP=%.2f | RR=1:%.1f", + plan.direction, + plan.entry, + plan.stopLoss, + plan.takeProfit, + RiskReward + ); + + // === SEND ORDER Ở ĐÂY === + // SendMarketOrder(plan) hoặc PlacePending(plan) + + MarkOBAsUsed(); + } + } + +} + +bool IsOBExpired(const OBWatchState &ob) +{ + if(!ob.active) + return true; + + int aliveSec = (int)(TimeCurrent() - ob.createdTime); + return aliveSec > OBMaxAliveMinutes * 60; +} + +void MarkOBAsUsed() +{ + g_OBWatch.used = true; + g_OBWatch.active = false; +} + +bool IsOBCloseBreak( + int direction, + double obHigh, + double obLow +) { + double close = iClose(_Symbol, TriggerTimeframe, 1); + + if(direction == 1) // buy OB + return close < obLow; + + if(direction == -1) // sell OB + return close > obHigh; + + return false; +} + +bool IsTriggerKeyLevelBreak( + const TrendState &trend, + ENUM_TIMEFRAMES triggerTf +) { + double close = iClose(_Symbol, triggerTf, 1); + + if(trend.trendDirection == 1) + return close > trend.currentKeyLevel; + + if(trend.trendDirection == -1) + return close < trend.currentKeyLevel; + + return false; +} + +OrderPlan BuildOrderPlanFromTriggerTF( + const TriggerTFStructure &ts, + ENUM_TIMEFRAMES tf +) +{ + OrderPlan plan; + ZeroMemory(plan); + + double entryPrice = iClose(_Symbol, tf, 1); + + plan.valid = true; + plan.direction = ts.direction; + plan.entry = entryPrice; + + double riskPoints; + + // ===== BUY ===== + if(ts.direction == 1) + { + plan.stopLoss = MathMin(ts.newKeyLevel, g_OBWatch.obLow); + + riskPoints = plan.entry - plan.stopLoss; + if(riskPoints <= 0) + { + plan.valid = false; + return plan; + } + + plan.takeProfit = plan.entry + riskPoints * RiskReward; + } + // ===== SELL ===== + else + { + plan.stopLoss = MathMax(ts.newKeyLevel, g_OBWatch.obHigh); + + riskPoints = plan.stopLoss - plan.entry; + if(riskPoints <= 0) + { + plan.valid = false; + return plan; + } + + plan.takeProfit = plan.entry - riskPoints * RiskReward; + } + + return plan; +} + +double CalculateRiskLot( + double entryPrice, + double stopLossPrice +) { + double balance = AccountInfoDouble(ACCOUNT_BALANCE); + double riskMoney = balance * RiskPercent / 100.0; + + double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); + double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); + + if(tickValue <= 0 || tickSize <= 0) + return 0; + + double slPoints = MathAbs(entryPrice - stopLossPrice) / _Point; + if(slPoints <= 0) + return 0; + + double costPerLot = slPoints * (tickValue / tickSize); + double lot = riskMoney / costPerLot; + + // ===== Normalize theo broker ===== + double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); + double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); + double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); + + lot = MathMax(minLot, MathMin(lot, maxLot)); + lot = NormalizeDouble(lot, (int)MathLog10(1.0 / lotStep)); + + return lot; +} + +void ExecuteOrder(const OrderPlan &plan) +{ + if(!plan.valid) return; + + MqlTradeRequest req; + MqlTradeResult res; + ZeroMemory(req); + ZeroMemory(res); + + req.action = TRADE_ACTION_DEAL; + req.symbol = _Symbol; + req.volume = plan.lot; + req.type = plan.direction == 1 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; + req.price = plan.direction == 1 ? + SymbolInfoDouble(_Symbol, SYMBOL_ASK) : + SymbolInfoDouble(_Symbol, SYMBOL_BID); + + req.sl = plan.stopLoss; + req.tp = plan.takeProfit; + req.deviation= 5; + req.magic = 202501; + req.comment = "ICT_OB_Trigger"; + + OrderSend(req, res); + + if(res.retcode != TRADE_RETCODE_DONE) + { + Print("❌ OrderSend failed: ", res.retcode); + } + else + { + PrintFormat( + "✅ ORDER SENT | lot=%.2f | entry=%.2f | SL=%.2f | TP=%.2f", + plan.lot, + plan.entry, + plan.stopLoss, + plan.takeProfit + ); + } +} + void OnTick() { if(!IsNewBarFormed(TriggerTimeframe, g_LastTriggerBarTime)) return; + if(!IsTradingSessionAllowed()) + return; + + // ===== HTF BIAS ===== + UpdateHTFBias(BiasTimeframe, g_HTFBias); + + // ===== TREND STRUCTURE ===== UpdateMarketStructure(TrendTimeframe, g_TrendState); double currentBidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); @@ -327,6 +999,17 @@ void OnTick() DrawMarketStructure(TrendTimeframe, g_TrendState); DrawTrendSummaryLabel(g_TrendState); - // === Entry logic sẽ gắn tại đây === + // ===== ENTRY FILTER ===== + if(!IsHTFBiasAligned(g_HTFBias.bias, + g_TrendState.trendDirection)) + return; + + // ===== ORDER BLOCK FLOW ===== + if(!g_OBWatch.active) { + TryActivateOBWatch(g_TrendState, currentBidPrice); + } else { + HandleOBWatching(g_TrendState, currentBidPrice); + } } + //+------------------------------------------------------------------+