mql5/Experts/Advisors/DualEA/Include/AdaptiveSignalOptimizer.mqh

715 lines
26 KiB
MQL5
Raw Permalink Normal View History

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