diff --git a/Experts/ICT.mq5 b/Experts/ICT.mq5 index 6782fb0..32f89c4 100644 --- a/Experts/ICT.mq5 +++ b/Experts/ICT.mq5 @@ -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_D1; // Khung thời gian cao -input ENUM_TIMEFRAMES MiddleTF = PERIOD_H1; // Khung thời gian trung bình (dùng để tìm FVG) -input ENUM_TIMEFRAMES LowTF = PERIOD_M5; // Khung thời gian thấp (Tìm điểm vào lệnh) +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) // --- multi-TF swing storage (slots) --- // slot 0 = HighTF, slot 1 = MiddleTF, slot 2 = LowTF @@ -1928,13 +1928,13 @@ bool CheckValidEntry( if(mssDirection == 0) return false; - // HTF phải có trend rõ ràng - if(htfTrend == 0) - { - if(PrintEntryLog) - Print("CheckValidEntry FAIL: HTF is SIDEWAY"); - return false; - } + // HTF phải có trend rõ ràng // TEST + // if(htfTrend == 0) + // { + // if(PrintEntryLog) + // Print("CheckValidEntry FAIL: HTF is SIDEWAY"); + // return false; + // } if(fvgIndex < 0 || fvgIndex >= MTF_FVG_count) { diff --git a/Experts/XAU_H1_M5_Pullback.mq5 b/Experts/XAU_H1_M5_Pullback.mq5 new file mode 100644 index 0000000..bb23fd0 --- /dev/null +++ b/Experts/XAU_H1_M5_Pullback.mq5 @@ -0,0 +1,332 @@ +//+------------------------------------------------------------------+ +//| ICT Early Trend Detection EA | +//| Structure + Early Key-Level Break (MSS-style) | +//| Clean Architecture – Meaningful Naming | +//| Author: Bell CW | +//+------------------------------------------------------------------+ +#property strict + +//==================================================== +// INPUT PARAMETERS +//==================================================== + +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 + +//==================================================== +// DATA STRUCTURE +//==================================================== + +struct TrendState +{ + int trendDirection; // 1 = uptrend, -1 = downtrend, 0 = neutral + + double latestSwingHigh; // Swing High mới nhất + double previousSwingHigh; // Swing High trước đó + + double latestSwingLow; // Swing Low mới nhất + double previousSwingLow; // Swing Low trước đó + + int latestSwingHighIndex; + int previousSwingHighIndex; + + int latestSwingLowIndex; + int previousSwingLowIndex; + + double currentKeyLevel; // Key level theo ICT structure + + bool earlyFlipUsedThisBar; // Đã dùng early flip trong HTF bar này chưa + datetime lastStructureUpdateTime; // Thời gian HTF bar cuối cùng đã xử lý +}; + +//==================================================== +// GLOBAL STATE +//==================================================== + +TrendState g_TrendState; +datetime g_LastTriggerBarTime = 0; + +//==================================================== +// INITIALIZATION +//==================================================== + +int OnInit() +{ + ZeroMemory(g_TrendState); + g_TrendState.trendDirection = 0; + g_TrendState.lastStructureUpdateTime = 0; + + Print("ICT Early Trend Detection EA initialized successfully"); + return INIT_SUCCEEDED; +} + +//==================================================== +// BAR UTILITY +//==================================================== + +bool IsNewBarFormed(ENUM_TIMEFRAMES timeframe, datetime &lastBarTime) +{ + datetime currentBarTime = iTime(_Symbol, timeframe, 0); + if(currentBarTime != lastBarTime) + { + lastBarTime = currentBarTime; + return true; + } + return false; +} + +//==================================================== +// SWING DETECTION (GENERIC) +//==================================================== + +bool IsSwingHighAtBar( + ENUM_TIMEFRAMES timeframe, + int barIndex +) +{ + double highPrice = iHigh(_Symbol, timeframe, barIndex); + + for(int offset = 1; offset <= SwingDetectionRange; offset++) + { + if(iHigh(_Symbol, timeframe, barIndex - offset) >= highPrice) return false; + if(iHigh(_Symbol, timeframe, barIndex + offset) >= highPrice) return false; + } + return true; +} + +bool IsSwingLowAtBar( + ENUM_TIMEFRAMES timeframe, + int barIndex +) +{ + double lowPrice = iLow(_Symbol, timeframe, barIndex); + + for(int offset = 1; offset <= SwingDetectionRange; offset++) + { + if(iLow(_Symbol, timeframe, barIndex - offset) <= lowPrice) return false; + if(iLow(_Symbol, timeframe, barIndex + offset) <= lowPrice) return false; + } + return true; +} + +//==================================================== +// STRUCTURE VALIDATION +//==================================================== + +bool HasConfirmedMarketStructure(const TrendState &state) +{ + return ( + state.latestSwingHigh > 0 && + state.previousSwingHigh > 0 && + state.latestSwingLow > 0 && + state.previousSwingLow > 0 + ); +} + +//==================================================== +// STRUCTURE UPDATE (TIMEFRAME AGNOSTIC) +//==================================================== + +void UpdateMarketStructure( + ENUM_TIMEFRAMES timeframe, + TrendState &state +) +{ + datetime currentStructureBarTime = iTime(_Symbol, timeframe, 0); + if(currentStructureBarTime == state.lastStructureUpdateTime) + return; + + state.lastStructureUpdateTime = currentStructureBarTime; + state.earlyFlipUsedThisBar = false; + + double detectedSwingHighs[2]; + double detectedSwingLows[2]; + int detectedSwingHighIndices[2]; + int detectedSwingLowIndices[2]; + + int highCount = 0; + int lowCount = 0; + + for(int barIndex = SwingDetectionRange + 1; + barIndex < StructureScanLookbackBars; + barIndex++) + { + if(highCount < 2 && IsSwingHighAtBar(timeframe, barIndex)) + { + detectedSwingHighs[highCount] = iHigh(_Symbol, timeframe, barIndex); + detectedSwingHighIndices[highCount] = barIndex; + highCount++; + } + + if(lowCount < 2 && IsSwingLowAtBar(timeframe, barIndex)) + { + detectedSwingLows[lowCount] = iLow(_Symbol, timeframe, barIndex); + detectedSwingLowIndices[lowCount] = barIndex; + lowCount++; + } + + if(highCount == 2 && lowCount == 2) + break; + } + + if(highCount < 2 || lowCount < 2) + return; + + state.latestSwingHigh = detectedSwingHighs[0]; + state.previousSwingHigh = detectedSwingHighs[1]; + state.latestSwingLow = detectedSwingLows[0]; + state.previousSwingLow = detectedSwingLows[1]; + + state.latestSwingHighIndex = detectedSwingHighIndices[0]; + state.previousSwingHighIndex = detectedSwingHighIndices[1]; + state.latestSwingLowIndex = detectedSwingLowIndices[0]; + state.previousSwingLowIndex = detectedSwingLowIndices[1]; + + // ===== STRUCTURE INTERPRETATION ===== + if(state.previousSwingHigh < state.latestSwingHigh && + state.previousSwingLow < state.latestSwingLow) + { + state.trendDirection = 1; + state.currentKeyLevel = state.latestSwingLow; + } + else if(state.previousSwingHigh > state.latestSwingHigh && + state.previousSwingLow > state.latestSwingLow) + { + state.trendDirection = -1; + state.currentKeyLevel = state.latestSwingHigh; + } +} + +//==================================================== +// EARLY TREND FLIP (MSS-STYLE) +//==================================================== + +void CheckForEarlyTrendFlip( + TrendState &state, + double currentPrice +) +{ + if(!HasConfirmedMarketStructure(state)) return; + if(state.earlyFlipUsedThisBar) return; + + if(state.trendDirection == 1 && + currentPrice < state.currentKeyLevel) + { + state.trendDirection = -1; + state.currentKeyLevel = state.latestSwingHigh; + state.earlyFlipUsedThisBar = true; + } + else if(state.trendDirection == -1 && + currentPrice > state.currentKeyLevel) + { + state.trendDirection = 1; + state.currentKeyLevel = state.latestSwingLow; + state.earlyFlipUsedThisBar = true; + } +} + +//==================================================== +// DRAWING UTILITIES +//==================================================== + +void DrawSwingLabel( + string objectName, + string labelText, + ENUM_TIMEFRAMES timeframe, + int barIndex, + double price, + color labelColor +) +{ + if(barIndex < 0) return; + + datetime barTime = iTime(_Symbol, timeframe, barIndex); + + ObjectDelete(0, objectName); + ObjectCreate(0, objectName, OBJ_TEXT, 0, barTime, price); + ObjectSetInteger(0, objectName, OBJPROP_COLOR, labelColor); + ObjectSetInteger(0, objectName, OBJPROP_FONTSIZE, 8); + ObjectSetString (0, objectName, OBJPROP_TEXT, labelText); +} + +void DrawMarketStructure( + ENUM_TIMEFRAMES timeframe, + const TrendState &state +) +{ + DrawSwingLabel("SWING_HIGH_LATEST", "H0", timeframe, + state.latestSwingHighIndex, + state.latestSwingHigh, clrLime); + + DrawSwingLabel("SWING_HIGH_PREVIOUS", "H1", timeframe, + state.previousSwingHighIndex, + state.previousSwingHigh, clrGreen); + + DrawSwingLabel("SWING_LOW_LATEST", "L0", timeframe, + state.latestSwingLowIndex, + state.latestSwingLow, clrRed); + + DrawSwingLabel("SWING_LOW_PREVIOUS", "L1", timeframe, + state.previousSwingLowIndex, + state.previousSwingLow, clrMaroon); +} + +void DrawTrendSummaryLabel(const TrendState &state) +{ + string labelName = "TREND_SUMMARY"; + ObjectDelete(0, labelName); + + string labelText = "TREND: NEUTRAL"; + color labelColor = clrGray; + + if(state.trendDirection == 1) + { + labelText = StringFormat( + "UPTREND - Key Level: %.2f%s", + state.currentKeyLevel, + state.earlyFlipUsedThisBar ? " -> EARLY FLIP" : "" + ); + labelColor = state.earlyFlipUsedThisBar ? clrOrange : clrLime; + } + else if(state.trendDirection == -1) + { + labelText = StringFormat( + "DOWNTREND - Key Level: %.2f%s", + state.currentKeyLevel, + state.earlyFlipUsedThisBar ? " -> EARLY FLIP" : "" + ); + labelColor = state.earlyFlipUsedThisBar ? clrOrange : clrRed; + } + + ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10); + ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20); + ObjectSetInteger(0, labelName, OBJPROP_COLOR, labelColor); + ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 9); + ObjectSetString (0, labelName, OBJPROP_TEXT, labelText); +} + +//==================================================== +// MAIN LOOP +//==================================================== + +void OnTick() +{ + if(!IsNewBarFormed(TriggerTimeframe, g_LastTriggerBarTime)) + return; + + UpdateMarketStructure(TrendTimeframe, g_TrendState); + + double currentBidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + CheckForEarlyTrendFlip(g_TrendState, currentBidPrice); + + DrawMarketStructure(TrendTimeframe, g_TrendState); + DrawTrendSummaryLabel(g_TrendState); + + // === Entry logic sẽ gắn tại đây === +} +//+------------------------------------------------------------------+