mirror of
https://github.com/rithsila/MT5-EA-Sniper-Strategy.git
synced 2026-08-17 20:58:13 +00:00
- 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
714 lines
24 KiB
Plaintext
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 ×[], 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
|
|
}
|
|
}
|
|
}
|