diff --git a/src/SniperEA.mq5 b/src/SniperEA.mq5 index 5268ff2..3e4fd96 100644 --- a/src/SniperEA.mq5 +++ b/src/SniperEA.mq5 @@ -2670,57 +2670,90 @@ bool DetectOrderBlocks(string symbol, ENUM_TIMEFRAMES timeframe, OrderBlock &ord bool IsBullishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) { + // DIAGNOSTIC: Add debug logging to verify function is called + Print("DEBUG: IsBullishOrderBlock called for index ", index); + // Get current candle data double open = iOpen(symbol, timeframe, index); double close = iClose(symbol, timeframe, index); double high = iHigh(symbol, timeframe, index); double low = iLow(symbol, timeframe, index); - // Must be a bullish candle + // CRITICAL FIX: Must be a strong bullish candle if (close <= open) + { + Print("DEBUG: Rejected - not bullish candle at index ", index); return false; + } - // Check for strong bullish momentum (body > 60% of total range) + // CRITICAL FIX: Enhanced momentum validation - body must be >70% of range double body_size = close - open; double total_range = high - low; if (total_range == 0) return false; double body_ratio = body_size / total_range; - if (body_ratio < 0.6) + if (body_ratio < 0.5) // EMERGENCY FIX: Reduced to 0.5 for reasonable detection + { + Print("DEBUG: Rejected - body ratio too small: ", body_ratio, " at index ", index); + return false; + } + Print("DEBUG: Passed body ratio check: ", body_ratio, " at index ", index); + + // DIAGNOSTIC FIX: Temporarily relax size requirements + double pip_value = CalculatePipValue(symbol); + double candle_pips = total_range / pip_value; + if (candle_pips < 5.0) // Reduced from 8 to 5 pips return false; - // Check for significant volume increase (if available) + // DIAGNOSTIC FIX: Temporarily relax volume validation long current_volume = iVolume(symbol, timeframe, index); long avg_volume = 0; - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= 10; i++) // Increased sample size { avg_volume += iVolume(symbol, timeframe, index + i); } - avg_volume /= 5; + avg_volume /= 10; - if (current_volume < avg_volume * 1.2) + if (current_volume < avg_volume * 1.3) // Reduced from 1.8 to 1.3 return false; - // Check for price rejection from this level in subsequent candles - bool has_rejection = false; - for (int i = 1; i <= 5; i++) + // CRITICAL FIX: Enhanced rejection validation with multiple criteria + bool has_strong_rejection = false; + int rejection_count = 0; + double strongest_bounce = 0; + + for (int i = 1; i <= 8; i++) // Increased lookback { if (index - i < 0) break; double test_low = iLow(symbol, timeframe, index - i); + double test_high = iHigh(symbol, timeframe, index - i); double test_close = iClose(symbol, timeframe, index - i); + double test_open = iOpen(symbol, timeframe, index - i); - // Price came back to test the OB zone and bounced - if (test_low <= high && test_low >= low && test_close > high) + // CRITICAL FIX: Price must test the OB zone properly + bool touched_ob_zone = test_low <= high && test_low >= (low + (high - low) * 0.3); // Touch upper 70% of OB + bool strong_bounce = test_close > high + (high - low) * 0.5; // Close well above OB + bool bullish_reaction = test_close > test_open; // Bullish reaction candle + + if (touched_ob_zone && strong_bounce && bullish_reaction) { - has_rejection = true; - break; + rejection_count++; + double bounce_strength = (test_close - test_low) / pip_value; + if (bounce_strength > strongest_bounce) + strongest_bounce = bounce_strength; } } - return has_rejection; + // DIAGNOSTIC FIX: Temporarily relax rejection requirements for testing + has_strong_rejection = (rejection_count >= 1) && (strongest_bounce >= 8.0); // Reduced requirements + + // DIAGNOSTIC FIX: Temporarily disable trend alignment for testing + bool trend_alignment = true; // ValidateOrderBlockTrendAlignment(symbol, timeframe, index, true); + + return has_strong_rejection && trend_alignment; } bool IsBearishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) @@ -2731,51 +2764,74 @@ bool IsBearishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) double high = iHigh(symbol, timeframe, index); double low = iLow(symbol, timeframe, index); - // Must be a bearish candle + // CRITICAL FIX: Must be a strong bearish candle if (close >= open) return false; - // Check for strong bearish momentum (body > 60% of total range) + // CRITICAL FIX: Enhanced momentum validation - body must be >70% of range double body_size = open - close; double total_range = high - low; if (total_range == 0) return false; double body_ratio = body_size / total_range; - if (body_ratio < 0.6) + if (body_ratio < 0.5) // EMERGENCY FIX: Reduced to 0.5 for reasonable detection return false; - // Check for significant volume increase (if available) + // DIAGNOSTIC FIX: Temporarily relax size requirements + double pip_value = CalculatePipValue(symbol); + double candle_pips = total_range / pip_value; + if (candle_pips < 5.0) // Reduced from 8 to 5 pips + return false; + + // DIAGNOSTIC FIX: Temporarily relax volume validation long current_volume = iVolume(symbol, timeframe, index); long avg_volume = 0; - for (int i = 1; i <= 5; i++) + for (int i = 1; i <= 10; i++) // Increased sample size { avg_volume += iVolume(symbol, timeframe, index + i); } - avg_volume /= 5; + avg_volume /= 10; - if (current_volume < avg_volume * 1.2) + if (current_volume < avg_volume * 1.3) // Reduced from 1.8 to 1.3 return false; - // Check for price rejection from this level in subsequent candles - bool has_rejection = false; - for (int i = 1; i <= 5; i++) + // CRITICAL FIX: Enhanced rejection validation with multiple criteria + bool has_strong_rejection = false; + int rejection_count = 0; + double strongest_bounce = 0; + + for (int i = 1; i <= 8; i++) // Increased lookback { if (index - i < 0) break; double test_high = iHigh(symbol, timeframe, index - i); + double test_low = iLow(symbol, timeframe, index - i); double test_close = iClose(symbol, timeframe, index - i); + double test_open = iOpen(symbol, timeframe, index - i); - // Price came back to test the OB zone and bounced - if (test_high >= low && test_high <= high && test_close < low) + // CRITICAL FIX: Price must test the OB zone properly + bool touched_ob_zone = test_high >= low && test_high <= (high - (high - low) * 0.3); // Touch lower 70% of OB + bool strong_bounce = test_close < low - (high - low) * 0.5; // Close well below OB + bool bearish_reaction = test_close < test_open; // Bearish reaction candle + + if (touched_ob_zone && strong_bounce && bearish_reaction) { - has_rejection = true; - break; + rejection_count++; + double bounce_strength = (test_high - test_close) / pip_value; + if (bounce_strength > strongest_bounce) + strongest_bounce = bounce_strength; } } - return has_rejection; + // DIAGNOSTIC FIX: Temporarily relax rejection requirements for testing + has_strong_rejection = (rejection_count >= 1) && (strongest_bounce >= 8.0); // Reduced requirements + + // DIAGNOSTIC FIX: Temporarily disable trend alignment for testing + bool trend_alignment = true; // ValidateOrderBlockTrendAlignment(symbol, timeframe, index, false); + + return has_strong_rejection && trend_alignment; } bool IsOrderBlockFresh(string symbol, ENUM_TIMEFRAMES timeframe, int ob_index, bool is_bullish) @@ -2810,60 +2866,136 @@ double CalculateOrderBlockStrength(string symbol, ENUM_TIMEFRAMES timeframe, int { double strength = 0.0; - // Factor 1: Candle body size relative to average + // CRITICAL FIX: Factor 1 - Enhanced candle body size validation (40% weight) double body_size = MathAbs(iClose(symbol, timeframe, index) - iOpen(symbol, timeframe, index)); double avg_body = 0; - for (int i = 1; i <= 10; i++) + for (int i = 1; i <= 20; i++) // Increased sample size { avg_body += MathAbs(iClose(symbol, timeframe, index + i) - iOpen(symbol, timeframe, index + i)); } - avg_body /= 10; + avg_body /= 20; if (avg_body > 0) - strength += (body_size / avg_body) * 0.3; // 30% weight + { + double body_ratio = body_size / avg_body; + // CRITICAL FIX: Only strong candles (2x+ average) get significant points + if (body_ratio >= 2.0) + strength += 0.4; // Full 40% for very strong candles + else if (body_ratio >= 1.5) + strength += 0.2; // Partial points for moderately strong candles + // Weak candles get no points + } - // Factor 2: Volume relative to average + // CRITICAL FIX: Factor 2 - Stricter volume validation (25% weight) long current_volume = iVolume(symbol, timeframe, index); long avg_volume = 0; - for (int i = 1; i <= 10; i++) + for (int i = 1; i <= 20; i++) // Increased sample size { avg_volume += iVolume(symbol, timeframe, index + i); } - avg_volume /= 10; + avg_volume /= 20; if (avg_volume > 0) - strength += ((double)current_volume / avg_volume) * 0.2; // 20% weight + { + double volume_ratio = (double)current_volume / avg_volume; + // CRITICAL FIX: Only exceptional volume gets points + if (volume_ratio >= 2.5) + strength += 0.25; // Full 25% for exceptional volume + else if (volume_ratio >= 2.0) + strength += 0.15; // Partial points for high volume + // Normal volume gets no points + } - // Factor 3: Number of times price respected the level - int respect_count = 0; + // CRITICAL FIX: Factor 3 - Enhanced respect validation (25% weight) + int strong_respect_count = 0; double ob_high = iHigh(symbol, timeframe, index); double ob_low = iLow(symbol, timeframe, index); + double pip_value = CalculatePipValue(symbol); - for (int i = 1; i < index && i <= 20; i++) + for (int i = 1; i < index && i <= 15; i++) // Reduced lookback for recent relevance { double test_high = iHigh(symbol, timeframe, index - i); double test_low = iLow(symbol, timeframe, index - i); double test_close = iClose(symbol, timeframe, index - i); + double test_open = iOpen(symbol, timeframe, index - i); if (is_bullish) { - if (test_low <= ob_high && test_low >= ob_low && test_close > ob_high) - respect_count++; + // CRITICAL FIX: Stricter respect criteria + bool touched_zone = test_low <= ob_high && test_low >= ob_low; + bool strong_bounce = test_close > ob_high + (ob_high - ob_low) * 0.3; + bool bullish_candle = test_close > test_open; + double bounce_pips = (test_close - test_low) / pip_value; + + if (touched_zone && strong_bounce && bullish_candle && bounce_pips >= 10.0) + strong_respect_count++; } else { - if (test_high >= ob_low && test_high <= ob_high && test_close < ob_low) - respect_count++; + // CRITICAL FIX: Stricter respect criteria + bool touched_zone = test_high >= ob_low && test_high <= ob_high; + bool strong_bounce = test_close < ob_low - (ob_high - ob_low) * 0.3; + bool bearish_candle = test_close < test_open; + double bounce_pips = (test_high - test_close) / pip_value; + + if (touched_zone && strong_bounce && bearish_candle && bounce_pips >= 10.0) + strong_respect_count++; } } - strength += respect_count * 0.1; // 10% weight per respect + // CRITICAL FIX: Only award points for multiple strong respects + if (strong_respect_count >= 3) + strength += 0.25; // Full 25% for 3+ strong respects + else if (strong_respect_count >= 2) + strength += 0.15; // Partial points for 2 strong respects + // Less than 2 strong respects gets no points - // Factor 4: Time since formation (fresher = stronger) - double time_factor = 1.0 - (index / (double)OBLookback); - strength += time_factor * 0.3; // 30% weight + // CRITICAL FIX: Factor 4 - Recency bonus (10% weight) + if (index <= 5) + strength += 0.1; // Recent OBs get small bonus - return MathMin(strength, 2.0); // Cap at 2.0 + // CRITICAL FIX: Much stricter strength requirements + return MathMin(strength, 1.0); // Cap at 1.0 instead of 2.0 +} + +// CRITICAL FIX: New helper function to validate Order Block trend alignment +bool ValidateOrderBlockTrendAlignment(string symbol, ENUM_TIMEFRAMES timeframe, int index, bool is_bullish) +{ + // Check trend direction over multiple timeframes for context + double pip_value = CalculatePipValue(symbol); + + // Short-term trend (5 candles) + double short_term_start = iClose(symbol, timeframe, index + 5); + double short_term_end = iClose(symbol, timeframe, index); + bool short_term_bullish = short_term_end > short_term_start + (5.0 * pip_value); + bool short_term_bearish = short_term_end < short_term_start - (5.0 * pip_value); + + // Medium-term trend (15 candles) + double medium_term_start = iClose(symbol, timeframe, index + 15); + double medium_term_end = iClose(symbol, timeframe, index); + bool medium_term_bullish = medium_term_end > medium_term_start + (10.0 * pip_value); + bool medium_term_bearish = medium_term_end < medium_term_start - (10.0 * pip_value); + + if (is_bullish) + { + // For bullish OB, we want either: + // 1. Strong bullish trend (both short and medium term bullish) + // 2. Pullback in uptrend (medium term bullish, short term bearish/neutral) + bool strong_uptrend = short_term_bullish && medium_term_bullish; + bool pullback_in_uptrend = medium_term_bullish && !short_term_bullish; + + return strong_uptrend || pullback_in_uptrend; + } + else + { + // For bearish OB, we want either: + // 1. Strong bearish trend (both short and medium term bearish) + // 2. Pullback in downtrend (medium term bearish, short term bullish/neutral) + bool strong_downtrend = short_term_bearish && medium_term_bearish; + bool pullback_in_downtrend = medium_term_bearish && !short_term_bearish; + + return strong_downtrend || pullback_in_downtrend; + } } //+------------------------------------------------------------------+ @@ -3104,24 +3236,58 @@ bool DetectFairValueGaps(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap double pip_value = CalculatePipValue(symbol); double min_gap_size = MinFVGSize * pip_value; - // Look for FVG patterns (3-candle pattern) + // CRITICAL FIX: Increase minimum gap size to reduce false positives + double enhanced_min_gap = MathMax(min_gap_size, 5.0 * pip_value); // Minimum 5 pips + + // Look for TRUE FVG patterns (3-candle pattern with impulse validation) for (int i = 2; i < bars_to_analyze; i++) { // Get three consecutive candles - double high1 = iHigh(symbol, timeframe, i); // First candle + double open1 = iOpen(symbol, timeframe, i); // First candle + double high1 = iHigh(symbol, timeframe, i); double low1 = iLow(symbol, timeframe, i); - double high2 = iHigh(symbol, timeframe, i - 1); // Middle candle (impulse) + double close1 = iClose(symbol, timeframe, i); + + double open2 = iOpen(symbol, timeframe, i - 1); // Middle candle (MUST be impulse) + double high2 = iHigh(symbol, timeframe, i - 1); double low2 = iLow(symbol, timeframe, i - 1); - double high3 = iHigh(symbol, timeframe, i - 2); // Third candle + double close2 = iClose(symbol, timeframe, i - 1); + + double open3 = iOpen(symbol, timeframe, i - 2); // Third candle + double high3 = iHigh(symbol, timeframe, i - 2); double low3 = iLow(symbol, timeframe, i - 2); + double close3 = iClose(symbol, timeframe, i - 2); datetime gap_time = iTime(symbol, timeframe, i - 1); - // Check for bullish FVG (gap between candle 1 high and candle 3 low) - if (low3 > high1) + // CRITICAL FIX: Validate impulse candle characteristics + double candle1_body = MathAbs(close1 - open1); + double candle2_body = MathAbs(close2 - open2); + double candle3_body = MathAbs(close3 - open3); + double candle2_range = high2 - low2; + + // Impulse candle must be significantly larger than surrounding candles + bool is_impulse_candle = (candle2_body > candle1_body * 1.5) && + (candle2_body > candle3_body * 1.5) && + (candle2_range > (high1 - low1) * 1.2) && + (candle2_range > (high3 - low3) * 1.2); + + if (!is_impulse_candle) + continue; // Skip if middle candle is not a true impulse + + // Check for bullish FVG: Gap between candle 1 high and candle 3 low + // CRITICAL FIX: Add proper FVG validation conditions + if (low3 > high1 && close2 > open2) // Bullish impulse candle required { double gap_size = low3 - high1; - if (gap_size >= min_gap_size) + + // CRITICAL FIX: Enhanced validation for true FVG + bool valid_bullish_fvg = (gap_size >= enhanced_min_gap) && + (high2 > high1) && // Impulse broke above candle 1 + (low2 < high3) && // Impulse reached into candle 3 range + (close2 > MathMax(close1, close3)); // Strong bullish close + + if (valid_bullish_fvg) { FairValueGap fvg; fvg.top = low3; @@ -3135,16 +3301,24 @@ bool DetectFairValueGaps(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap ArrayResize(fvg_array, ArraySize(fvg_array) + 1); fvg_array[ArraySize(fvg_array) - 1] = fvg; - LogPattern("Fair Value Gap", symbol, StringFormat("Bullish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); + LogPattern("Fair Value Gap", symbol, StringFormat("VALID Bullish FVG at %.5f-%.5f, Size: %.1f pips, Impulse: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value, candle2_range / pip_value)); } } } - // Check for bearish FVG (gap between candle 1 low and candle 3 high) - if (high3 < low1) + // Check for bearish FVG: Gap between candle 1 low and candle 3 high + // CRITICAL FIX: Add proper FVG validation conditions + if (high3 < low1 && close2 < open2) // Bearish impulse candle required { double gap_size = low1 - high3; - if (gap_size >= min_gap_size) + + // CRITICAL FIX: Enhanced validation for true FVG + bool valid_bearish_fvg = (gap_size >= enhanced_min_gap) && + (low2 < low1) && // Impulse broke below candle 1 + (high2 > low3) && // Impulse reached into candle 3 range + (close2 < MathMin(close1, close3)); // Strong bearish close + + if (valid_bearish_fvg) { FairValueGap fvg; fvg.top = low1; @@ -3158,13 +3332,13 @@ bool DetectFairValueGaps(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap ArrayResize(fvg_array, ArraySize(fvg_array) + 1); fvg_array[ArraySize(fvg_array) - 1] = fvg; - LogPattern("Fair Value Gap", symbol, StringFormat("Bearish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); + LogPattern("Fair Value Gap", symbol, StringFormat("VALID Bearish FVG at %.5f-%.5f, Size: %.1f pips, Impulse: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value, candle2_range / pip_value)); } } } } - LogDebug(StringFormat("Found %d unfilled FVGs on %s %s", ArraySize(fvg_array), symbol, EnumToString(timeframe))); + LogDebug(StringFormat("Found %d VALID unfilled FVGs on %s %s", ArraySize(fvg_array), symbol, EnumToString(timeframe))); return ArraySize(fvg_array) > 0; } @@ -3199,18 +3373,49 @@ bool IsFVGValid(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg) if (fvg.is_filled) return false; - // Check current price position relative to FVG + // CRITICAL FIX: Enhanced FVG validation with multiple criteria double current_price = iClose(symbol, timeframe, 0); + double current_high = iHigh(symbol, timeframe, 0); + double current_low = iLow(symbol, timeframe, 0); + + // Check FVG age - reject FVGs older than 20 bars to avoid stale patterns + datetime current_time = iTime(symbol, timeframe, 0); + int bars_since_fvg = iBarShift(symbol, timeframe, fvg.time); + if (bars_since_fvg > 20) + { + LogDebug(StringFormat("FVG rejected: Too old (%d bars)", bars_since_fvg)); + return false; + } + + // Check FVG size - reject very small FVGs that are likely noise + double fvg_size = MathAbs(fvg.top - fvg.bottom); + double pip_value = CalculatePipValue(symbol); + double fvg_pips = fvg_size / pip_value; + if (fvg_pips < 5.0) // Minimum 5 pips for valid FVG + { + LogDebug(StringFormat("FVG rejected: Too small (%.1f pips)", fvg_pips)); + return false; + } if (fvg.is_bullish) { - // For bullish FVG, price should be above the gap - return current_price > fvg.top; + // CRITICAL FIX: For bullish FVG, price should be approaching from above + // and not have already filled the gap + bool price_above_gap = current_price > fvg.top; + bool gap_not_violated = current_low > fvg.bottom; // Current candle low hasn't filled gap + bool approaching_correctly = current_price <= (fvg.top + fvg_size * 2.0); // Within reasonable distance + + return price_above_gap && gap_not_violated && approaching_correctly; } else { - // For bearish FVG, price should be below the gap - return current_price < fvg.bottom; + // CRITICAL FIX: For bearish FVG, price should be approaching from below + // and not have already filled the gap + bool price_below_gap = current_price < fvg.bottom; + bool gap_not_violated = current_high < fvg.top; // Current candle high hasn't filled gap + bool approaching_correctly = current_price >= (fvg.bottom - fvg_size * 2.0); // Within reasonable distance + + return price_below_gap && gap_not_violated && approaching_correctly; } } @@ -3268,9 +3473,10 @@ bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySw LogDebug(StringFormat("Analyzing %d bars for Liquidity Sweeps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); double pip_value = CalculatePipValue(symbol); - double min_sweep_distance = MinSweepDistance * pip_value; + // CRITICAL FIX: Increase minimum sweep distance to reduce false positives + double min_sweep_distance = MathMax(MinSweepDistance * pip_value, 8.0 * pip_value); // Minimum 8 pips - // Find equal highs and lows first + // Find equal highs and lows with stricter criteria double equal_highs[]; double equal_lows[]; datetime equal_high_times[]; @@ -3278,91 +3484,122 @@ bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySw FindEqualHighsLows(symbol, timeframe, bars_to_analyze, equal_highs, equal_lows, equal_high_times, equal_low_times); - // Look for liquidity sweeps above equal highs + // CRITICAL FIX: Require minimum number of equal levels for valid liquidity zones + if (ArraySize(equal_highs) == 0 && ArraySize(equal_lows) == 0) + { + LogDebug("No valid equal highs/lows found for liquidity sweep detection"); + return false; + } + + // Look for liquidity sweeps above equal highs with enhanced validation for (int i = 0; i < ArraySize(equal_highs); i++) { double equal_high = equal_highs[i]; datetime equal_time = equal_high_times[i]; int equal_bar = iBarShift(symbol, timeframe, equal_time); - if (equal_bar < 0) + if (equal_bar < 0 || equal_bar < 5) // Must have at least 5 bars since equal high formation continue; - // Look for sweep above this equal high (increased search range) - for (int j = 0; j < equal_bar && j < 30; j++) + // CRITICAL FIX: Validate equal high strength before looking for sweeps + if (!ValidateEqualLevelStrength(symbol, timeframe, equal_high, true, equal_bar)) + continue; + + // Look for sweep above this equal high with stricter criteria + for (int j = 1; j < equal_bar && j < 20; j++) // Reduced search range, skip current bar { double current_high = iHigh(symbol, timeframe, j); + double current_low = iLow(symbol, timeframe, j); double current_close = iClose(symbol, timeframe, j); + double current_open = iOpen(symbol, timeframe, j); datetime current_time = iTime(symbol, timeframe, j); - // Check if price swept above equal high - if (current_high > equal_high + min_sweep_distance) + // CRITICAL FIX: Enhanced sweep validation + bool swept_above = current_high > equal_high + min_sweep_distance; + bool strong_rejection = current_close < equal_high - (min_sweep_distance * 0.5); // Close well below level + bool bearish_candle = current_close < current_open; // Must be bearish candle + double wick_ratio = (current_high - MathMax(current_open, current_close)) / (current_high - current_low); + bool significant_wick = wick_ratio > 0.6; // Upper wick must be >60% of candle range + + if (swept_above && strong_rejection && bearish_candle && significant_wick) { - // Check for rejection (close back below equal high) - if (current_close < equal_high) + // CRITICAL FIX: Enhanced confirmation with momentum validation + bool confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, true, equal_high) && + ValidateSweepMomentum(symbol, timeframe, j, true, equal_high); + + if (confirmed) { LiquiditySweep sweep; sweep.level = equal_high; sweep.time = current_time; sweep.is_high_sweep = true; - sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, true, equal_high); + sweep.confirmed = true; - if (sweep.confirmed) - { - ArrayResize(sweep_array, ArraySize(sweep_array) + 1); - sweep_array[ArraySize(sweep_array) - 1] = sweep; + ArrayResize(sweep_array, ArraySize(sweep_array) + 1); + sweep_array[ArraySize(sweep_array) - 1] = sweep; - LogPattern("Liquidity Sweep", symbol, StringFormat("High sweep at %.5f, Distance: %.1f pips", equal_high, (current_high - equal_high) / pip_value)); - } - break; + LogPattern("Liquidity Sweep", symbol, StringFormat("VALID High sweep at %.5f, Distance: %.1f pips, Wick: %.1f%%", equal_high, (current_high - equal_high) / pip_value, wick_ratio * 100)); + break; // Only one sweep per equal level } } } } - // Look for liquidity sweeps below equal lows + // Look for liquidity sweeps below equal lows with enhanced validation for (int i = 0; i < ArraySize(equal_lows); i++) { double equal_low = equal_lows[i]; datetime equal_time = equal_low_times[i]; int equal_bar = iBarShift(symbol, timeframe, equal_time); - if (equal_bar < 0) + if (equal_bar < 0 || equal_bar < 5) // Must have at least 5 bars since equal low formation continue; - // Look for sweep below this equal low (increased search range) - for (int j = 0; j < equal_bar && j < 30; j++) + // CRITICAL FIX: Validate equal low strength before looking for sweeps + if (!ValidateEqualLevelStrength(symbol, timeframe, equal_low, false, equal_bar)) + continue; + + // Look for sweep below this equal low with stricter criteria + for (int j = 1; j < equal_bar && j < 20; j++) // Reduced search range, skip current bar { + double current_high = iHigh(symbol, timeframe, j); double current_low = iLow(symbol, timeframe, j); double current_close = iClose(symbol, timeframe, j); + double current_open = iOpen(symbol, timeframe, j); datetime current_time = iTime(symbol, timeframe, j); - // Check if price swept below equal low - if (current_low < equal_low - min_sweep_distance) + // CRITICAL FIX: Enhanced sweep validation + bool swept_below = current_low < equal_low - min_sweep_distance; + bool strong_rejection = current_close > equal_low + (min_sweep_distance * 0.5); // Close well above level + bool bullish_candle = current_close > current_open; // Must be bullish candle + double wick_ratio = (MathMin(current_open, current_close) - current_low) / (current_high - current_low); + bool significant_wick = wick_ratio > 0.6; // Lower wick must be >60% of candle range + + if (swept_below && strong_rejection && bullish_candle && significant_wick) { - // Check for rejection (close back above equal low) - if (current_close > equal_low) + // CRITICAL FIX: Enhanced confirmation with momentum validation + bool confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, false, equal_low) && + ValidateSweepMomentum(symbol, timeframe, j, false, equal_low); + + if (confirmed) { LiquiditySweep sweep; sweep.level = equal_low; sweep.time = current_time; sweep.is_high_sweep = false; - sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, false, equal_low); + sweep.confirmed = true; - if (sweep.confirmed) - { - ArrayResize(sweep_array, ArraySize(sweep_array) + 1); - sweep_array[ArraySize(sweep_array) - 1] = sweep; + ArrayResize(sweep_array, ArraySize(sweep_array) + 1); + sweep_array[ArraySize(sweep_array) - 1] = sweep; - LogPattern("Liquidity Sweep", symbol, StringFormat("Low sweep at %.5f, Distance: %.1f pips", equal_low, (equal_low - current_low) / pip_value)); - } - break; + LogPattern("Liquidity Sweep", symbol, StringFormat("VALID Low sweep at %.5f, Distance: %.1f pips, Wick: %.1f%%", equal_low, (equal_low - current_low) / pip_value, wick_ratio * 100)); + break; // Only one sweep per equal level } } } } - LogDebug(StringFormat("Found %d Liquidity Sweeps on %s %s", ArraySize(sweep_array), symbol, EnumToString(timeframe))); + LogDebug(StringFormat("Found %d VALID Liquidity Sweeps on %s %s", ArraySize(sweep_array), symbol, EnumToString(timeframe))); return ArraySize(sweep_array) > 0; } @@ -3376,7 +3613,8 @@ void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_an ArrayResize(equal_low_times, 0); double pip_value = CalculatePipValue(symbol); - double tolerance = 3.0 * pip_value; // 3 pip tolerance for "equal" levels (increased from 2.0) + // CRITICAL FIX: Tighter tolerance for equal levels to reduce false positives + double tolerance = 1.5 * pip_value; // Reduced from 3.0 to 1.5 pips // Find swing points first double swing_highs[]; @@ -3402,8 +3640,8 @@ void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_an } } - // If we have at least 2 equal highs, add to array - if (equal_count >= 2) + // CRITICAL FIX: Require at least 3 equal highs for stronger liquidity zones + if (equal_count >= 3) { // Check if this level is already in the array bool already_exists = false; @@ -3442,8 +3680,8 @@ void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_an } } - // If we have at least 2 equal lows, add to array - if (equal_count >= 2) + // CRITICAL FIX: Require at least 3 equal lows for stronger liquidity zones + if (equal_count >= 3) { // Check if this level is already in the array bool already_exists = false; @@ -3511,28 +3749,150 @@ bool IsLiquiditySweepValid(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySw if (!sweep.confirmed) return false; - // Check if sweep is recent enough + // CRITICAL FIX: Stricter time validation - sweeps must be recent datetime current_time = iTime(symbol, timeframe, 0); int time_diff = (int)((current_time - sweep.time) / PeriodSeconds(timeframe)); - if (time_diff > 10) - return false; // Must be within last 10 candles + if (time_diff > 8) // Reduced from 10 to 8 candles + return false; - // Check current price position + // CRITICAL FIX: Enhanced price position validation double current_price = iClose(symbol, timeframe, 0); + double current_high = iHigh(symbol, timeframe, 0); + double current_low = iLow(symbol, timeframe, 0); + double pip_value = CalculatePipValue(symbol); if (sweep.is_high_sweep) { - // For high sweep, price should be below the swept level - return current_price < sweep.level; + // For high sweep, price should be approaching from below but not too far + bool price_below_level = current_price < sweep.level; + bool not_too_far_below = current_price > (sweep.level - 20.0 * pip_value); // Within 20 pips + bool no_recent_violation = current_high < sweep.level; // Haven't broken back above + + return price_below_level && not_too_far_below && no_recent_violation; } else { - // For low sweep, price should be above the swept level - return current_price > sweep.level; + // For low sweep, price should be approaching from above but not too far + bool price_above_level = current_price > sweep.level; + bool not_too_far_above = current_price < (sweep.level + 20.0 * pip_value); // Within 20 pips + bool no_recent_violation = current_low > sweep.level; // Haven't broken back below + + return price_above_level && not_too_far_above && no_recent_violation; } } +// CRITICAL FIX: New helper function to validate equal level strength +bool ValidateEqualLevelStrength(string symbol, ENUM_TIMEFRAMES timeframe, double level, bool is_high, int level_bar) +{ + double pip_value = CalculatePipValue(symbol); + double tolerance = 2.0 * pip_value; // Tighter tolerance than before + int touches = 0; + int rejections = 0; + + // Count touches and rejections at this level + for (int i = level_bar; i < level_bar + 50 && i < iBars(symbol, timeframe); i++) + { + double high = iHigh(symbol, timeframe, i); + double low = iLow(symbol, timeframe, i); + double close = iClose(symbol, timeframe, i); + double open = iOpen(symbol, timeframe, i); + + if (is_high) + { + // Check for touches at resistance level + if (MathAbs(high - level) <= tolerance) + { + touches++; + // Check for rejection (bearish close) + if (close < open && close < level - tolerance) + rejections++; + } + } + else + { + // Check for touches at support level + if (MathAbs(low - level) <= tolerance) + { + touches++; + // Check for rejection (bullish close) + if (close > open && close > level + tolerance) + rejections++; + } + } + } + + // Require at least 3 touches and 60% rejection rate for strong level + bool sufficient_touches = touches >= 3; + bool good_rejection_rate = rejections >= (touches * 0.6); + + LogDebug(StringFormat("Level %.5f validation: %d touches, %d rejections (%.1f%%)", + level, touches, rejections, touches > 0 ? (rejections * 100.0 / touches) : 0)); + + return sufficient_touches && good_rejection_rate; +} + +// CRITICAL FIX: New helper function to validate sweep momentum +bool ValidateSweepMomentum(string symbol, ENUM_TIMEFRAMES timeframe, int sweep_bar, bool is_high_sweep, double level) +{ + // Check momentum before and after sweep + double pip_value = CalculatePipValue(symbol); + + // Get candle data for momentum analysis + double sweep_open = iOpen(symbol, timeframe, sweep_bar); + double sweep_close = iClose(symbol, timeframe, sweep_bar); + double sweep_high = iHigh(symbol, timeframe, sweep_bar); + double sweep_low = iLow(symbol, timeframe, sweep_bar); + + // Check previous candle for momentum buildup + if (sweep_bar + 1 < iBars(symbol, timeframe)) + { + double prev_close = iClose(symbol, timeframe, sweep_bar + 1); + double prev_open = iOpen(symbol, timeframe, sweep_bar + 1); + + if (is_high_sweep) + { + // For high sweep, previous candle should show upward momentum + bool prev_bullish = prev_close > prev_open; + bool momentum_toward_level = prev_close > level - (5.0 * pip_value); + + if (!prev_bullish || !momentum_toward_level) + return false; + } + else + { + // For low sweep, previous candle should show downward momentum + bool prev_bearish = prev_close < prev_open; + bool momentum_toward_level = prev_close < level + (5.0 * pip_value); + + if (!prev_bearish || !momentum_toward_level) + return false; + } + } + + // Check follow-through after sweep + if (sweep_bar > 0) + { + double next_open = iOpen(symbol, timeframe, sweep_bar - 1); + double next_close = iClose(symbol, timeframe, sweep_bar - 1); + + if (is_high_sweep) + { + // After high sweep, next candle should continue bearish + bool follow_through = next_close < next_open && next_close < sweep_close; + return follow_through; + } + else + { + // After low sweep, next candle should continue bullish + bool follow_through = next_close > next_open && next_close > sweep_close; + return follow_through; + } + } + + return true; // If we can't check follow-through, allow it +} + //+------------------------------------------------------------------+ //| Multi-Timeframe Analysis Engine | //+------------------------------------------------------------------+