Files
MT5-EA-Sniper-Strategy/src/EMERGENCY_FIXES.mq5
T
rithsila 885564cdaf Remove tests directory from git tracking and update .gitignore
- Added tests/, Tests/, TESTS/ to .gitignore
- Removed tests directory from git tracking while keeping it locally
- Tests directory contains logs, validation reports, and temporary test files
- This prevents test artifacts from being committed to the repository
2025-09-28 14:22:52 +07:00

714 lines
24 KiB
Plaintext

//+------------------------------------------------------------------+
//| EMERGENCY ALGORITHMIC FIXES FOR SNIPER EA |
//| Critical fixes for weekend test failures |
//| - BOS Detection Algorithm Overhaul |
//| - Liquidity Sweep Zone-Based Detection |
//| - Historical Data Processing Fixes |
//| - Memory Management Improvements |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| CRITICAL FIX #1: IMPROVED BOS DETECTION ALGORITHM |
//+------------------------------------------------------------------+
// New structure for improved swing point detection
struct SwingPointImproved
{
double price;
datetime time;
bool is_high;
double strength;
int detection_method; // 0=3-bar, 1=5-bar, 2=8-bar, 3=13-bar
int confirmation_count;
};
// Improved swing point detection with multiple timeframe validation
bool FindSwingPointsImproved(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze,
double &swing_highs[], double &swing_lows[],
datetime &swing_high_times[], datetime &swing_low_times[])
{
ArrayResize(swing_highs, 0);
ArrayResize(swing_lows, 0);
ArrayResize(swing_high_times, 0);
ArrayResize(swing_low_times, 0);
// Use multiple lookback periods for better detection (Fibonacci-based)
int lookback_periods[] = {3, 5, 8, 13};
double confidence_threshold = 0.6; // Require 60% of bars to confirm swing
for (int p = 0; p < ArraySize(lookback_periods); p++)
{
int current_lookback = lookback_periods[p];
for (int i = current_lookback; i < bars_to_analyze - current_lookback; i++)
{
// More flexible swing detection - use percentage-based confirmation
int higher_count = 0, lower_count = 0;
int total_comparison_bars = current_lookback * 2;
double current_high = iHigh(symbol, timeframe, i);
double current_low = iLow(symbol, timeframe, i);
// Check surrounding bars
for (int j = i - current_lookback; j <= i + current_lookback; j++)
{
if (j != i && j >= 0 && j < iBars(symbol, timeframe))
{
if (iHigh(symbol, timeframe, j) < current_high)
higher_count++;
if (iLow(symbol, timeframe, j) > current_low)
lower_count++;
}
}
// Calculate confidence levels
double high_confidence = (double)higher_count / total_comparison_bars;
double low_confidence = (double)lower_count / total_comparison_bars;
// Add swing high if confidence threshold met
if (high_confidence >= confidence_threshold)
{
if (!IsSwingPointDuplicate(swing_highs, swing_high_times, current_high, iTime(symbol, timeframe, i)))
{
ArrayResize(swing_highs, ArraySize(swing_highs) + 1);
ArrayResize(swing_high_times, ArraySize(swing_high_times) + 1);
swing_highs[ArraySize(swing_highs) - 1] = current_high;
swing_high_times[ArraySize(swing_high_times) - 1] = iTime(symbol, timeframe, i);
}
}
// Add swing low if confidence threshold met
if (low_confidence >= confidence_threshold)
{
if (!IsSwingPointDuplicate(swing_lows, swing_low_times, current_low, iTime(symbol, timeframe, i)))
{
ArrayResize(swing_lows, ArraySize(swing_lows) + 1);
ArrayResize(swing_low_times, ArraySize(swing_low_times) + 1);
swing_lows[ArraySize(swing_lows) - 1] = current_low;
swing_low_times[ArraySize(swing_low_times) - 1] = iTime(symbol, timeframe, i);
}
}
}
}
Print(StringFormat("Improved swing detection found %d highs and %d lows for %s %s",
ArraySize(swing_highs), ArraySize(swing_lows), symbol, EnumToString(timeframe)));
return ArraySize(swing_highs) > 0 || ArraySize(swing_lows) > 0;
}
// Helper function to prevent duplicate swing points
bool IsSwingPointDuplicate(double &existing_prices[], datetime &existing_times[],
double new_price, datetime new_time)
{
double pip_value = 0.0001; // Default for most pairs
double tolerance = 5.0 * pip_value; // 5-pip tolerance for duplicates
for (int i = 0; i < ArraySize(existing_prices); i++)
{
if (MathAbs(existing_prices[i] - new_price) <= tolerance)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| CRITICAL FIX #2: ZONE-BASED LIQUIDITY SWEEP DETECTION |
//+------------------------------------------------------------------+
// New structure for liquidity zones
struct LiquidityZone
{
double upper_bound;
double lower_bound;
double center_price;
datetime formation_time;
datetime sweep_time;
bool is_high_zone;
int touch_count;
double zone_strength;
bool is_swept;
};
// Improved liquidity sweep detection using zones instead of exact levels
bool DetectLiquiditySweepsImproved(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep_array[])
{
ArrayResize(sweep_array, 0);
int bars_to_analyze = MathMin(150, iBars(symbol, timeframe) - 10); // Increased analysis range
if (bars_to_analyze < 30)
return false;
Print(StringFormat("Analyzing %d bars for improved liquidity sweeps on %s %s",
bars_to_analyze, symbol, EnumToString(timeframe)));
// Create liquidity zones instead of exact levels
LiquidityZone zones[];
if (!CreateLiquidityZones(symbol, timeframe, bars_to_analyze, zones))
return false;
// Detect sweeps of liquidity zones
for (int i = 0; i < ArraySize(zones); i++)
{
if (DetectZoneSweep(symbol, timeframe, zones[i]))
{
LiquiditySweep sweep;
sweep.level = zones[i].center_price;
sweep.time = zones[i].sweep_time;
sweep.is_high_sweep = zones[i].is_high_zone;
sweep.confirmed = true; // Zone-based sweeps are auto-confirmed
ArrayResize(sweep_array, ArraySize(sweep_array) + 1);
sweep_array[ArraySize(sweep_array) - 1] = sweep;
Print(StringFormat("Zone sweep detected: %s at %.5f (zone: %.5f-%.5f)",
zones[i].is_high_zone ? "HIGH" : "LOW", zones[i].center_price,
zones[i].lower_bound, zones[i].upper_bound));
}
}
Print(StringFormat("Found %d zone-based liquidity sweeps on %s %s",
ArraySize(sweep_array), symbol, EnumToString(timeframe)));
return ArraySize(sweep_array) > 0;
}
// Create liquidity zones from price clusters
bool CreateLiquidityZones(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, LiquidityZone &zones[])
{
ArrayResize(zones, 0);
double pip_value = CalculatePipValue(symbol);
double zone_width = 10.0 * pip_value; // 10-pip zones (increased from 3-pip exact levels)
// Get recent significant highs and lows
double recent_highs[], recent_lows[];
datetime high_times[], low_times[];
GetRecentSignificantLevels(symbol, timeframe, bars_to_analyze, recent_highs, recent_lows, high_times, low_times);
// Create zones from clustered highs
CreateZonesFromLevels(recent_highs, high_times, true, zone_width, zones);
// Create zones from clustered lows
CreateZonesFromLevels(recent_lows, low_times, false, zone_width, zones);
Print(StringFormat("Created %d liquidity zones for %s %s", ArraySize(zones), symbol, EnumToString(timeframe)));
return ArraySize(zones) > 0;
}
// Get significant price levels for zone creation
void GetRecentSignificantLevels(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze,
double &highs[], double &lows[], datetime &high_times[], datetime &low_times[])
{
ArrayResize(highs, 0);
ArrayResize(lows, 0);
ArrayResize(high_times, 0);
ArrayResize(low_times, 0);
// Use improved swing detection
double swing_highs[], swing_lows[];
datetime swing_high_times[], swing_low_times[];
if (FindSwingPointsImproved(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times))
{
ArrayCopy(highs, swing_highs);
ArrayCopy(lows, swing_lows);
ArrayCopy(high_times, swing_high_times);
ArrayCopy(low_times, swing_low_times);
}
}
// Create zones from price level clusters
void CreateZonesFromLevels(double &levels[], datetime &times[], bool is_high_zone, double zone_width, LiquidityZone &zones[])
{
for (int i = 0; i < ArraySize(levels); i++)
{
double center_price = levels[i];
// Check if this level is already part of an existing zone
bool already_in_zone = false;
for (int j = 0; j < ArraySize(zones); j++)
{
if (center_price >= zones[j].lower_bound && center_price <= zones[j].upper_bound)
{
already_in_zone = true;
zones[j].touch_count++; // Increase zone strength
break;
}
}
if (!already_in_zone)
{
// Create new zone
LiquidityZone new_zone;
new_zone.center_price = center_price;
new_zone.upper_bound = center_price + (zone_width / 2);
new_zone.lower_bound = center_price - (zone_width / 2);
new_zone.formation_time = times[i];
new_zone.is_high_zone = is_high_zone;
new_zone.touch_count = 1;
new_zone.zone_strength = 1.0;
new_zone.is_swept = false;
ArrayResize(zones, ArraySize(zones) + 1);
zones[ArraySize(zones) - 1] = new_zone;
}
}
}
// Detect if a liquidity zone has been swept
bool DetectZoneSweep(string symbol, ENUM_TIMEFRAMES timeframe, LiquidityZone &zone)
{
if (zone.is_swept)
return false; // Already swept
int zone_bar = iBarShift(symbol, timeframe, zone.formation_time);
if (zone_bar < 0)
return false;
// Look for price action that sweeps through the zone
for (int i = 0; i < zone_bar && i < 50; i++) // Increased search range
{
double bar_high = iHigh(symbol, timeframe, i);
double bar_low = iLow(symbol, timeframe, i);
double bar_close = iClose(symbol, timeframe, i);
if (zone.is_high_zone)
{
// Check for sweep above zone with rejection
if (bar_high > zone.upper_bound && bar_close < zone.center_price)
{
zone.sweep_time = iTime(symbol, timeframe, i);
zone.is_swept = true;
return true;
}
}
else
{
// Check for sweep below zone with rejection
if (bar_low < zone.lower_bound && bar_close > zone.center_price)
{
zone.sweep_time = iTime(symbol, timeframe, i);
zone.is_swept = true;
return true;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| CRITICAL FIX #3: HISTORICAL DATA PROCESSING FIXES |
//+------------------------------------------------------------------+
// Improved BOS validation using bar-based instead of time-based logic
bool IsBOSValidImproved(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos)
{
// Use bar shift instead of time difference for historical compatibility
int bos_bar = iBarShift(symbol, timeframe, bos.time);
if (bos_bar < 0)
return false;
// Check if BOS is within reasonable bar distance (not time distance)
if (bos_bar > 50) // Increased from previous restrictive limits
return false;
// Validate price action relative to BOS level with buffer
double current_price = iClose(symbol, timeframe, 0);
double pip_value = CalculatePipValue(symbol);
double bos_validation_buffer = 5.0 * pip_value; // 5-pip buffer for validation
if (bos.is_bullish)
{
// For bullish BOS, current price should be above level (with buffer)
return current_price > (bos.level - bos_validation_buffer);
}
else
{
// For bearish BOS, current price should be below level (with buffer)
return current_price < (bos.level + bos_validation_buffer);
}
}
// Improved liquidity sweep validation using bar-based logic
bool IsLiquiditySweepValidImproved(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep)
{
if (!sweep.confirmed)
return false;
// Use bar-based validation instead of time-based
int sweep_bar = iBarShift(symbol, timeframe, sweep.time);
if (sweep_bar < 0 || sweep_bar > 30) // Within 30 bars instead of 10 time periods
return false;
// More flexible price position validation with buffer
double current_price = iClose(symbol, timeframe, 0);
double pip_value = CalculatePipValue(symbol);
double validation_buffer = 8.0 * pip_value; // 8-pip buffer for flexibility
if (sweep.is_high_sweep)
{
// For high sweep, price should be below swept level (with buffer)
return current_price < (sweep.level + validation_buffer);
}
else
{
// For low sweep, price should be above swept level (with buffer)
return current_price > (sweep.level - validation_buffer);
}
}
//+------------------------------------------------------------------+
//| CRITICAL FIX #4: MEMORY MANAGEMENT AND ARRAY CLEANUP |
//+------------------------------------------------------------------+
#define MAX_PATTERN_HISTORY 50
#define CLEANUP_FREQUENCY 25
struct PatternArrayManager
{
int cleanup_counter;
datetime last_cleanup;
bool cleanup_enabled;
};
PatternArrayManager g_array_manager = {0, 0, true};
// Initialize array management system
void InitializeArrayManager()
{
g_array_manager.cleanup_counter = 0;
g_array_manager.last_cleanup = TimeCurrent();
g_array_manager.cleanup_enabled = true;
Print("Pattern Array Manager initialized");
}
// Main cleanup function for all pattern arrays
void CleanupPatternArrays(MarketStructureData &mtf_data)
{
if (!g_array_manager.cleanup_enabled)
return;
g_array_manager.cleanup_counter++;
if (g_array_manager.cleanup_counter >= CLEANUP_FREQUENCY)
{
Print(StringFormat("Performing pattern array cleanup (cycle %d)", g_array_manager.cleanup_counter));
// Clean up old patterns to prevent memory issues
int ob_before = ArraySize(mtf_data.order_blocks);
int fvg_before = ArraySize(mtf_data.fair_value_gaps);
int bos_before = ArraySize(mtf_data.bos_events);
int sweep_before = ArraySize(mtf_data.liquidity_sweeps);
CleanupOldOrderBlocks(mtf_data.order_blocks);
CleanupOldFVGs(mtf_data.fair_value_gaps);
CleanupOldBOSEvents(mtf_data.bos_events);
CleanupOldSweeps(mtf_data.liquidity_sweeps);
Print(StringFormat("Cleanup completed: OB %d->%d, FVG %d->%d, BOS %d->%d, Sweeps %d->%d",
ob_before, ArraySize(mtf_data.order_blocks),
fvg_before, ArraySize(mtf_data.fair_value_gaps),
bos_before, ArraySize(mtf_data.bos_events),
sweep_before, ArraySize(mtf_data.liquidity_sweeps)));
g_array_manager.cleanup_counter = 0;
g_array_manager.last_cleanup = TimeCurrent();
}
}
// Cleanup old order blocks
void CleanupOldOrderBlocks(OrderBlock &order_blocks[])
{
if (ArraySize(order_blocks) <= MAX_PATTERN_HISTORY)
return;
// Keep only the most recent patterns
int keep_count = MAX_PATTERN_HISTORY;
OrderBlock temp_array[];
ArrayResize(temp_array, keep_count);
// Copy most recent patterns
for (int i = 0; i < keep_count; i++)
{
temp_array[i] = order_blocks[ArraySize(order_blocks) - keep_count + i];
}
// Replace original array
ArrayResize(order_blocks, keep_count);
for (int i = 0; i < keep_count; i++)
{
order_blocks[i] = temp_array[i];
}
}
// Cleanup old FVGs
void CleanupOldFVGs(FairValueGap &fvgs[])
{
if (ArraySize(fvgs) <= MAX_PATTERN_HISTORY)
return;
int keep_count = MAX_PATTERN_HISTORY;
FairValueGap temp_array[];
ArrayResize(temp_array, keep_count);
for (int i = 0; i < keep_count; i++)
{
temp_array[i] = fvgs[ArraySize(fvgs) - keep_count + i];
}
ArrayResize(fvgs, keep_count);
for (int i = 0; i < keep_count; i++)
{
fvgs[i] = temp_array[i];
}
}
// Cleanup old BOS events
void CleanupOldBOSEvents(BreakOfStructure &bos_events[])
{
if (ArraySize(bos_events) <= MAX_PATTERN_HISTORY)
return;
int keep_count = MAX_PATTERN_HISTORY;
BreakOfStructure temp_array[];
ArrayResize(temp_array, keep_count);
for (int i = 0; i < keep_count; i++)
{
temp_array[i] = bos_events[ArraySize(bos_events) - keep_count + i];
}
ArrayResize(bos_events, keep_count);
for (int i = 0; i < keep_count; i++)
{
bos_events[i] = temp_array[i];
}
}
// Cleanup old liquidity sweeps
void CleanupOldSweeps(LiquiditySweep &sweeps[])
{
if (ArraySize(sweeps) <= MAX_PATTERN_HISTORY)
return;
int keep_count = MAX_PATTERN_HISTORY;
LiquiditySweep temp_array[];
ArrayResize(temp_array, keep_count);
for (int i = 0; i < keep_count; i++)
{
temp_array[i] = sweeps[ArraySize(sweeps) - keep_count + i];
}
ArrayResize(sweeps, keep_count);
for (int i = 0; i < keep_count; i++)
{
sweeps[i] = temp_array[i];
}
}
//+------------------------------------------------------------------+
//| CRITICAL FIX #5: IMPROVED CONFLUENCE VALIDATION |
//+------------------------------------------------------------------+
struct PatternScore
{
double sweep_score; // 0-25 points
double bos_score; // 0-25 points
double fvg_score; // 0-25 points
double ob_score; // 0-25 points
double total_score; // Sum of all scores
string score_breakdown; // Detailed breakdown for logging
};
// Improved confluence validation using scoring system
bool ValidateFlexibleConfluenceImproved(string symbol, bool is_bullish, MarketStructureData &m1_data)
{
Print(StringFormat("=== Improved Confluence Validation for %s %s Setup ===",
symbol, is_bullish ? "Bullish" : "Bearish"));
PatternScore scores;
scores.sweep_score = CalculateSweepScore(symbol, is_bullish, m1_data);
scores.bos_score = CalculateBOSScore(symbol, is_bullish, m1_data);
scores.fvg_score = CalculateFVGScore(symbol, is_bullish, m1_data);
scores.ob_score = CalculateOBScore(symbol, is_bullish, m1_data);
scores.total_score = scores.sweep_score + scores.bos_score + scores.fvg_score + scores.ob_score;
scores.score_breakdown = StringFormat("Sweep=%.1f, BOS=%.1f, FVG=%.1f, OB=%.1f",
scores.sweep_score, scores.bos_score, scores.fvg_score, scores.ob_score);
double required_score = 50.0; // Require 50% total score instead of 3/4 criteria
Print(StringFormat("Pattern Scores: %s, Total=%.1f/100", scores.score_breakdown, scores.total_score));
if (scores.total_score >= required_score)
{
Print(StringFormat("Score-based confluence met (%.1f >= %.1f) - executing trade", scores.total_score, required_score));
return ExecuteTradeWithScores(symbol, is_bullish, scores, m1_data);
}
Print(StringFormat("Insufficient confluence score (%.1f < %.1f)", scores.total_score, required_score));
return false;
}
// Calculate sweep score (0-25 points)
double CalculateSweepScore(string symbol, bool is_bullish, MarketStructureData &m1_data)
{
double score = 0.0;
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 && IsLiquiditySweepValidImproved(symbol, PERIOD_M1, m1_data.liquidity_sweeps[i]))
{
score = 25.0; // Full points for valid sweep
break;
}
}
return score;
}
// Calculate BOS score (0-25 points)
double CalculateBOSScore(string symbol, bool is_bullish, MarketStructureData &m1_data)
{
double score = 0.0;
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 &&
IsBOSValidImproved(symbol, PERIOD_M1, m1_data.bos_events[i]))
{
score = 25.0; // Full points for valid BOS
break;
}
}
return score;
}
// Calculate FVG score (0-25 points)
double CalculateFVGScore(string symbol, bool is_bullish, MarketStructureData &m1_data)
{
double score = 0.0;
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]))
{
score = 25.0; // Full points for valid FVG
break;
}
}
return score;
}
// Calculate OB score (0-25 points)
double CalculateOBScore(string symbol, bool is_bullish, MarketStructureData &m1_data)
{
double score = 0.0;
double best_strength = 0.0;
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 > best_strength)
{
best_strength = m1_data.order_blocks[i].strength;
}
}
if (best_strength > 0.0)
{
score = MathMin(25.0, best_strength * 12.5); // Scale strength to 0-25 points
}
return score;
}
// Execute trade with scoring information
bool ExecuteTradeWithScores(string symbol, bool is_bullish, PatternScore &scores, MarketStructureData &m1_data)
{
Print(StringFormat("Executing %s trade for %s with score %.1f (%s)",
is_bullish ? "bullish" : "bearish", symbol, scores.total_score, scores.score_breakdown));
// Find best patterns for trade execution
OrderBlock best_ob;
FairValueGap best_fvg;
LiquiditySweep best_sweep;
// Get best patterns based on scores
if (scores.ob_score > 0)
GetBestOrderBlock(symbol, is_bullish, m1_data, best_ob);
if (scores.fvg_score > 0)
GetBestFVG(symbol, is_bullish, m1_data, best_fvg);
if (scores.sweep_score > 0)
GetBestSweep(symbol, is_bullish, m1_data, best_sweep);
// Execute trade using existing trade execution functions
if (is_bullish)
{
return ExecuteBullishTradeWithFibonacci(symbol, best_ob, best_fvg, best_sweep, FibonacciRetracement());
}
else
{
return ExecuteBearishTradeWithFibonacci(symbol, best_ob, best_fvg, best_sweep, FibonacciRetracement());
}
}
// Helper functions to get best patterns
void GetBestOrderBlock(string symbol, bool is_bullish, MarketStructureData &m1_data, OrderBlock &best_ob)
{
double best_strength = 0.0;
int best_index = -1;
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 > best_strength)
{
best_strength = m1_data.order_blocks[i].strength;
best_index = i;
}
}
if (best_index >= 0)
best_ob = m1_data.order_blocks[best_index];
}
void GetBestFVG(string symbol, bool is_bullish, MarketStructureData &m1_data, FairValueGap &best_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]))
{
best_fvg = m1_data.fair_value_gaps[i];
break; // Take first valid FVG
}
}
}
void GetBestSweep(string symbol, bool is_bullish, MarketStructureData &m1_data, LiquiditySweep &best_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 && IsLiquiditySweepValidImproved(symbol, PERIOD_M1, m1_data.liquidity_sweeps[i]))
{
best_sweep = m1_data.liquidity_sweeps[i];
break; // Take first valid sweep
}
}
}