2025-10-03 01:38:36 -04:00 | | | //+------------------------------------------------------------------+
|
| | | //| AdaptiveSignalOptimizer.mqh |
|
| | | //| Purpose: ML-Driven Adaptive Signal Optimization System |
|
| | | //| Features: Strategy-specific profiles, Multi-pass validation, |
|
| | | //| Learning-based parameter tuning |
|
| | | //+------------------------------------------------------------------+
|
| | | #ifndef __ADAPTIVESIGNALOPTIMIZER_MQH__
|
| | | #define __ADAPTIVESIGNALOPTIMIZER_MQH__
|
| | |
|
| | | #include "LearningBridge.mqh"
|
2026-02-24 12:47:37 -05:00 | | | #include "EnhancedEfficientGateSystem.mqh"
|
2026-02-05 23:31:20 -05:00 | | | #include "GateSystemAutoLearning.mqh"
|
| | |
|
2025-10-03 01:38:36 -04:00 | | | // Strategy-specific adjustment profile
|
| | | struct StrategyProfile
|
| | | {
|
| | | string strategy_name;
|
| | |
|
| | | // Adjustment ranges (min, max, step)
|
| | | double price_adjust_range[3]; // [min, max, step]
|
| | | double sl_adjust_range[3];
|
| | | double tp_adjust_range[3];
|
| | | double volume_adjust_range[3];
|
| | |
|
| | | // Learning metrics
|
| | | double success_rate;
|
| | | int total_adjustments;
|
| | | int successful_adjustments;
|
| | | datetime last_update;
|
| | |
|
| | | // ML-derived optimal adjustments
|
| | | double optimal_price_tweak;
|
| | | double optimal_sl_tweak;
|
| | | double optimal_tp_tweak;
|
| | | double optimal_volume_tweak;
|
| | | };
|
| | |
|
| | | // Gate journey step - tracks what happened at each gate
|
| | | struct GateJourneyStep
|
| | | {
|
| | | string gate_name;
|
| | | int gate_index;
|
| | | bool passed;
|
| | | string reason;
|
| | |
|
| | | // Adjustments made at this gate
|
| | | double price_before;
|
| | | double price_after;
|
| | | double sl_before;
|
| | | double sl_after;
|
| | | double tp_before;
|
| | | double tp_after;
|
| | | double volume_before;
|
| | | double volume_after;
|
| | |
|
| | | bool was_adjusted;
|
| | | datetime step_time;
|
| | | };
|
| | |
|
| | | // Adjustment attempt record
|
| | | struct AdjustmentAttempt
|
| | | {
|
| | | int attempt_number;
|
| | | string gate_blocked;
|
| | | double price_tweak;
|
| | | double sl_tweak;
|
| | | double tp_tweak;
|
| | | double volume_tweak;
|
| | | bool passed;
|
| | | string reason;
|
| | |
|
| | | // Full gate journey for this attempt
|
| | | GateJourneyStep journey[8];
|
| | | int journey_length;
|
| | | };
|
| | |
|
| | | // Enhanced decision with adjustment tracking
|
| | | class CAdaptiveDecision : public CSignalDecision
|
| | | {
|
| | | public:
|
2026-02-24 12:47:37 -05:00 | | | // Note: is_adjusted is inherited from CSignalDecision, don't redeclare
|
2025-10-03 01:38:36 -04:00 | | | int adjustment_attempts;
|
| | | AdjustmentAttempt attempts[3]; // Max 3 attempts
|
| | | string original_signal_id;
|
| | |
|
| | | // Full gate journey (all gates, all attempts)
|
| | | GateJourneyStep complete_journey[24]; // 8 gates × 3 attempts max
|
| | | int complete_journey_length;
|
| | |
|
| | | CAdaptiveDecision() : CSignalDecision()
|
| | | {
|
| | | is_adjusted = false;
|
| | | adjustment_attempts = 0;
|
| | | original_signal_id = "";
|
| | | complete_journey_length = 0;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Main Adaptive Signal Optimizer Class |
|
| | | //+------------------------------------------------------------------+
|
| | | class CAdaptiveSignalOptimizer
|
| | | {
|
| | | private:
|
| | | CLearningBridge* m_learning;
|
2026-02-05 23:31:20 -05:00 | | | CEfficientGateManagerEnhanced* m_gate_manager;
|
2025-10-16 18:03:12 -04:00 | | |
|
| | | // Simple array-based profile storage (MQL5 HashMap doesn't support structs)
|
| | | string m_profile_keys[];
|
| | | StrategyProfile m_profile_values[];
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | // Configuration
|
| | | int m_max_attempts;
|
| | | bool m_use_ml_optimization;
|
| | | bool m_verbose_logging;
|
| | |
|
| | | // Statistics
|
| | | int m_total_optimizations;
|
| | | int m_successful_optimizations;
|
| | |
|
2025-10-16 18:03:12 -04:00 | | | // Helper methods for profile storage
|
| | | int FindProfileIndex(const string key)
|
| | | {
|
| | | for(int i = 0; i < ArraySize(m_profile_keys); i++)
|
| | | {
|
| | | if(m_profile_keys[i] == key) return i;
|
| | | }
|
| | | return -1;
|
| | | }
|
| | |
|
| | | bool GetProfile(const string key, StrategyProfile &out_profile)
|
| | | {
|
| | | int idx = FindProfileIndex(key);
|
| | | if(idx >= 0)
|
| | | {
|
| | | out_profile = m_profile_values[idx];
|
| | | return true;
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | void SetProfile(const string key, const StrategyProfile &profile)
|
| | | {
|
| | | int idx = FindProfileIndex(key);
|
| | | if(idx >= 0)
|
| | | {
|
| | | m_profile_values[idx] = profile;
|
| | | }
|
| | | else
|
| | | {
|
| | | int size = ArraySize(m_profile_keys);
|
| | | ArrayResize(m_profile_keys, size + 1);
|
| | | ArrayResize(m_profile_values, size + 1);
|
| | | m_profile_keys[size] = key;
|
| | | m_profile_values[size] = profile;
|
| | | }
|
| | | }
|
| | |
|
2025-10-03 01:38:36 -04:00 | | | // Initialize strategy profiles for all 23 strategies
|
| | | void InitializeStrategyProfiles()
|
| | | {
|
| | | string strategies[] = {
|
| | | "MovingAverageStrategy", "ADXStrategy", "RSIStrategy", "BollingerBandsStrategy",
|
| | | "MACDStrategy", "StochasticStrategy", "IchimokuStrategy", "ParabolicSARStrategy",
|
| | | "CCIStrategy", "WilliamsRStrategy", "ATRStrategy", "FibonacciStrategy",
|
| | | "PivotPointStrategy", "BreakoutStrategy", "MomentumStrategy", "VolumeStrategy",
|
| | | "GapStrategy", "NewsStrategy", "CorrelationStrategy", "SeasonalStrategy",
|
| | | "MachineLearningStrategy", "EnsembleStrategy", "HybridStrategy"
|
| | | };
|
| | |
|
| | | for(int i = 0; i < ArraySize(strategies); i++)
|
| | | {
|
| | | StrategyProfile profile;
|
| | | profile.strategy_name = strategies[i];
|
| | |
|
| | | // Default adjustment ranges (will be ML-optimized)
|
2026-01-14 13:37:28 -05:00 | | | // Price: ±1% adjustment range (more conservative)
|
| | | profile.price_adjust_range[0] = -0.01;
|
| | | profile.price_adjust_range[1] = 0.01;
|
| | | profile.price_adjust_range[2] = 0.0025; // 0.25% steps
|
2025-10-03 01:38:36 -04:00 | | |
|
2026-01-14 13:37:28 -05:00 | | | // SL: 0.8x to 1.2x (more conservative)
|
| | | profile.sl_adjust_range[0] = 0.8;
|
| | | profile.sl_adjust_range[1] = 1.2;
|
| | | profile.sl_adjust_range[2] = 0.05;
|
2025-10-03 01:38:36 -04:00 | | |
|
2026-01-14 13:37:28 -05:00 | | | // TP: 0.9x to 1.5x (more conservative)
|
| | | profile.tp_adjust_range[0] = 0.9;
|
| | | profile.tp_adjust_range[1] = 1.5;
|
| | | profile.tp_adjust_range[2] = 0.1;
|
2025-10-03 01:38:36 -04:00 | | |
|
2026-01-14 13:37:28 -05:00 | | | // Volume: 0.5x to 1.0x (reduce only for safety)
|
| | | profile.volume_adjust_range[0] = 0.5;
|
2025-10-03 01:38:36 -04:00 | | | profile.volume_adjust_range[1] = 1.0;
|
2026-01-14 13:37:28 -05:00 | | | profile.volume_adjust_range[2] = 0.05;
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | // Initialize metrics
|
| | | profile.success_rate = 0.0;
|
| | | profile.total_adjustments = 0;
|
| | | profile.successful_adjustments = 0;
|
| | | profile.last_update = 0;
|
| | |
|
2026-01-14 13:37:28 -05:00 | | | // Start with more conservative tweaks
|
2025-10-03 01:38:36 -04:00 | | | profile.optimal_price_tweak = 0.0;
|
| | | profile.optimal_sl_tweak = 1.0;
|
2026-01-14 13:37:28 -05:00 | | | profile.optimal_tp_tweak = 1.1; // Slightly more aggressive TP
|
| | | profile.optimal_volume_tweak = 0.8; // Less conservative volume
|
2025-10-03 01:38:36 -04:00 | | |
|
2025-10-16 18:03:12 -04:00 | | | SetProfile(strategies[i], profile);
|
2025-10-03 01:38:36 -04:00 | | | }
|
| | |
|
| | | PrintFormat("✅ AdaptiveSignalOptimizer: Initialized %d strategy profiles", ArraySize(strategies));
|
| | | }
|
| | |
|
| | | // Learn optimal adjustments from historical data
|
| | | void LearnOptimalAdjustments(string strategy_name)
|
| | | {
|
| | | if(m_learning == NULL) return;
|
| | |
|
| | | StrategyProfile profile;
|
2025-10-16 18:03:12 -04:00 | | | if(!GetProfile(strategy_name, profile))
|
2025-10-03 01:38:36 -04:00 | | | return;
|
| | |
|
| | | // Query learning bridge for successful adjustments
|
| | | // This is where ML magic happens - analyze what tweaks led to profitable trades
|
| | |
|
| | | // For now, use simple heuristics based on success rate
|
| | | if(profile.total_adjustments > 10)
|
| | | {
|
| | | double success_rate = (double)profile.successful_adjustments / profile.total_adjustments;
|
| | |
|
| | | if(success_rate > 0.7)
|
| | | {
|
| | | // Good performance - be more aggressive
|
| | | profile.optimal_volume_tweak = MathMin(1.0, profile.optimal_volume_tweak * 1.1);
|
| | | }
|
| | | else if(success_rate < 0.4)
|
| | | {
|
| | | // Poor performance - be more conservative
|
| | | profile.optimal_volume_tweak = MathMax(0.3, profile.optimal_volume_tweak * 0.9);
|
| | | profile.optimal_sl_tweak = MathMax(0.5, profile.optimal_sl_tweak * 0.95);
|
| | | }
|
| | |
|
| | | profile.last_update = TimeCurrent();
|
2025-10-16 18:03:12 -04:00 | | | SetProfile(strategy_name, profile);
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("📊 ML Update [%s]: Success=%.1f%% Vol=%.2f SL=%.2f",
|
| | | strategy_name, success_rate * 100,
|
| | | profile.optimal_volume_tweak, profile.optimal_sl_tweak);
|
| | | }
|
| | | }
|
| | |
|
2026-02-04 14:28:59 -05:00 | | | bool ValidateAdjustedSignal(const TradingSignal &signal, string &reason) const
|
| | | {
|
| | | string symbol = (signal.symbol != "") ? signal.symbol : _Symbol;
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | if(!MathIsValidNumber(signal.price) || signal.price <= 0.0)
|
| | | {
|
| | | reason = "invalid price";
|
| | | return false;
|
| | | }
|
| | |
|
| | | if(!MathIsValidNumber(signal.sl) || !MathIsValidNumber(signal.tp))
|
| | | {
|
| | | reason = "invalid SL/TP";
|
| | | return false;
|
| | | }
|
| | |
|
| | | bool is_buy = (signal.type == 0);
|
| | | if(is_buy)
|
| | | {
|
| | | if(signal.sl >= signal.price)
|
| | | {
|
| | | reason = "buy SL above price";
|
| | | return false;
|
| | | }
|
| | | if(signal.tp <= signal.price)
|
| | | {
|
| | | reason = "buy TP below price";
|
| | | return false;
|
| | | }
|
| | | }
|
| | | else if(signal.type == 1)
|
| | | {
|
| | | if(signal.sl <= signal.price)
|
| | | {
|
| | | reason = "sell SL below price";
|
| | | return false;
|
| | | }
|
| | | if(signal.tp >= signal.price)
|
| | | {
|
| | | reason = "sell TP above price";
|
| | | return false;
|
| | | }
|
| | | }
|
| | |
|
| | | double vol_min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
| | | double vol_max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
| | | if(vol_min <= 0.0) vol_min = 0.01;
|
| | | if(vol_max <= 0.0) vol_max = 100.0;
|
| | |
|
| | | if(!MathIsValidNumber(signal.volume) || signal.volume < vol_min || signal.volume > vol_max)
|
| | | {
|
| | | reason = "invalid volume";
|
| | | return false;
|
| | | }
|
| | |
|
| | | return true;
|
| | | }
|
| | |
|
2025-10-03 01:38:36 -04:00 | | | public:
|
2026-02-05 23:31:20 -05:00 | | | CAdaptiveSignalOptimizer(CLearningBridge* learning, CEfficientGateManagerEnhanced* gate_mgr,
|
2025-10-03 01:38:36 -04:00 | | | int max_attempts = 3, bool use_ml = true)
|
| | | {
|
| | | m_learning = learning;
|
| | | m_gate_manager = gate_mgr;
|
| | | m_max_attempts = max_attempts;
|
| | | m_use_ml_optimization = use_ml;
|
| | | m_verbose_logging = true;
|
| | |
|
| | | m_total_optimizations = 0;
|
| | | m_successful_optimizations = 0;
|
| | |
|
| | | InitializeStrategyProfiles();
|
| | |
|
| | | PrintFormat("🎯 AdaptiveSignalOptimizer initialized: max_attempts=%d, ML=%s",
|
| | | m_max_attempts, m_use_ml_optimization ? "ON" : "OFF");
|
| | | }
|
| | |
|
| | | ~CAdaptiveSignalOptimizer()
|
| | | {
|
| | | PrintFormat("AdaptiveSignalOptimizer stats: %d/%d optimizations successful (%.1f%%)",
|
| | | m_successful_optimizations, m_total_optimizations,
|
| | | m_total_optimizations > 0 ? (double)m_successful_optimizations/m_total_optimizations*100 : 0);
|
| | | }
|
| | |
|
| | | // Main optimization function with multi-pass validation
|
| | | bool OptimizeSignal(TradingSignal &signal, CAdaptiveDecision &decision,
|
| | | const string strategy_name, string &blocking_reason)
|
| | | {
|
| | | m_total_optimizations++;
|
| | |
|
| | | // First attempt: Try original signal
|
| | | CSignalDecision temp_decision;
|
2026-02-24 12:47:37 -05:00 | | | string block_reason;
|
| | | bool passed = m_gate_manager.ProcessSignalEnhanced(signal, temp_decision, block_reason);
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | if(passed)
|
| | | {
|
| | | // Original signal passed - no adjustment needed
|
| | | CopyDecision(temp_decision, decision);
|
| | | decision.is_adjusted = false;
|
| | | decision.adjustment_attempts = 0;
|
| | |
|
| | | blocking_reason = "Original signal passed all gates";
|
| | | return true;
|
| | | }
|
| | |
|
| | | // Original failed - start adaptive optimization
|
| | | blocking_reason = "Original signal blocked";
|
| | | decision.original_signal_id = signal.id;
|
| | | decision.is_adjusted = true;
|
| | |
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("🔧 Optimizing blocked signal: %s [%s]", signal.id, strategy_name);
|
| | |
|
| | | // Get strategy profile
|
| | | StrategyProfile profile;
|
2025-10-16 18:03:12 -04:00 | | | if(!GetProfile(strategy_name, profile))
|
2025-10-03 01:38:36 -04:00 | | | {
|
| | | // Unknown strategy - use default profile
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("⚠️ Unknown strategy: %s, using defaults", strategy_name);
|
| | |
|
| | | profile.optimal_price_tweak = 0.0;
|
| | | profile.optimal_sl_tweak = 1.0;
|
| | | profile.optimal_tp_tweak = 1.0;
|
| | | profile.optimal_volume_tweak = 0.7;
|
| | | }
|
| | |
|
| | | // Update ML-learned adjustments
|
| | | if(m_use_ml_optimization)
|
| | | LearnOptimalAdjustments(strategy_name);
|
| | |
|
| | | // Multi-attempt optimization
|
| | | for(int attempt = 0; attempt < m_max_attempts; attempt++)
|
| | | {
|
| | | TradingSignal adjusted_signal = signal;
|
2026-02-04 14:28:59 -05:00 | | |
|
| | | // Normalize symbol and volume bounds early so validation doesn't skip all attempts.
|
| | | string symbol = (adjusted_signal.symbol != "") ? adjusted_signal.symbol : _Symbol;
|
| | | if(symbol == "") symbol = _Symbol;
|
| | | double vol_min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
| | | double vol_max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
| | | double vol_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
| | | if(vol_min <= 0.0) vol_min = 0.01;
|
| | | if(vol_max <= 0.0) vol_max = 100.0;
|
| | | if(vol_step <= 0.0) vol_step = vol_min;
|
| | | if(!MathIsValidNumber(adjusted_signal.volume) || adjusted_signal.volume <= 0.0)
|
| | | adjusted_signal.volume = vol_min;
|
| | | // Clamp and round to step
|
| | | if(adjusted_signal.volume < vol_min) adjusted_signal.volume = vol_min;
|
| | | if(adjusted_signal.volume > vol_max) adjusted_signal.volume = vol_max;
|
| | | adjusted_signal.volume = MathFloor(adjusted_signal.volume / vol_step + 1e-9) * vol_step;
|
| | | if(adjusted_signal.volume < vol_min) adjusted_signal.volume = vol_min;
|
| | | if(adjusted_signal.volume > vol_max) adjusted_signal.volume = vol_max;
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | // Calculate adjustments based on attempt number and ML insights
|
2026-01-14 13:37:28 -05:00 | | | // Use more conservative adjustments to increase success rate
|
| | | double aggressiveness = 1.0 + (attempt * 0.15); // Slower increase in adjustments
|
2025-10-03 01:38:36 -04:00 | | |
|
2026-01-14 13:37:28 -05:00 | | | // Apply ML-optimized adjustments with more conservative ranges
|
| | | adjusted_signal.price = signal.price * (1.0 + profile.optimal_price_tweak * aggressiveness * 0.5); // Reduce price adjustment impact
|
2026-02-04 14:28:59 -05:00 | | | double sl_dist = MathAbs(signal.price - signal.sl);
|
| | | double tp_dist = MathAbs(signal.tp - signal.price);
|
| | | if(sl_dist <= 0.0) sl_dist = MathAbs(signal.price) * 0.001;
|
| | | if(tp_dist <= 0.0) tp_dist = MathAbs(signal.price) * 0.001;
|
| | | double sl_mult = (profile.optimal_sl_tweak * (1.0 - attempt * 0.05));
|
| | | double tp_mult = (profile.optimal_tp_tweak * (1.0 + attempt * 0.075));
|
| | | if(sl_mult <= 0.01) sl_mult = 0.01;
|
| | | if(tp_mult <= 0.01) tp_mult = 0.01;
|
| | | if(signal.type == 0)
|
| | | {
|
| | | adjusted_signal.sl = adjusted_signal.price - sl_dist * sl_mult;
|
| | | adjusted_signal.tp = adjusted_signal.price + tp_dist * tp_mult;
|
| | | }
|
| | | else
|
| | | {
|
| | | adjusted_signal.sl = adjusted_signal.price + sl_dist * sl_mult;
|
| | | adjusted_signal.tp = adjusted_signal.price - tp_dist * tp_mult;
|
| | | }
|
| | | adjusted_signal.volume = adjusted_signal.volume * (profile.optimal_volume_tweak * (1.0 - attempt * 0.075)); // Gentler volume reduction
|
| | | // Re-clamp after applying tweaks
|
| | | if(adjusted_signal.volume < vol_min) adjusted_signal.volume = vol_min;
|
| | | if(adjusted_signal.volume > vol_max) adjusted_signal.volume = vol_max;
|
| | | adjusted_signal.volume = MathFloor(adjusted_signal.volume / vol_step + 1e-9) * vol_step;
|
| | | if(adjusted_signal.volume < vol_min) adjusted_signal.volume = vol_min;
|
| | | if(adjusted_signal.volume > vol_max) adjusted_signal.volume = vol_max;
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | // Special adjustments based on confidence
|
2026-01-14 13:37:28 -05:00 | | | if(signal.confidence < 0.3)
|
| | | {
|
| | | // Very low confidence - be extra conservative
|
| | | adjusted_signal.volume *= 0.7;
|
2026-02-04 14:28:59 -05:00 | | | double dist = MathAbs(adjusted_signal.price - adjusted_signal.sl);
|
| | | adjusted_signal.sl = (adjusted_signal.type == 0) ? adjusted_signal.price - dist * 0.9 : adjusted_signal.price + dist * 0.9;
|
2026-01-14 13:37:28 -05:00 | | | }
|
| | | else if(signal.confidence < 0.5)
|
2025-10-03 01:38:36 -04:00 | | | {
|
2026-01-14 13:37:28 -05:00 | | | // Low confidence - be moderately conservative
|
| | | adjusted_signal.volume *= 0.85;
|
2026-02-04 14:28:59 -05:00 | | | double dist = MathAbs(adjusted_signal.price - adjusted_signal.sl);
|
| | | adjusted_signal.sl = (adjusted_signal.type == 0) ? adjusted_signal.price - dist * 0.95 : adjusted_signal.price + dist * 0.95;
|
2025-10-03 01:38:36 -04:00 | | | }
|
| | |
|
| | | // Update signal ID to track adjustment
|
| | | adjusted_signal.id = signal.id + "_adj" + IntegerToString(attempt + 1);
|
| | |
|
| | | // Record attempt
|
| | | AdjustmentAttempt att;
|
| | | att.attempt_number = attempt + 1;
|
| | | att.price_tweak = (adjusted_signal.price - signal.price) / signal.price;
|
2026-02-04 14:28:59 -05:00 | | | if(MathIsValidNumber(signal.sl) && MathAbs(signal.sl) > 1e-12)
|
| | | att.sl_tweak = adjusted_signal.sl / signal.sl;
|
| | | else
|
| | | att.sl_tweak = 0.0;
|
| | | if(MathIsValidNumber(signal.tp) && MathAbs(signal.tp) > 1e-12)
|
| | | att.tp_tweak = adjusted_signal.tp / signal.tp;
|
| | | else
|
| | | att.tp_tweak = 0.0;
|
| | | if(MathIsValidNumber(signal.volume) && MathAbs(signal.volume) > 1e-12)
|
| | | att.volume_tweak = adjusted_signal.volume / signal.volume;
|
| | | else
|
| | | att.volume_tweak = 0.0;
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | // Try adjusted signal through gates
|
| | | CSignalDecision adj_decision;
|
2026-02-04 14:28:59 -05:00 | | | string validation_reason;
|
| | | if(!ValidateAdjustedSignal(adjusted_signal, validation_reason))
|
| | | {
|
| | | att.passed = false;
|
| | | att.reason = "Sanity check failed: " + validation_reason;
|
| | | decision.attempts[attempt] = att;
|
| | | decision.adjustment_attempts++;
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("⚠️ Adjustment attempt %d skipped (%s)", attempt + 1, validation_reason);
|
| | | continue;
|
| | | }
|
| | |
|
2026-02-24 12:47:37 -05:00 | | | string adj_block_reason;
|
| | | bool adj_passed = m_gate_manager.ProcessSignalEnhanced(adjusted_signal, adj_decision, adj_block_reason);
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | att.passed = adj_passed;
|
| | | att.gate_blocked = adj_passed ? "NONE" : "Multiple";
|
| | | att.reason = adj_passed ? "Passed after adjustment" : "Still blocked";
|
| | |
|
| | | decision.attempts[attempt] = att;
|
| | | decision.adjustment_attempts++;
|
| | |
|
| | | if(adj_passed)
|
| | | {
|
| | | // Success! Multi-pass validation (2 re-validation cycles)
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("✅ Adjustment attempt %d succeeded, starting re-validation...", attempt + 1);
|
| | |
|
| | | bool revalidation_passed = true;
|
| | |
|
| | | for(int revalidation = 1; revalidation <= 2; revalidation++)
|
| | | {
|
| | | CSignalDecision revalidation_decision;
|
2026-02-24 12:47:37 -05:00 | | | string rev_block_reason;
|
| | | bool revalidation_result = m_gate_manager.ProcessSignalEnhanced(adjusted_signal, revalidation_decision, rev_block_reason);
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | if(!revalidation_result)
|
| | | {
|
| | | revalidation_passed = false;
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("❌ Re-validation cycle %d FAILED", revalidation);
|
| | | break;
|
| | | }
|
| | | else
|
| | | {
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("✅ Re-validation cycle %d PASSED", revalidation);
|
| | |
|
| | | // Update decision with latest validation
|
| | | CopyDecision(revalidation_decision, adj_decision);
|
| | | }
|
| | | }
|
| | |
|
| | | if(revalidation_passed)
|
| | | {
|
| | | // All validations passed!
|
| | | CopyDecision(adj_decision, decision);
|
| | | decision.is_adjusted = true;
|
| | |
|
| | | // Update profile statistics
|
| | | profile.total_adjustments++;
|
| | | profile.successful_adjustments++;
|
2025-10-16 18:03:12 -04:00 | | | SetProfile(strategy_name, profile);
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | m_successful_optimizations++;
|
| | |
|
| | | blocking_reason = StringFormat("Optimized on attempt %d, passed 2 re-validations", attempt + 1);
|
| | |
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("🎉 Signal optimized successfully: %s → %s (Volume: %.2f → %.2f)",
|
| | | signal.id, adjusted_signal.id, signal.volume, adjusted_signal.volume);
|
| | |
|
| | | return true;
|
| | | }
|
| | | else
|
| | | {
|
| | | // Re-validation failed, try next adjustment
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("⚠️ Adjustment passed gates but failed re-validation, trying next adjustment...");
|
| | | continue;
|
| | | }
|
| | | }
|
| | |
|
| | | // This attempt failed, log and try next
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("❌ Adjustment attempt %d failed: V%.2f SL%.2f TP%.2f",
|
| | | attempt + 1, att.volume_tweak, att.sl_tweak, att.tp_tweak);
|
| | | }
|
| | |
|
| | | // All attempts exhausted
|
| | | profile.total_adjustments++;
|
2025-10-16 18:03:12 -04:00 | | | SetProfile(strategy_name, profile);
|
2025-10-03 01:38:36 -04:00 | | |
|
| | | blocking_reason = StringFormat("All %d optimization attempts failed", m_max_attempts);
|
| | |
|
| | | if(m_verbose_logging)
|
| | | PrintFormat("🚫 Signal optimization FAILED after %d attempts: %s", m_max_attempts, signal.id);
|
| | |
|
| | | return false;
|
| | | }
|
| | |
|
| | | // Helper to copy decision data
|
| | | void CopyDecision(const CSignalDecision &source, CSignalDecision &dest)
|
| | | {
|
| | | dest.signal_id = source.signal_id;
|
2026-02-24 12:47:37 -05:00 | | | dest.strategy = source.strategy; // CRITICAL: Copy strategy name
|
2025-10-03 01:38:36 -04:00 | | | dest.timestamp = source.timestamp;
|
| | | dest.symbol = source.symbol;
|
| | | dest.timeframe = source.timeframe;
|
| | | dest.original_price = source.original_price;
|
| | | dest.original_type = source.original_type;
|
| | | dest.original_sl = source.original_sl;
|
| | | dest.original_tp = source.original_tp;
|
| | | dest.original_volume = source.original_volume;
|
| | | dest.executed = source.executed;
|
| | | dest.final_price = source.final_price;
|
| | | dest.final_sl = source.final_sl;
|
| | | dest.final_tp = source.final_tp;
|
| | | dest.final_volume = source.final_volume;
|
| | | dest.confidence = source.confidence;
|
| | | dest.volatility = source.volatility;
|
| | | dest.correlation_score = source.correlation_score;
|
| | | dest.market_regime = source.market_regime;
|
| | |
|
| | | // Copy gate results
|
| | | for(int i = 0; i < 8; i++)
|
| | | {
|
| | | dest.gate_results[i] = source.gate_results[i];
|
| | | dest.gate_reasons[i] = source.gate_reasons[i];
|
| | | dest.gate_timestamps[i] = source.gate_timestamps[i];
|
| | |
|
| | | for(int j = 0; j < 5; j++)
|
| | | dest.gate_tweaks[i][j] = source.gate_tweaks[i][j];
|
| | | }
|
| | | }
|
| | |
|
| | | // Get optimization statistics
|
| | | void GetStatistics(int &total, int &successful, double &success_rate)
|
| | | {
|
| | | total = m_total_optimizations;
|
| | | successful = m_successful_optimizations;
|
| | | success_rate = total > 0 ? (double)successful / total * 100.0 : 0.0;
|
| | | }
|
| | |
|
| | | // Print detailed report
|
| | | void PrintOptimizationReport()
|
| | | {
|
| | | PrintFormat("\n=== 📊 Adaptive Signal Optimization Report ===");
|
| | | PrintFormat("Total Optimizations: %d", m_total_optimizations);
|
| | | PrintFormat("Successful: %d (%.1f%%)", m_successful_optimizations,
|
| | | m_total_optimizations > 0 ? (double)m_successful_optimizations/m_total_optimizations*100 : 0);
|
| | |
|
| | | Print("\nStrategy-Specific Performance:");
|
| | |
|
| | | // Print top 5 strategies by success rate
|
| | | // (Full implementation would iterate through all profiles)
|
| | | Print("(Strategy profiles tracked internally)");
|
| | | }
|
| | |
|
| | | // Print complete gate journey
|
| | | void PrintCompleteGateJourney(CAdaptiveDecision &decision)
|
| | | {
|
| | | if(decision.complete_journey_length == 0) return;
|
| | |
|
| | | PrintFormat("\n📍 ==== COMPLETE GATE JOURNEY for %s ====", decision.signal_id);
|
| | | PrintFormat("Original Signal: %s | Total Attempts: %d | Is Adjusted: %s",
|
| | | decision.original_signal_id != "" ? decision.original_signal_id : decision.signal_id,
|
| | | decision.adjustment_attempts,
|
| | | decision.is_adjusted ? "YES" : "NO");
|
| | |
|
| | | int current_attempt = 0;
|
| | |
|
| | | for(int i = 0; i < decision.complete_journey_length; i++)
|
| | | {
|
| | | GateJourneyStep step = decision.complete_journey[i];
|
| | |
|
| | | // Check if new attempt started
|
| | | if(i > 0 && step.gate_index == 0)
|
| | | {
|
| | | current_attempt++;
|
| | | PrintFormat("\n--- Attempt %d ---", current_attempt + 1);
|
| | | }
|
| | | else if(i == 0)
|
| | | {
|
| | | PrintFormat("--- Attempt 1 (Original Signal) ---");
|
| | | }
|
| | |
|
| | | // Print gate status
|
| | | string status_icon = step.passed ? "✅" : "❌";
|
| | | PrintFormat(" Gate %d [%s]: %s %s",
|
| | | step.gate_index + 1,
|
| | | step.gate_name,
|
| | | status_icon,
|
| | | step.passed ? "PASSED" : "BLOCKED");
|
| | |
|
| | | if(!step.passed)
|
| | | PrintFormat(" Reason: %s", step.reason);
|
| | |
|
| | | // Print adjustments if any
|
| | | if(step.was_adjusted)
|
| | | {
|
| | | PrintFormat(" 🔧 ADJUSTMENTS:");
|
| | |
|
| | | if(step.price_before != step.price_after)
|
| | | PrintFormat(" Price: %.5f → %.5f (%+.2f%%)",
|
| | | step.price_before, step.price_after,
|
| | | (step.price_after - step.price_before) / step.price_before * 100);
|
| | |
|
| | | if(step.sl_before != step.sl_after)
|
| | | PrintFormat(" SL: %.5f → %.5f (%+.2f%%)",
|
| | | step.sl_before, step.sl_after,
|
| | | (step.sl_after - step.sl_before) / step.sl_before * 100);
|
| | |
|
| | | if(step.tp_before != step.tp_after)
|
| | | PrintFormat(" TP: %.5f → %.5f (%+.2f%%)",
|
| | | step.tp_before, step.tp_after,
|
| | | (step.tp_after - step.tp_before) / step.tp_before * 100);
|
| | |
|
| | | if(step.volume_before != step.volume_after)
|
| | | PrintFormat(" Volume: %.2f → %.2f (%+.2f%%)",
|
| | | step.volume_before, step.volume_after,
|
| | | (step.volume_after - step.volume_before) / step.volume_before * 100);
|
| | | }
|
| | | }
|
| | |
|
| | | PrintFormat("\n🏁 FINAL OUTCOME: %s", decision.executed ? "✅ EXECUTED" : "❌ REJECTED");
|
| | | if(decision.executed)
|
| | | {
|
| | | PrintFormat(" Final Price: %.5f | SL: %.5f | TP: %.5f | Volume: %.2f",
|
| | | decision.final_price, decision.final_sl, decision.final_tp, decision.final_volume);
|
| | | }
|
| | | PrintFormat("==========================================\n");
|
| | | }
|
| | | };
|
| | |
|
| | | #endif
|