diff --git a/src/SniperEA.mq5 b/src/SniperEA.mq5 index 8c9cf26..d1f08b0 100644 --- a/src/SniperEA.mq5 +++ b/src/SniperEA.mq5 @@ -49,11 +49,12 @@ input int MaxPositionsPerSymbol = 3; // Maximum positions input group "=== Pattern Detection ===" input int OBLookback = 20; // Order Block lookback candles input double MinFVGSize = 3.0; // Minimum FVG size in pips -input double MinSweepDistance = 5.0; // Minimum sweep distance in pips +input double MinSweepDistance = 3.0; // Minimum sweep distance in pips (reduced from 5.0) input int BOSConfirmationCandles = 3; // BOS confirmation within candles -input int SwingLookback = 10; // Swing high/low lookback period +input int SwingLookback = 5; // Swing high/low lookback period (reduced from 10) input double OBStrengthFilter = 0.5; // Order Block strength filter (0-1) input bool RequireMultiTFConfirmation = true; // Require multi-timeframe confirmation +input int MinConfluenceCount = 3; // Minimum confluence criteria required (3/4 instead of 4/4) input group "=== Fibonacci Settings ===" input bool EnableFibonacci = true; // Enable Fibonacci retracement analysis input ENUM_FIBONACCI_MODE FibonacciMode = FIBONACCI_AS_FILTER; // Fibonacci integration mode @@ -380,6 +381,7 @@ double g_fibonacci_percentages[5] = {23.6, 38.2, 50.0, 61.8, 78.6}; // Standard //--- Function declarations bool ConfirmBOS(string symbol, ENUM_TIMEFRAMES timeframe, int break_bar, bool is_bullish, double level); +bool ValidateFlexibleConfluence(string symbol, bool is_bullish, MarketStructureData &m1_data); //--- Phase 4: Advanced Risk Management Function Declarations bool InitializePhase4RiskManagement(); @@ -1798,303 +1800,28 @@ bool AnalyzeBullishSetup(string symbol) ArraySize(m1_data.order_blocks), ArraySize(m1_data.fair_value_gaps), ArraySize(m1_data.bos_events), ArraySize(m1_data.liquidity_sweeps))); - // Step 1: Find valid liquidity sweep (low sweep for bullish setup) - LogDebug(StringFormat("Step 1: Looking for valid low sweep (total sweeps: %d)", ArraySize(m1_data.liquidity_sweeps))); - LiquiditySweep valid_sweep; - bool sweep_found = false; - - for (int i = 0; i < ArraySize(m1_data.liquidity_sweeps); i++) - { - LogDebug(StringFormat("Checking sweep %d: is_high_sweep=%s, confirmed=%s", - i, m1_data.liquidity_sweeps[i].is_high_sweep ? "true" : "false", - m1_data.liquidity_sweeps[i].confirmed ? "true" : "false")); - - if (!m1_data.liquidity_sweeps[i].is_high_sweep && - IsLiquiditySweepValid(symbol, PERIOD_M1, m1_data.liquidity_sweeps[i])) - { - valid_sweep = m1_data.liquidity_sweeps[i]; - sweep_found = true; - LogDebug(StringFormat("Valid low sweep found at level %.5f", valid_sweep.level)); - break; - } - } - - if (!sweep_found) - { - LogDebug(StringFormat("BLOCKING CONDITION: No valid low sweep found for bullish setup on %s", symbol)); - return false; - } - - // Step 2: Find opposite direction BOS (bullish BOS after low sweep) - BreakOfStructure valid_bos; - bool bos_found = false; - - for (int i = 0; i < ArraySize(m1_data.bos_events); i++) - { - if (m1_data.bos_events[i].is_bullish && - m1_data.bos_events[i].confirmed && - m1_data.bos_events[i].time > valid_sweep.time) // BOS must be after sweep - { - valid_bos = m1_data.bos_events[i]; - bos_found = true; - break; - } - } - - if (!bos_found) - { - LogDebug(StringFormat("No valid bullish BOS found after low sweep on %s", symbol)); - return false; - } - - // Step 3: Find valid FVG between BOS and current price - FairValueGap valid_fvg; - bool fvg_found = false; - - for (int i = 0; i < ArraySize(m1_data.fair_value_gaps); i++) - { - if (m1_data.fair_value_gaps[i].is_bullish && - IsFVGValid(symbol, PERIOD_M1, m1_data.fair_value_gaps[i]) && - m1_data.fair_value_gaps[i].time > valid_bos.time) // FVG must be after BOS - { - valid_fvg = m1_data.fair_value_gaps[i]; - fvg_found = true; - break; - } - } - - if (!fvg_found) - { - LogDebug(StringFormat("No valid bullish FVG found after BOS on %s", symbol)); - return false; - } - - // Step 4: Find fresh bullish Order Block - OrderBlock valid_ob; - bool ob_found = false; - - for (int i = 0; i < ArraySize(m1_data.order_blocks); i++) - { - if (m1_data.order_blocks[i].is_bullish && - m1_data.order_blocks[i].is_fresh && - m1_data.order_blocks[i].strength >= OBStrengthFilter && - m1_data.order_blocks[i].time > valid_fvg.time) // OB must be after FVG - { - valid_ob = m1_data.order_blocks[i]; - ob_found = true; - break; - } - } - - if (!ob_found) - { - LogDebug(StringFormat("No valid fresh bullish OB found after FVG on %s", symbol)); - return false; - } - - // Step 5: Fibonacci validation (if enabled) - FibonacciRetracement valid_fibonacci; - bool fibonacci_valid = false; - - if (EnableFibonacci) - { - LogDebug("Step 5: Checking Fibonacci validation for bullish setup"); - - // Find a suitable Fibonacci retracement for this setup - for (int i = 0; i < ArraySize(g_fibonacci_retracements); i++) - { - if (ValidateFibonacciSetup(symbol, true, g_fibonacci_retracements[i])) - { - valid_fibonacci = g_fibonacci_retracements[i]; - fibonacci_valid = true; - LogDebug(StringFormat("Valid Fibonacci retracement found: %.5f to %.5f", - valid_fibonacci.swing_high, valid_fibonacci.swing_low)); - break; - } - } - - // Apply mode-based validation logic - if (!ShouldTakeTradeBasedOnMode(symbol, true, valid_ob, valid_fvg, valid_sweep, - fibonacci_valid ? valid_fibonacci : FibonacciRetracement(), - fibonacci_valid)) - { - LogDebug(StringFormat("BLOCKING CONDITION: Mode-based validation failed for %s", symbol)); - return false; - } - } - - // Step 6: Check multi-timeframe alignment - if (RequireMultiTFConfirmation) - { - if (!IsMultiTimeframeAligned(symbol, true)) - { - LogDebug(StringFormat("Multi-timeframe not aligned for bullish setup on %s", symbol)); - return false; - } - } - - // Step 7: Execute bullish trade - // Phase 5: Record pattern signal detection with Fibonacci info - string pattern_name = "OB+BOS+FVG+Sweep_Bullish"; - if (EnableFibonacci && fibonacci_valid) - { - pattern_name = "OB+BOS+FVG+Sweep+Fib_Bullish"; - } - RecordPatternSignal(pattern_name, true); - - return ExecuteBullishTradeWithFibonacci(symbol, valid_ob, valid_fvg, valid_sweep, - fibonacci_valid ? valid_fibonacci : FibonacciRetracement()); + // CRITICAL FIX: Use flexible confluence validation instead of requiring all 4 criteria + return ValidateFlexibleConfluence(symbol, true, m1_data); } bool AnalyzeBearishSetup(string symbol) { + LogDebug(StringFormat("=== Analyzing Bearish Setup for %s ===", symbol)); + // Get M1 timeframe data MarketStructureData m1_data; if (!GetTimeframeData(PERIOD_M1, m1_data) || !m1_data.is_valid) { + LogDebug(StringFormat("M1 data not available or invalid for %s", symbol)); return false; } - // Step 1: Find valid liquidity sweep (high sweep for bearish setup) - LiquiditySweep valid_sweep; - bool sweep_found = false; + LogDebug(StringFormat("M1 data available: OB=%d, FVG=%d, BOS=%d, Sweeps=%d", + ArraySize(m1_data.order_blocks), ArraySize(m1_data.fair_value_gaps), + ArraySize(m1_data.bos_events), ArraySize(m1_data.liquidity_sweeps))); - for (int i = 0; i < ArraySize(m1_data.liquidity_sweeps); i++) - { - if (m1_data.liquidity_sweeps[i].is_high_sweep && - IsLiquiditySweepValid(symbol, PERIOD_M1, m1_data.liquidity_sweeps[i])) - { - valid_sweep = m1_data.liquidity_sweeps[i]; - sweep_found = true; - break; - } - } - - if (!sweep_found) - { - LogDebug(StringFormat("No valid high sweep found for bearish setup on %s", symbol)); - return false; - } - - // Step 2: Find opposite direction BOS (bearish BOS after high sweep) - BreakOfStructure valid_bos; - bool bos_found = false; - - for (int i = 0; i < ArraySize(m1_data.bos_events); i++) - { - if (!m1_data.bos_events[i].is_bullish && - m1_data.bos_events[i].confirmed && - m1_data.bos_events[i].time > valid_sweep.time) // BOS must be after sweep - { - valid_bos = m1_data.bos_events[i]; - bos_found = true; - break; - } - } - - if (!bos_found) - { - LogDebug(StringFormat("No valid bearish BOS found after high sweep on %s", symbol)); - return false; - } - - // Step 3: Find valid FVG between BOS and current price - FairValueGap valid_fvg; - bool fvg_found = false; - - for (int i = 0; i < ArraySize(m1_data.fair_value_gaps); i++) - { - if (!m1_data.fair_value_gaps[i].is_bullish && - IsFVGValid(symbol, PERIOD_M1, m1_data.fair_value_gaps[i]) && - m1_data.fair_value_gaps[i].time > valid_bos.time) // FVG must be after BOS - { - valid_fvg = m1_data.fair_value_gaps[i]; - fvg_found = true; - break; - } - } - - if (!fvg_found) - { - LogDebug(StringFormat("No valid bearish FVG found after BOS on %s", symbol)); - return false; - } - - // Step 4: Find fresh bearish Order Block - OrderBlock valid_ob; - bool ob_found = false; - - for (int i = 0; i < ArraySize(m1_data.order_blocks); i++) - { - if (!m1_data.order_blocks[i].is_bullish && - m1_data.order_blocks[i].is_fresh && - m1_data.order_blocks[i].strength >= OBStrengthFilter && - m1_data.order_blocks[i].time > valid_fvg.time) // OB must be after FVG - { - valid_ob = m1_data.order_blocks[i]; - ob_found = true; - break; - } - } - - if (!ob_found) - { - LogDebug(StringFormat("No valid fresh bearish OB found after FVG on %s", symbol)); - return false; - } - - // Step 5: Fibonacci validation (if enabled) - FibonacciRetracement valid_fibonacci; - bool fibonacci_valid = false; - - if (EnableFibonacci) - { - LogDebug("Step 5: Checking Fibonacci validation for bearish setup"); - - // Find a suitable Fibonacci retracement for this setup - for (int i = 0; i < ArraySize(g_fibonacci_retracements); i++) - { - if (ValidateFibonacciSetup(symbol, false, g_fibonacci_retracements[i])) - { - valid_fibonacci = g_fibonacci_retracements[i]; - fibonacci_valid = true; - LogDebug(StringFormat("Valid Fibonacci retracement found: %.5f to %.5f", - valid_fibonacci.swing_high, valid_fibonacci.swing_low)); - break; - } - } - - // Apply mode-based validation logic - if (!ShouldTakeTradeBasedOnMode(symbol, false, valid_ob, valid_fvg, valid_sweep, - fibonacci_valid ? valid_fibonacci : FibonacciRetracement(), - fibonacci_valid)) - { - LogDebug(StringFormat("BLOCKING CONDITION: Mode-based validation failed for %s", symbol)); - return false; - } - } - - // Step 6: Check multi-timeframe alignment - if (RequireMultiTFConfirmation) - { - if (!IsMultiTimeframeAligned(symbol, false)) - { - LogDebug(StringFormat("Multi-timeframe not aligned for bearish setup on %s", symbol)); - return false; - } - } - - // Step 7: Execute bearish trade - // Phase 5: Record pattern signal detection with Fibonacci info - string pattern_name = "OB+BOS+FVG+Sweep_Bearish"; - if (EnableFibonacci && fibonacci_valid) - { - pattern_name = "OB+BOS+FVG+Sweep+Fib_Bearish"; - } - RecordPatternSignal(pattern_name, true); - - return ExecuteBearishTradeWithFibonacci(symbol, valid_ob, valid_fvg, valid_sweep, - fibonacci_valid ? valid_fibonacci : FibonacciRetracement()); + // CRITICAL FIX: Use flexible confluence validation instead of requiring all 4 criteria + return ValidateFlexibleConfluence(symbol, false, m1_data); } //+------------------------------------------------------------------+ @@ -3147,7 +2874,7 @@ bool DetectBreakOfStructure(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStr { ArrayResize(bos_events, 0); - int bars_to_analyze = MathMin(SwingLookback * 3, iBars(symbol, timeframe) - 10); + int bars_to_analyze = MathMin(SwingLookback * 8, iBars(symbol, timeframe) - 10); // Increased from 3x to 8x if (bars_to_analyze < 20) return false; @@ -3183,17 +2910,19 @@ void FindSwingPoints(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analy double current_low = iLow(symbol, timeframe, i); datetime current_time = iTime(symbol, timeframe, i); - // Check for swing high + // Check for swing high (relaxed criteria - allow equal highs) bool is_swing_high = true; + int higher_count = 0; for (int j = 1; j <= SwingLookback; j++) { - if (iHigh(symbol, timeframe, i - j) >= current_high || - iHigh(symbol, timeframe, i + j) >= current_high) + if (iHigh(symbol, timeframe, i - j) > current_high || + iHigh(symbol, timeframe, i + j) > current_high) { - is_swing_high = false; - break; + higher_count++; } } + // Allow swing high if less than 2 bars are higher (more permissive) + is_swing_high = (higher_count < 2); if (is_swing_high) { @@ -3203,17 +2932,19 @@ void FindSwingPoints(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analy swing_high_times[ArraySize(swing_high_times) - 1] = current_time; } - // Check for swing low + // Check for swing low (relaxed criteria - allow equal lows) bool is_swing_low = true; + int lower_count = 0; for (int j = 1; j <= SwingLookback; j++) { - if (iLow(symbol, timeframe, i - j) <= current_low || - iLow(symbol, timeframe, i + j) <= current_low) + if (iLow(symbol, timeframe, i - j) < current_low || + iLow(symbol, timeframe, i + j) < current_low) { - is_swing_low = false; - break; + lower_count++; } } + // Allow swing low if less than 2 bars are lower (more permissive) + is_swing_low = (lower_count < 2); if (is_swing_low) { @@ -3241,7 +2972,7 @@ void AnalyzeBOSPatterns(string symbol, ENUM_TIMEFRAMES timeframe, if (start_bar < 0) continue; - for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) + for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 4; j++) // Increased search range { double current_high = iHigh(symbol, timeframe, j); double current_close = iClose(symbol, timeframe, j); @@ -3281,7 +3012,7 @@ void AnalyzeBOSPatterns(string symbol, ENUM_TIMEFRAMES timeframe, if (start_bar < 0) continue; - for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) + for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 4; j++) // Increased search range { double current_low = iLow(symbol, timeframe, j); double current_close = iClose(symbol, timeframe, j); @@ -3332,8 +3063,8 @@ bool ConfirmBOS(string symbol, ENUM_TIMEFRAMES timeframe, int break_bar, bool is } } - // Require at least 2 out of 3 confirmation candles - return confirmation_count >= MathMax(2, BOSConfirmationCandles / 2); + // Require at least 1 out of 3 confirmation candles (relaxed from 2) + return confirmation_count >= MathMax(1, BOSConfirmationCandles / 3); } bool IsBOSValid(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos) @@ -3342,7 +3073,7 @@ bool IsBOSValid(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos) datetime current_time = iTime(symbol, timeframe, 0); int time_diff = (int)((current_time - bos.time) / PeriodSeconds(timeframe)); - if (time_diff > BOSConfirmationCandles * 3) + if (time_diff > BOSConfirmationCandles * 6) // Increased validity period from 3x to 6x return false; // Check if price is still respecting the BOS level @@ -3558,8 +3289,8 @@ bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySw if (equal_bar < 0) continue; - // Look for sweep above this equal high - for (int j = 0; j < equal_bar && j < 20; j++) + // Look for sweep above this equal high (increased search range) + for (int j = 0; j < equal_bar && j < 30; j++) { double current_high = iHigh(symbol, timeframe, j); double current_close = iClose(symbol, timeframe, j); @@ -3600,8 +3331,8 @@ bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySw if (equal_bar < 0) continue; - // Look for sweep below this equal low - for (int j = 0; j < equal_bar && j < 20; j++) + // Look for sweep below this equal low (increased search range) + for (int j = 0; j < equal_bar && j < 30; j++) { double current_low = iLow(symbol, timeframe, j); double current_close = iClose(symbol, timeframe, j); @@ -3646,7 +3377,7 @@ void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_an ArrayResize(equal_low_times, 0); double pip_value = CalculatePipValue(symbol); - double tolerance = 2.0 * pip_value; // 2 pip tolerance for "equal" levels + double tolerance = 3.0 * pip_value; // 3 pip tolerance for "equal" levels (increased from 2.0) // Find swing points first double swing_highs[]; @@ -3750,8 +3481,8 @@ bool ConfirmLiquiditySweep(string symbol, ENUM_TIMEFRAMES timeframe, int sweep_b double wick_size = sweep_high - sweep_close; double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); - // Wick should be at least 2x the body size - if (wick_size < body_size * 2) + // Wick should be at least 0.5x the body size (relaxed from 2x) + if (wick_size < body_size * 0.5) return false; // Close should be below the swept level @@ -3764,8 +3495,8 @@ bool ConfirmLiquiditySweep(string symbol, ENUM_TIMEFRAMES timeframe, int sweep_b double wick_size = sweep_close - sweep_low; double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); - // Wick should be at least 2x the body size - if (wick_size < body_size * 2) + // Wick should be at least 0.5x the body size (relaxed from 2x) + if (wick_size < body_size * 0.5) return false; // Close should be above the swept level @@ -3946,9 +3677,13 @@ bool UpdateTimeframeData(string symbol, MarketStructureData &mtf_data) bool bos_success = DetectBreakOfStructure(symbol, mtf_data.timeframe, mtf_data.bos_events); bool sweep_success = DetectLiquiditySweeps(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps); - // Consider update successful if at least pattern detection worked + // Consider update successful if at least pattern detection worked OR if we have sufficient bars bool patterns_success = ob_success || fvg_success || bos_success || sweep_success; + // CRITICAL FIX: Allow timeframe to be valid even if no patterns detected, as long as we have data + bool has_sufficient_data = iBars(symbol, mtf_data.timeframe) >= 50; + bool update_success = patterns_success || has_sufficient_data; + // Phase 2: Update enhanced multi-timeframe data (always attempt, don't fail on bias calc issues) // Calculate bias strength for this timeframe (don't fail if this doesn't work) mtf_data.current_bias = CalculateBiasStrength(symbol, mtf_data.timeframe); @@ -3981,9 +3716,9 @@ bool UpdateTimeframeData(string symbol, MarketStructureData &mtf_data) // Update metadata mtf_data.last_update = iTime(symbol, mtf_data.timeframe, 0); - mtf_data.is_valid = patterns_success; + mtf_data.is_valid = update_success; // Use the improved success criteria - if (patterns_success) + if (update_success) { LogDebug(StringFormat("%s analysis completed: OB=%d, FVG=%d, BOS=%d, Sweeps=%d, Bias=%.1f%% (%s), Phase=%s", EnumToString(mtf_data.timeframe), @@ -4002,7 +3737,7 @@ bool UpdateTimeframeData(string symbol, MarketStructureData &mtf_data) } } - return patterns_success; + return update_success; } void DrawPatternsOnChart(string symbol, MarketStructureData &mtf_data) @@ -4345,13 +4080,21 @@ BiasStrength CalculateBiasStrength(string symbol, ENUM_TIMEFRAMES timeframe) bias.pattern_score = 0.0; MarketStructureData mtf_data; - if (!GetTimeframeData(timeframe, mtf_data) || !mtf_data.is_valid) + if (!GetTimeframeData(timeframe, mtf_data)) { - LogDebug(StringFormat("Cannot calculate bias strength - invalid data for %s %s", + LogDebug(StringFormat("Cannot calculate bias strength - no data available for %s %s", symbol, EnumToString(timeframe))); return bias; } + // CRITICAL FIX: Allow bias calculation even if mtf_data.is_valid is false + // This prevents circular dependency where bias calc needs valid data but data validity depends on patterns + if (!mtf_data.is_valid) + { + LogDebug(StringFormat("Calculating bias with limited data for %s %s (patterns may be incomplete)", + symbol, EnumToString(timeframe))); + } + // Calculate BOS Score (0-40 points) bias.bos_score = CalculateBOSScore(mtf_data.bos_events); @@ -7432,3 +7175,209 @@ void LogFibonacciAnalysisStatus(string symbol) TimeToString(fib.created_time))); } } + +//+------------------------------------------------------------------+ +//| Flexible Confluence Validation System | +//+------------------------------------------------------------------+ +bool ValidateFlexibleConfluence(string symbol, bool is_bullish, MarketStructureData &m1_data) +{ + LogDebug(StringFormat("=== Flexible Confluence Validation for %s %s Setup ===", + symbol, is_bullish ? "Bullish" : "Bearish")); + + int confluence_count = 0; + string confluence_details = ""; + + // Criterion 1: Valid Liquidity Sweep + bool sweep_valid = false; + LiquiditySweep valid_sweep; + + for (int i = 0; i < ArraySize(m1_data.liquidity_sweeps); i++) + { + bool sweep_direction_match = is_bullish ? !m1_data.liquidity_sweeps[i].is_high_sweep : m1_data.liquidity_sweeps[i].is_high_sweep; + + if (sweep_direction_match && IsLiquiditySweepValid(symbol, PERIOD_M1, m1_data.liquidity_sweeps[i])) + { + valid_sweep = m1_data.liquidity_sweeps[i]; + sweep_valid = true; + confluence_count++; + confluence_details += "✓ Liquidity Sweep "; + LogDebug(StringFormat("✓ Valid %s sweep found at %.5f", + is_bullish ? "low" : "high", valid_sweep.level)); + break; + } + } + + if (!sweep_valid) + { + confluence_details += "✗ Liquidity Sweep "; + LogDebug(StringFormat("✗ No valid %s sweep found", is_bullish ? "low" : "high")); + } + + // Criterion 2: Valid Break of Structure + bool bos_valid = false; + BreakOfStructure valid_bos; + + for (int i = 0; i < ArraySize(m1_data.bos_events); i++) + { + if (m1_data.bos_events[i].is_bullish == is_bullish && + m1_data.bos_events[i].confirmed && + IsBOSValid(symbol, PERIOD_M1, m1_data.bos_events[i])) + { + // If we have a sweep, BOS should be after sweep + if (sweep_valid && m1_data.bos_events[i].time <= valid_sweep.time) + continue; + + valid_bos = m1_data.bos_events[i]; + bos_valid = true; + confluence_count++; + confluence_details += "✓ Break of Structure "; + LogDebug(StringFormat("✓ Valid %s BOS found at %.5f", + is_bullish ? "bullish" : "bearish", valid_bos.level)); + break; + } + } + + if (!bos_valid) + { + confluence_details += "✗ Break of Structure "; + LogDebug(StringFormat("✗ No valid %s BOS found", is_bullish ? "bullish" : "bearish")); + } + + // Criterion 3: Valid Fair Value Gap + bool fvg_valid = false; + FairValueGap valid_fvg; + + for (int i = 0; i < ArraySize(m1_data.fair_value_gaps); i++) + { + if (m1_data.fair_value_gaps[i].is_bullish == is_bullish && + IsFVGValid(symbol, PERIOD_M1, m1_data.fair_value_gaps[i])) + { + // If we have BOS, FVG should be after BOS + if (bos_valid && m1_data.fair_value_gaps[i].time <= valid_bos.time) + continue; + + valid_fvg = m1_data.fair_value_gaps[i]; + fvg_valid = true; + confluence_count++; + confluence_details += "✓ Fair Value Gap "; + LogDebug(StringFormat("✓ Valid %s FVG found: %.5f-%.5f", + is_bullish ? "bullish" : "bearish", valid_fvg.bottom, valid_fvg.top)); + break; + } + } + + if (!fvg_valid) + { + confluence_details += "✗ Fair Value Gap "; + LogDebug(StringFormat("✗ No valid %s FVG found", is_bullish ? "bullish" : "bearish")); + } + + // Criterion 4: Valid Order Block + bool ob_valid = false; + OrderBlock valid_ob; + + for (int i = 0; i < ArraySize(m1_data.order_blocks); i++) + { + if (m1_data.order_blocks[i].is_bullish == is_bullish && + m1_data.order_blocks[i].is_fresh && + m1_data.order_blocks[i].strength >= OBStrengthFilter) + { + // If we have FVG, OB should be after FVG + if (fvg_valid && m1_data.order_blocks[i].time <= valid_fvg.time) + continue; + + valid_ob = m1_data.order_blocks[i]; + ob_valid = true; + confluence_count++; + confluence_details += "✓ Order Block "; + LogDebug(StringFormat("✓ Valid %s OB found: %.5f-%.5f (strength: %.2f)", + is_bullish ? "bullish" : "bearish", valid_ob.low, valid_ob.high, valid_ob.strength)); + break; + } + } + + if (!ob_valid) + { + confluence_details += "✗ Order Block "; + LogDebug(StringFormat("✗ No valid %s OB found", is_bullish ? "bullish" : "bearish")); + } + + // Check if we meet minimum confluence requirements + bool confluence_met = confluence_count >= MinConfluenceCount; + + LogDebug(StringFormat("Confluence Summary: %d/4 criteria met (%s)", confluence_count, confluence_details)); + LogDebug(StringFormat("Minimum required: %d/4 - Result: %s", MinConfluenceCount, confluence_met ? "PASS" : "FAIL")); + + if (!confluence_met) + { + LogDebug(StringFormat("BLOCKING CONDITION: Insufficient confluence (%d/%d) for %s setup on %s", + confluence_count, MinConfluenceCount, is_bullish ? "bullish" : "bearish", symbol)); + return false; + } + + // Additional validations if confluence is met + + // Multi-timeframe alignment check + if (RequireMultiTFConfirmation) + { + if (!IsMultiTimeframeAligned(symbol, is_bullish)) + { + LogDebug(StringFormat("Multi-timeframe not aligned for %s setup on %s", + is_bullish ? "bullish" : "bearish", symbol)); + return false; + } + } + + // Fibonacci validation (if enabled) + FibonacciRetracement valid_fibonacci; + bool fibonacci_valid = false; + + if (EnableFibonacci) + { + LogDebug("Checking Fibonacci validation for setup"); + + for (int i = 0; i < ArraySize(g_fibonacci_retracements); i++) + { + if (ValidateFibonacciSetup(symbol, is_bullish, g_fibonacci_retracements[i])) + { + valid_fibonacci = g_fibonacci_retracements[i]; + fibonacci_valid = true; + LogDebug(StringFormat("Valid Fibonacci retracement found: %.5f to %.5f", + valid_fibonacci.swing_high, valid_fibonacci.swing_low)); + break; + } + } + + // Apply mode-based validation logic + if (!ShouldTakeTradeBasedOnMode(symbol, is_bullish, valid_ob, valid_fvg, valid_sweep, + fibonacci_valid ? valid_fibonacci : FibonacciRetracement(), + fibonacci_valid)) + { + LogDebug(StringFormat("BLOCKING CONDITION: Mode-based validation failed for %s", symbol)); + return false; + } + } + + // Execute trade if all validations pass + LogDebug(StringFormat("All validations passed - executing %s trade", is_bullish ? "bullish" : "bearish")); + + // Record pattern signal detection + string pattern_name = StringFormat("Flexible_%d_of_4_%s", confluence_count, is_bullish ? "Bullish" : "Bearish"); + if (EnableFibonacci && fibonacci_valid) + { + pattern_name += "_Fib"; + } + RecordPatternSignal(pattern_name, true); + + // Execute the trade + if (is_bullish) + { + return ExecuteBullishTradeWithFibonacci(symbol, valid_ob, valid_fvg, valid_sweep, + fibonacci_valid ? valid_fibonacci : FibonacciRetracement()); + } + else + { + return ExecuteBearishTradeWithFibonacci(symbol, valid_ob, valid_fvg, valid_sweep, + fibonacci_valid ? valid_fibonacci : FibonacciRetracement()); + } +}