//+------------------------------------------------------------------+ //| PaperEA_v2.mq5 - Efficient DualEA Main | //+------------------------------------------------------------------+ #property copyright "DualEA Enhanced Paper System" #property version "2.0" #property strict //+------------------------------------------------------------------+ //| INCLUDES - COMPREHENSIVE MODULE INTEGRATION | //+------------------------------------------------------------------+ // NEW: Auto-learning gate system (replaces old GateManager/GateRegistry) #include "..\Include\GateSystemAutoLearning.mqh" // Core Services #include "..\Include\KnowledgeBase.mqh" #include "..\Include\TradeManager.mqh" #include "..\Include\PolicyEngine.mqh" // Telemetry System #include "../Include/Telemetry.mqh" #include "../Include/TelemetryStandard.mqh" #include "../Include/SessionManager.mqh" #include "../Include/CorrelationManager.mqh" #include "../Include/VolatilitySizer.mqh" // Centralized gate orchestration macros #include "..\\Include\\GatingPipeline.mqh" // Adaptive Signal Optimization System #include "..\Include\AdaptiveSignalOptimizer.mqh" #include "..\Include\PolicyUpdater.mqh" #include "..\Include\PositionReviewer.mqh" #include "..\Include\GateLearningSystem.mqh" #include "..\Include\UnifiedTradeLogger.mqh" // Unified System Components #include "..\Include\ConfigManager.mqh" #include "..\Include\EventBus.mqh" // Standard Libraries #include #include // Logging level constants for ShouldLog() (unique names to avoid macro collisions) #ifndef PAPEREA_LOG_DEBUG #define PAPEREA_LOG_DEBUG 3 #endif #ifndef PAPEREA_LOG_INFO #define PAPEREA_LOG_INFO 2 #endif #ifndef PAPEREA_LOG_WARNING #define PAPEREA_LOG_WARNING 1 #endif #ifndef PAPEREA_LOG_ERROR #define PAPEREA_LOG_ERROR 0 #endif // Basic LOG macro used widely across this EA #ifndef LOG #define LOG(msg) Print(msg) #endif // Defensive: some includes may define ShouldLog as a macro. // We require the function declared later in this file. #ifdef ShouldLog #undef ShouldLog #endif #ifndef PAPEREA_SHOULD_LOG #define PAPEREA_SHOULD_LOG(level) (ShouldLog((int)(level))) #endif // Advanced Regime Detection #include "..\\Include\\AdvancedRegimeDetector.mqh" // Nuclear-Grade 100-Level Optimization #include "NuclearOptimization.mqh" // Regime-based parameter adaptation #include "regime_adaptation.mqh" // Position management #include "..\\Include\\ModelPredictor.mqh" // Policy hot-reload bridge #include "..\\Include\\PolicyHttpBridge.mqh" // Strategy Implementations - Per-asset registry (internally includes concrete strategy headers) #include "..\\Include\\Strategies\\AssetRegistry.mqh" // Strategy selector #include "..\Include\StrategySelector.mqh" // P0/P1 COMPREHENSIVE HARDENING & INTELLIGENCE UPGRADE #include "..\Include\DualEA_MasterIntegration.mqh" // P0-P5 ADVANCED HARDENING & INTELLIGENCE INTEGRATION #include "..\Include\PaperEA_v2_P0P5_Integration.mqh" // Additional components referenced by PaperEA_v2 #include "..\Include\CGateAudit.mqh" #include "..\Include\CInsightsRealtime.mqh" // Learning and Export Systems - Using file-based export for strategy tester compatibility #include "..\Include\LearningBridge.mqh" #include "..\Include\FileBasedFeatureExport.mqh" // Replaces DLL-based export #include "..\Include\StrategySignalGenerators.mqh" // Extensible strategy signal generators #include "..\Include\IncrementalInsightEngine.mqh" // Real-time O(1) statistics engine // ===================[ EFFICIENT GATE SYSTEM CONFIGURATION ]=================== input group "=== EFFICIENT 8-STAGE GATE SYSTEM ===" input bool UseEfficientGates = true; // Use optimized gate implementation input int GateProcessingMode = 0; // 0=Full, 1=RiskOnly, 2=Fast, 3=Bypass input bool ShadowMode = false; // Disable shadow mode for real trading input bool LiveTradingEnabled = true; // Enable demo trading input bool LoadONNXModels = true; // Load dynamic threshold ONNX models input bool AutoReloadModels = true; // Auto-reload models when updated input int RetrainIntervalHours = 24; // Hours between model retraining input bool G5_PerformanceWaxEnabled = true; // Enable performance validation input double G5_MinWinRate = 0.45; // Min strategy win rate input int G5_LookbackTrades = 20; // Trades to look back input bool G6_MLPolishEnabled = true; // Enable ML validation input double G6_MLConfidenceThreshold = 0.55; // ML confidence threshold input bool G7_LiveCleanEnabled = true; // Enable market condition checks input double G7_MaxSpreadPercent = 0.05; // Max spread (5%) // Risk gates ALWAYS enforced (G1, G4, G7, G8) - per MQL5 risk management best practices // IMPORTANT: "Paper" refers to DEMO ACCOUNT, NOT simulated trades! // - PaperEA executes REAL MT5 trades via OrderSend() on DEMO accounts // - LiveEA executes REAL MT5 trades via OrderSend() on LIVE accounts // - Both use identical execution logic through TradeManager // - All positions tracked by MT5's native position system // - Use PositionSelect(), PositionGetDouble(), OnTradeTransaction() for real position data // ===================[ NO SIMULATION - REAL EXECUTION ONLY ]=================== // Using CFeaturesKB from Include\KnowledgeBase.mqh //+------------------------------------------------------------------+ //| INPUT PARAMETERS - COMPREHENSIVE SYSTEM CONFIGURATION | //+------------------------------------------------------------------+ // ===================[ TRADING INPUTS ]=================== // Symbol and Timeframe automatically detected from chart input double LotSize = 0.0; // Let VolatilitySizer decide input int MagicNumber = 12345; input double StopLossPips_Input = 150; // 50:300:25 - Universal SL range (user initial value) input double TakeProfitPips_Input = 300; // 100:600:50 - Universal TP range (user initial value) // Modifiable runtime parameters (auto-adapted by universal detector) double StopLossPips = 150; double TakeProfitPips = 300; input bool TrailEnabled = true; input int TrailType = 2; // 0=fixed, 2=ATR input int TrailActivationPoints = 30; // 10:100:10 - Trail activation threshold input int TrailDistancePoints = 20; // 10:60:5 - Trail distance from price input int TrailStepPoints = 5; // 2:15:1 - Trail step increment input int TrailATRPeriod = 14; // 7:28:7 - ATR period for dynamic trailing input double TrailATRMultiplier = 2.0; // 1.0:4.0:0.5 - ATR multiplier for trail distance input bool UsePositionManager = true; input int PMMaxOpenPositions = 10; // 3:20:1 - Max concurrent positions input bool NoConstraintsMode = true; // ===================[ GATING/INSIGHTS ]=================== input bool UseInsightsGating = true; input int GateMinTrades = 0; input double GateMinWinRate = 0.00; input double GateMinExpectancyR = -10.0; input double GateMaxDrawdownR = 1000000.0; input double GateMinProfitFactor = 0.00; input bool InsightsAutoBuild = true; input int InsightsStaleHours = 48; input int InsightsMinSourceAdvanceHours = 48; input int InsightsMinIntervalHours = 24; input bool InsightsCheckOnTimer = true; input int InsightsRebuildTimeoutMs = 1800000; input bool ExploreOnNoSlice = true; input int ExploreMaxPerSlice = 100; input int ExploreMaxPerSlicePerDay = 100; // ===================[ STRATEGY SELECTOR ]=================== input bool UseStrategySelector = true; input double SelW_PF = 1.0; // 0.5:2.0:0.25 - Profit factor selection weight input double SelW_Exp = 1.0; // 0.5:2.0:0.25 - Expectancy selection weight input double SelW_WR = 0.5; // 0.2:1.5:0.15 - Win rate selection weight input double SelW_DD = 0.3; // 0.1:1.0:0.1 - Drawdown selection weight input bool SelStrictThresholds = false; // 0:1:1 - Use strict vs probabilistic thresholds input bool SelUseRecency = true; // 0:1:1 - Weight recent performance more heavily input int SelRecentDays = 14; // 7:30:7 - Recent performance lookback days input double SelRecAlpha = 0.5; // 0.3:0.8:0.1 - Recency weighting decay factor input int GateAuditMinSignals = 5; // Minimum signals per audit window before alerting input int GateAuditMinUptimeMin = 60; // Grace period (minutes) after initialization input int GateAuditAlertCooldownMin = 60; // Cooldown between repeated alerts // ===================[ ADVANCED GATING/TUNING ]=================== input bool P5_AutoDisableEnable = true; // 0:1:1 - Auto-disable underperforming strategies input double P5_MinPF_Input = 1.20; // 1.10:1.50:0.10 - Minimum profit factor threshold (user initial) input double P5_MinWR_Input = 0.45; // 0.40:0.60:0.05 - Minimum win rate threshold (user initial) input double P5_MinExpR = -0.05; // -0.20:0.10:0.05 - Minimum expectancy R threshold // Modifiable runtime gating thresholds (auto-adapted by ML) double P5_MinPF = 1.20; double P5_MinWR = 0.45; input int P5_AutoDisableCooldownM = 1440; // 720:2880:360 - Cooldown minutes before re-enable input bool P5_AutoReenable = true; // 0:1:1 - Auto-reenable after cooldown input bool P5_AutoTuneEnable = true; // 0:1:1 - Auto-tune strategy parameters input int P5_AutoTuneEveryMin = 60; // 30:240:30 - Auto-tune interval minutes input bool P5_TimerRescoreEnable = true; // 0:1:1 - Enable periodic rescoring input int P5_TimerRescoreEveryMin = 60; // 30:240:30 - Rescoring interval minutes input bool P5_CorrPruneEnable = true; // 0:1:1 - Enable correlation pruning input double P5_CorrMax = 0.80; // 0.70:0.95:0.05 - Max correlation threshold input int P5_CorrLookbackDays = 30; // 14:90:14 - Correlation lookback period input bool P5_MTFConfirmEnable = true; // 0:1:1 - Enable multi-timeframe confirmation input string P5_MTFHigherTFs = "H1,H4"; // Fixed: H1,H4,H1+D1 - Multi-timeframe pairs input int P5_MTFMinAgree = 1; // 1:3:1 - Minimum MTF confirmations required input bool P5_StabilityGateEnable = false; // 0:1:1 - Enable stability filtering input int P5_StabilityWindowDays = 14; // 7:30:7 - Stability assessment window input double P5_StabilityMaxStdR = 1.00; // 0.5:2.0:0.25 - Max standard deviation R for stability input bool P5_PersistLossCounters = false; // 0:1:1 - Persist loss counters across sessions input bool P5_PickBestEnable = false; // 0:1:1 - Enable best strategy selection mode // ===================[ RISK MANAGEMENT ]=================== input int MaxOpenPositions = 0; input bool GuardsEnabled = true; input double GuardMaxSpreadPoints = 0.0; input bool ATRRegimeEnable = false; input int ATRRegimePeriod = 14; input int ATRRegimeLookback = 500; input double ATRMinPercentile = 0.0; input double ATRMaxPercentile = 100.0; input double ATRRegimeMinATRPct = 0.0; input double ATRRegimeMaxATRPct = 1000.0; input bool UseCircuitBreakers = true; // ENABLED for safety input double CBDailyLossLimitPct_Input = 5.0; // 2.0:10.0:1.0 - Daily loss circuit breaker % (user initial) input double CBDrawdownLimitPct = 10.0; // 5.0:20.0:2.5 - Drawdown circuit breaker % input int CBCooldownMinutes = 60; // 30:180:30 - Circuit breaker cooldown period // Modifiable runtime circuit breaker (auto-adapted by ML) double CBDailyLossLimitPct = 5.0; // ===================[ NEWS & SESSION FILTERING ]=================== input bool UseNewsFilter = true; input int NewsBufferBeforeMin = 30; // 15:120:15 - News buffer before events (minutes) input int NewsBufferAfterMin = 30; // 15:120:15 - News buffer after events (minutes) input int NewsImpactMin = 2; // 1:5:1 - Minimum news impact level to filter input bool NewsUseFile = true; input string NewsFileRelPath = "DualEA\\news_blackouts.csv"; input bool UsePromotionGate = false; input bool PromoLiveOnly = false; input int PromoStartHour = 0; input int PromoEndHour = 24; input bool UseRegimeGate = true; // ENABLED for adaptive parameters input int RegimeATRPeriod = 14; input double RegimeMinATRPct = 0.5; // Low volatility threshold input double RegimeMaxATRPct = 2.5; // High volatility threshold input bool RegimeTagTelemetry = true; // Track regime in telemetry input string RegimeMethod = "combined"; // Use ATR + ADX input int RegimeADXPeriod = 14; input double RegimeADXTrendThreshold = 25.0; input int RegimeStrongTrendThreshold = 35.0; input double RegimeVolatilityHigh = 75.0; input double RegimeVolatilityLow = 25.0; input int CircuitCooldownSec = 0; // ===================[ SESSION & CORRELATION MANAGEMENT ]=================== input bool UseSessionManager = true; input int SessionEndHour = 20; // 0:23:1 - Session end hour input int MaxTradesPerSession = 10; // 3:25:2 - Maximum trades per session input int SessionTZOffsetMinutes= 0; // -720:720:60 - Session timezone offset (minutes) input string SessionWindowsSpec = ""; input bool UseCorrelationManager = true; input double MaxCorrelationLimit = 0.7; // 0.5:0.9:0.05 - Max allowed position correlation input int CorrLookbackDays = 30; // 7:90:7 - Correlation lookback period (days) // 7:90:7 - Correlation lookback period (days) input bool UseVolatilitySizer = true; input int VolSizerATRPeriod = 14; // 7:28:7 - ATR period for volatility sizing input double VolSizerBaseATRPct = 1.0; input double VolSizerMinMult = 0.1; // 0.05:0.5:0.05 - Min position size multiplier input double VolSizerMaxMult = 3.0; // 1.5:5.0:0.5 - Max position size multiplier input double VolSizerTargetRisk_Input = 1.0; // 0.5:3.0:0.25 - Target risk per trade % (user initial) // Modifiable runtime risk sizing (auto-adapted by universal detector) double VolSizerTargetRisk = 1.0; // ===================[ POLICY ENGINE ]=================== input bool UsePolicyGating = true; input bool DefaultPolicyFallback = true; input bool FallbackDemoOnly = true; input bool FallbackWhenNoPolicy = true; input bool UsePolicyEngine = true; // ENABLED for ML optimization input string PolicyServerUrl = "http://127.0.0.1:8000"; input string PolicyFilePath = "DualEA\\policy.json"; input int PolicyHttpPollPercent = 70; input int HotReloadIntervalSec = 10; input string ConfigFilePath = "DualEA\\config\\nuclear_config.json"; // ===================[ TRADING HOURS & SESSIONS ]=================== input bool UseTradingHours = false; // 0:1:1 - Enable trading hours filtering input int TradingStartHour = 7; // 0:23:1 - Trading session start hour input int TradingEndHour = 20; // 0:23:1 - Trading session end hour // ===================[ TELEMETRY & LOGGING ]=================== input bool TelemetryEnabled = true; input int TelemetryLevel = 1; input string TelemetryExperiment = ""; input int TelemetryBufferMax = 256; input string TelemetryDir = "DualEA\\telemetry"; input bool GateLogThrottleEnabled = true; input int GateLogCooldownSec = 30; input int TelemetryFlushIntervalSec = 0; input int Verbosity = 1; // ===================[ ADAPTIVE PARAMETER ENGINE ]=================== input bool AutoDetectParameters = true; // Enable smart parameter detection input string RiskProfile = "MODERATE"; // CONSERVATIVE, MODERATE, AGGRESSIVE, AUTO input bool EnableMLAdaptation = true; // Enable machine learning parameter adaptation input int AdaptationIntervalMinutes = 60; // Hourly adaptation frequency input bool AllowManualOverride = true; // Allow manual parameter override // ===================[ ADDITIONAL RISK CONTROLS ]=================== input double SpreadMaxPoints = 0.0; input int SessionStartHour = 0; input int SessionMaxTrades = 0; input int MaxTradesPerBar = 0; input int PaperAggroLevel = 50; input double MaxDailyLossPct = 0.0; input double MaxDrawdownPct = 0.0; input double MinMarginLevel = 0.0; input int ConsecutiveLossLimit = 0; input int GlobalSL_Points = 500; // 200:1000:100 - Global stop loss points input int GlobalTP_Points = 1000; // 400:2000:200 - Global take profit points // ===================[ DEBUG & DEVELOPMENT ]=================== input bool DebugTrailing = false; input bool KBDebugInit = true; input bool TrainerLSTM_Enable = false; input int TrainerLSTM_MinSeq = 50; input int TrainerLSTM_MaxSeq = 500; input bool TrainerLSTM_UseRecency = true; input bool HeartbeatEnabled = true; input int HeartbeatMinutes = 15; input bool HeartbeatVerbose = true; // ===================[ ONNX RUNTIME PREDICTOR INPUTS ]=================== input bool UseOnnxPredictor = true; input string OnnxModelPath = "DualEA\\signal_model.onnx"; input string OnnxConfigJsonPath = "DualEA\\onnx_config.json"; input string OnnxConfigIniPath = "DualEA\\onnx_config.ini"; input bool OnnxPathsUseCommonDir = true; // ===================[ LEGACY GATE PARAMETERS ]=================== #include "AdaptiveEngine.mqh" // Global adaptive engines CAdaptiveEngine g_adaptive_engine; CMLAdaptationEngine g_ml_engine; // CGateAudit g_gate_audit defined in CGateAudit.mqh - do not redefine input bool UseGateSystem = true; input string LearningDataPath = "DualEA\\PaperData"; // Relative to Common Files (Strategy Tester compatible) input int MaxLearningRecords = 10000; // ===================[ TRADE FREQUENCY GATING ]=================== input int PaperTradeCooldownSec = 900; // 15 min default //+------------------------------------------------------------------+ //| STRUCTURES | //+------------------------------------------------------------------+ // TradingSignal struct is defined in GateManager.mqh //+------------------------------------------------------------------+ //| GLOBAL STATE - COMPREHENSIVE SYSTEM VARIABLES | //+------------------------------------------------------------------+ // ===================[ CORE SYSTEM MANAGERS ]=================== CLearningBridge *g_learning_bridge = NULL; // NEW: Efficient Gate Manager replaces old CGateManager CEfficientGateManager *g_gate_manager = NULL; CTradeManager *g_trade_manager = NULL; CTelemetryStandard *g_telemetry = NULL; CStrategySignalRegistry *g_signal_registry = NULL; // Extensible strategy signal generators CIncrementalInsightEngine* g_insight_engine = NULL; // Definition for extern in IncrementalInsightEngine.mqh // g_paper_positions removed - using REAL MT5 positions via PositionSelect() // ===================[ DYNAMIC PARAMETER ENGINE ]=================== // NOTE: CalculateDynamicParameters() is now a method in CAdaptiveEngine class (AdaptiveEngine.mqh) // No standalone function needed - use g_adaptive_engine.CalculateDynamicParameters() instead struct AdaptiveParams { double base_sl_mult; double base_tp_mult; double risk_multiplier; double volatility_factor; double spread_factor; double timeframe_factor; double ml_adjustment; }; AdaptiveParams g_adaptive; double g_current_atr = 0.0; double g_current_spread = 0.0; datetime g_last_adaptation = 0; // ===================[ TIMER/COOLDOWN FOR TRADE FREQUENCY GATING ]=================== static datetime last_paper_trade_time = 0; static int last_processed_minute = -1; // ===================[ PENDING ORDERS/DEALS TRACKING FOR EVENT HANDLERS ]=================== ulong g_pending_orders[]; string g_pending_orders_strat[]; ulong g_pending_deals[]; string g_pending_deals_strat[]; // ===================[ TRACKED POSITIONS FOR ANALYTICS/CLOSURE ]=================== ulong g_pos_ids[]; string g_pos_strats[]; double g_pos_entry_price[]; double g_pos_initial_risk[]; datetime g_pos_start_time[]; int g_pos_type[]; double g_pos_max_price[]; double g_pos_min_price[]; // ===================[ NEWS BLACKOUT CACHE ]=================== string g_news_key[]; datetime g_news_from[]; datetime g_news_to[]; int g_news_impact[]; // ===================[ SESSION AND RISK STATE ]=================== datetime g_session_start = 0; int g_session_day = 0; double g_session_equity_start = 0.0; double g_equity_highwater = 0.0; // ===================[ CIRCUIT BREAKER STATE ]=================== bool g_cb_active = false; datetime g_cb_trigger_time = 0; string g_cb_last_cause = ""; double g_cb_last_threshold = 0.0; double g_cb_last_value = 0.0; // ===================[ TELEMETRY, GATING, AND SELECTOR STATE ]=================== string g_gate_log_keys[]; datetime g_gate_log_last_ts[]; // ===================[ GATING/INSIGHTS/SELECTOR ARRAYS ]=================== string g_gate_strat[]; string g_gate_sym[]; int g_gate_tf[]; int g_gate_cnt[]; double g_gate_wr[]; double g_gate_avgR[]; double g_gate_pf[]; double g_gate_dd[]; // ===================[ POLICY CACHE ]=================== bool g_policy_loaded = false; double g_policy_min_conf = 0.0; string g_pol_strat[]; string g_pol_sym[]; int g_pol_tf[]; double g_pol_p[]; double g_pol_sl[]; double g_pol_tp[]; double g_pol_trail[]; int g_policy_last_len = -1; int g_config_last_len = -1; // ===================[ EXPLORATION TRACKING ]=================== string g_exp_keys[]; int g_exp_weeks[]; int g_exp_counts[]; string g_explore_pending_key = ""; string g_exp_day_keys[]; int g_exp_day_days[]; int g_exp_day_counts[]; // ===================[ LOSS COUNTERS ]=================== string g_loss_sym[]; long g_loss_mag[]; int g_loss_cnt[]; // ===================[ TELEMETRY POINTERS ]=================== CTelemetryStandard* g_tel_standard = NULL; CSessionManager* g_session_manager = NULL; CCorrelationManager* g_correlation_manager = NULL; CVolatilitySizer* g_volatility_sizer = NULL; CPolicyEngine* g_policy_engine = NULL; CInsightsRealtime* g_ins_rt = NULL; // Policy bridge for file-change detection CPolicyHttpBridge* g_policy_bridge = NULL; // ===================[ ADAPTIVE OPTIMIZATION SYSTEM ]=================== CAdaptiveSignalOptimizer* g_adaptive_optimizer = NULL; CPolicyUpdater* g_policy_updater = NULL; CPositionReviewer* g_position_reviewer = NULL; CGateLearningSystem* g_gate_learning = NULL; CUnifiedTradeLogger* g_trade_logger = NULL; CAdvancedRegimeDetector* g_regime_detector = NULL; // ===================[ NUCLEAR-GRADE 100-LEVEL OPTIMIZATION ]=================== COptimizedCorrelationEngine* g_nuclear_correlation = NULL; CRiskMetricsCalculator* g_risk_metrics = NULL; CKellyPositionSizer* g_kelly_sizer = NULL; CConfigurationManager* g_config_manager = NULL; CPolicyStateManager* g_policy_state = NULL; CRingBuffer* g_log_queue = NULL; // ===================[ SELECTOR, POSITION MANAGER, FEATURES LOGGER, KNOWLEDGE BASE, TRADE MANAGER ]=================== CStrategySelector* g_selector = NULL; CPositionManager* g_position_manager = NULL; // Definition for extern in CPositionManager.mqh CFeaturesKB* g_features = NULL; CKnowledgeBase* g_kb = NULL; CTelemetry* g_telemetry_base = NULL; CModelPredictor g_model_predictor; bool g_model_predictor_ready = false; string g_mlp_feature_columns[]; double g_mlp_feature_buffer[]; bool g_mlp_warned_unready = false; bool g_mlp_warned_features = false; // ===================[ ONNX PREDICTOR HELPERS ]=================== // Define ENUM_STATISTICS values if not already defined (for TesterStatistics compatibility) #ifndef STAT_BALANCE_DDREL_PERCENT #define STAT_BALANCE_DDREL_PERCENT 0 #define STAT_SHARPE_RATIO 1 #define STAT_TRADES_WIN 2 #define STAT_TRADES_LOSS 3 #define STAT_TRADES_TOTAL 4 #endif string ResolveDualEAPath(const string relative_path, const bool prefer_common) { string trimmed = relative_path; StringTrimLeft(trimmed); StringTrimRight(trimmed); if(StringLen(trimmed) == 0) return ""; if(StringFind(trimmed, ":\\") >= 0 || StringSubstr(trimmed, 0, 2) == "\\\\") return trimmed; if(prefer_common) return TerminalInfoString(TERMINAL_COMMONDATA_PATH) + "\\Files\\" + trimmed; return trimmed; } double SafeDivision(const double numerator, const double denominator) { if(MathAbs(denominator) < 1e-9) return 0.0; return numerator / denominator; } int CsvCountTokens(const string csv) { string tmp = csv; StringTrimLeft(tmp); StringTrimRight(tmp); if(StringLen(tmp) == 0) return 0; string parts[]; int count = StringSplit(tmp, ',', parts); int cleaned = 0; for(int i = 0; i < count; i++) { string s = parts[i]; StringTrimLeft(s); StringTrimRight(s); if(StringLen(s) == 0) continue; cleaned++; } return cleaned; } bool CsvContainsToken(const string &haystack_csv, const string &token) { string tmp = haystack_csv; StringTrimLeft(tmp); StringTrimRight(tmp); if(StringLen(tmp) == 0) return false; string parts[]; int count = StringSplit(tmp, ',', parts); for(int i = 0; i < count; i++) { string s = parts[i]; StringTrimLeft(s); StringTrimRight(s); if(s == token) return true; } return false; } string CsvMergeUnique(const string &base_csv, const string &extra_csv) { string base = base_csv; StringTrimLeft(base); StringTrimRight(base); string extra = extra_csv; StringTrimLeft(extra); StringTrimRight(extra); string result = base; if(StringLen(extra) == 0) return result; string tokens[]; int count = StringSplit(extra, ',', tokens); for(int i = 0; i < count; i++) { string s = tokens[i]; StringTrimLeft(s); StringTrimRight(s); if(StringLen(s) == 0) continue; if(StringLen(result) == 0) { result = s; continue; } if(!CsvContainsToken(result, s)) result += "," + s; } return result; } void ShutdownModelPredictor() { if(g_model_predictor_ready) { g_model_predictor.Shutdown(); g_model_predictor_ready = false; LOG("ONNX predictor shut down"); } ArrayResize(g_mlp_feature_columns, 0); ArrayResize(g_mlp_feature_buffer, 0); } bool BuildPredictorFeatureVector(const TradingSignal &signal, const string &strategy_name, const CAdaptiveDecision *decision_ptr, const string &status, const string &reason, const datetime ts, double &buffer[]) { int feat_count = ArraySize(g_mlp_feature_columns); if(feat_count == 0) return false; ArrayResize(buffer, feat_count); string resolved_symbol = (signal.symbol != "") ? signal.symbol : _Symbol; string resolved_strategy = (strategy_name != "") ? strategy_name : signal.strategy; string resolved_regime = signal.regime; if(resolved_regime == "") resolved_regime = GetMarketRegime(); string resolved_market_regime = (signal.market_regime != "") ? signal.market_regime : resolved_regime; string normalized_status = status; StringToLower(normalized_status); string normalized_reason = reason; StringToLower(normalized_reason); double base_price = (signal.price != 0.0) ? signal.price : SymbolInfoDouble(_Symbol, SYMBOL_BID); double base_volume = (signal.volume != 0.0) ? signal.volume : LotSize; double base_sl = (signal.sl != 0.0) ? signal.sl : base_price; double base_tp = (signal.tp != 0.0) ? signal.tp : base_price; double final_price = base_price; double final_volume = base_volume; double final_sl = base_sl; double final_tp = base_tp; bool executed = false; bool is_adjusted = false; int adjustment_attempts = 0; int gate_passed = 0; double gate_pass_flags[8]; ArrayInitialize(gate_pass_flags, 0.0); double volume_delta_pct = 0.0; double price_delta_pct = 0.0; double sl_scale = 1.0; double tp_scale = 1.0; if(CheckPointer(decision_ptr) != POINTER_INVALID) { const CAdaptiveDecision *decision = decision_ptr; final_price = (decision.final_price != 0.0) ? decision.final_price : final_price; final_volume = (decision.final_volume != 0.0) ? decision.final_volume : final_volume; final_sl = (decision.final_sl != 0.0) ? decision.final_sl : final_sl; final_tp = (decision.final_tp != 0.0) ? decision.final_tp : final_tp; executed = decision.executed; is_adjusted = decision.is_adjusted; adjustment_attempts = decision.adjustment_attempts; gate_passed = CountPassedGates(*decision); for(int gi = 0; gi < 8; gi++) gate_pass_flags[gi] = decision.gate_results[gi] ? 1.0 : 0.0; volume_delta_pct = SafeDivision(final_volume - decision.original_volume, decision.original_volume) * 100.0; price_delta_pct = SafeDivision(final_price - decision.original_price, decision.original_price) * 100.0; sl_scale = SafeDivision(final_sl, decision.original_sl == 0.0 ? final_sl : decision.original_sl); tp_scale = SafeDivision(final_tp, decision.original_tp == 0.0 ? final_tp : decision.original_tp); } if(!MathIsValidNumber(sl_scale) || sl_scale == 0.0) sl_scale = 1.0; if(!MathIsValidNumber(tp_scale) || tp_scale == 0.0) tp_scale = 1.0; MqlDateTime mt; TimeToStruct(ts, mt); int hour_value = mt.hour; int timeframe_value = (signal.timeframe != 0) ? signal.timeframe : _Period; double volatility_value = (signal.volatility != 0.0) ? signal.volatility : GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period); double correlation_value = (signal.correlation != 0.0) ? signal.correlation : GetCorrelation(); double confidence_value = signal.confidence; if(confidence_value <= 0.0 || confidence_value > 1.0) confidence_value = 0.5; for(int i = 0; i < feat_count; i++) { string col = g_mlp_feature_columns[i]; double value = 0.0; if(col == "price") value = final_price; else if(col == "volume") value = final_volume; else if(col == "sl") value = final_sl; else if(col == "tp") value = final_tp; else if(col == "confidence") value = confidence_value; else if(col == "volatility") value = volatility_value; else if(col == "correlation") value = correlation_value; else if(col == "hour") value = hour_value; else if(col == "timeframe") value = timeframe_value; else if(col == "order_type") value = signal.type; else if(col == "gate_passed_count") value = gate_passed; else if(col == "adjustment_attempts") value = adjustment_attempts; else if(col == "volume_change_pct") value = volume_delta_pct; else if(col == "price_change_pct") value = price_delta_pct; else if(col == "sl_scale") value = sl_scale; else if(col == "tp_scale") value = tp_scale; else if(col == "executed") value = executed ? 1.0 : 0.0; else if(col == "is_adjusted") value = is_adjusted ? 1.0 : 0.0; else if(StringSubstr(col, 0, 4) == "gate" && StringFind(col, "_pass") > 0) { int pass_pos = StringFind(col, "_pass"); int gate_index = -1; if(pass_pos > 4) { int length = (int)(pass_pos - 4); string gate_token = StringSubstr(col, 4, length); gate_index = (int)StringToInteger(gate_token) - 1; } if(gate_index >= 0 && gate_index < 8) value = gate_pass_flags[gate_index]; } else if(col == "strategy_code" || col == "symbol_code" || col == "status_code" || col == "regime_code" || col == "market_regime_code" || col == "reason_code") { string field = ""; string label = ""; if(col == "strategy_code") { field = "strategy"; label = resolved_strategy; } else if(col == "symbol_code") { field = "symbol"; label = resolved_symbol; } else if(col == "status_code") { field = "status"; label = normalized_status; } else if(col == "regime_code") { field = "regime"; label = resolved_regime; } else if(col == "market_regime_code") { field = "market_regime"; label = resolved_market_regime; } else if(col == "reason_code") { field = "reason"; label = normalized_reason; } value = g_model_predictor.EncodeCategorical(field, label); } buffer[i] = value; } return true; } void GateSanitizeTelemetryReporter(const string gate_name, TradingSignal &signal) { if(!TelemetryEnabled) return; if(CheckPointer(g_telemetry_base) == POINTER_INVALID) return; double base_price = MathMax(0.0001, MathAbs(signal.price)); double sl_gap = MathAbs(signal.price - signal.sl) / base_price; double tp_gap = MathAbs(signal.tp - signal.price) / base_price; double severity = (sl_gap + tp_gap) * 0.5; if(CheckPointer(g_gate_learning) != POINTER_INVALID) { // Note: RecordSanitizationEvent method not available - disabled // g_gate_learning.RecordSanitizationEvent(gate_name, signal.price, signal.sl, signal.tp, signal.volume); } if(CheckPointer(g_tel_standard) != POINTER_INVALID) { g_tel_standard.LogGateSanitize((signal.symbol == "" ? _Symbol : signal.symbol), (signal.timeframe != 0 ? signal.timeframe : _Period), gate_name, severity, sl_gap, tp_gap, signal.volume); return; } string details = StringFormat("gate=%s symbol=%s strategy=%s price=%.5f sl=%.5f tp=%.5f volume=%.2f", gate_name, (signal.symbol == "" ? _Symbol : signal.symbol), (signal.strategy == "" ? "unknown" : signal.strategy), signal.price, signal.sl, signal.tp, signal.volume); g_telemetry_base.LogEvent(_Symbol, _Period, "gate_sanitize", "sanitized", details); } // Callback function pointer for gate sanitize telemetry typedef void (*GateSanitizeCallback)(const string, TradingSignal&); GateSanitizeCallback g_gate_sanitize_callback = NULL; void SetGateSanitizeTelemetryCallback(GateSanitizeCallback callback) { g_gate_sanitize_callback = callback; } // P0-4: ONNX Health Monitoring - Latency tracking and degradation detection struct SONNXHealthMetrics { ulong total_calls; ulong total_latency_ms; ulong max_latency_ms; ulong min_latency_ms; ulong failed_calls; ulong slow_calls; // Calls > 100ms threshold datetime last_failure; double avg_latency_ms; bool healthy; void Init() { total_calls = 0; total_latency_ms = 0; max_latency_ms = 0; min_latency_ms = ULONG_MAX; failed_calls = 0; slow_calls = 0; last_failure = 0; avg_latency_ms = 0.0; healthy = true; } void RecordLatency(ulong latency_ms, bool success) { total_calls++; if(success) { total_latency_ms += latency_ms; max_latency_ms = MathMax(max_latency_ms, latency_ms); min_latency_ms = MathMin(min_latency_ms, latency_ms); avg_latency_ms = (double)total_latency_ms / total_calls; // P0-4: Flag slow calls (>100ms threshold per MT5 Build 5572 guidelines) if(latency_ms > 100) { slow_calls++; } } else { failed_calls++; last_failure = TimeCurrent(); } // P0-4: Health check - degrade if >5% failures or >20% slow calls double failure_rate = (double)failed_calls / total_calls; double slow_rate = (double)slow_calls / total_calls; healthy = (failure_rate < 0.05) && (slow_rate < 0.20); } double GetFailureRate() { return total_calls > 0 ? (double)failed_calls / total_calls : 0.0; } double GetSlowRate() { return total_calls > 0 ? (double)slow_calls / total_calls : 0.0; } }; SONNXHealthMetrics g_onnx_health; bool g_onnx_health_initialized = false; input int ONNX_MaxLatencyMs = 100; // P0-4: Max acceptable ONNX latency input bool ONNX_EnableHealthMonitor = true; // P0-4: Enable health monitoring // P0-4: Heuristic confidence calculation as ONNX fallback double CalculateHeuristicConfidence(const TradingSignal &signal) { double confidence = 0.5; // Neutral baseline // Adjust based on signal strength indicators if(signal.volatility > 0) { // Lower confidence in high volatility confidence -= MathMin(0.15, signal.volatility * 0.5); } // Boost confidence if signal has good structure if(signal.price > 0 && signal.sl > 0 && signal.tp > 0) { double sl_dist = MathAbs(signal.price - signal.sl); double tp_dist = MathAbs(signal.tp - signal.price); if(tp_dist > sl_dist) // Favorable R:R { confidence += 0.1; } } // Ensure bounds confidence = MathMax(0.3, MathMin(0.7, confidence)); return confidence; } // Alias for backward compatibility double HeuristicConfidence(const TradingSignal &signal) { return CalculateHeuristicConfidence(signal); } // P0-4: Health-aware model evaluation with latency guardrails double EvaluateModelProbabilityWithHealth(const TradingSignal &signal, const string strategy_name, const CAdaptiveDecision *decision_ptr, const string status, const string reason) { if(!g_onnx_health_initialized) { g_onnx_health.Init(); g_onnx_health_initialized = true; } if(!UseOnnxPredictor || !g_model_predictor_ready) { if(ONNX_EnableHealthMonitor) { g_onnx_health.RecordLatency(0, false); } return -1.0; } // P0-4: Check health before attempting inference if(ONNX_EnableHealthMonitor && !g_onnx_health.healthy) { static datetime last_health_warning = 0; if(TimeCurrent() - last_health_warning > 300) // Warn every 5 minutes { LOG(StringFormat("[P0-4] ONNX health degraded (fail=%.1f%% slow=%.1f%%), using heuristic fallback", g_onnx_health.GetFailureRate()*100, g_onnx_health.GetSlowRate()*100)); last_health_warning = TimeCurrent(); } return -2.0; // Signal to use heuristic fallback } // P0-4: Track latency ulong start_time = GetTickCount(); double result = EvaluateModelProbability(signal, strategy_name, decision_ptr, status, reason); ulong latency_ms = GetTickCount() - start_time; // P0-4: Record metrics if(ONNX_EnableHealthMonitor) { bool success = (result >= 0.0); g_onnx_health.RecordLatency(latency_ms, success); // P0-4: Log slow calls if(latency_ms > (ulong)ONNX_MaxLatencyMs) { static int slow_call_count = 0; slow_call_count++; if(slow_call_count <= 10 || slow_call_count % 100 == 0) { LOG(StringFormat("[P0-4] ONNX slow inference: %I64ums (threshold: %dms) - count: %d", latency_ms, ONNX_MaxLatencyMs, slow_call_count)); } } } return result; } double EvaluateModelProbability(const TradingSignal &signal, const string strategy_name, const CAdaptiveDecision *decision_ptr, const string status, const string reason) { if(!UseOnnxPredictor || !g_model_predictor_ready) return -1.0; if(ArraySize(g_mlp_feature_columns) == 0) return -1.0; bool telemetry_ready = TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID; string phase = status; if(StringLen(reason) > 0) phase += ":" + reason; datetime now = TimeCurrent(); if(!BuildPredictorFeatureVector(signal, strategy_name, decision_ptr, status, reason, now, g_mlp_feature_buffer)) { if(!g_mlp_warned_features) { LOG("WARNING: Unable to build ML predictor feature vector (metadata mismatch)"); g_mlp_warned_features = true; } if(telemetry_ready) { string details = StringFormat("phase=%s outcome=feature_build_failed strategy=%s", phase, strategy_name); g_telemetry_base.LogEvent(_Symbol, _Period, "ml_eval", "failed", details); } return -1.0; } double probability = g_model_predictor.Predict(g_mlp_feature_buffer, ArraySize(g_mlp_feature_buffer)); if(telemetry_ready) { string details = StringFormat("phase=%s prob=%.4f strategy=%s", phase, probability, strategy_name); g_telemetry_base.LogEvent(_Symbol, _Period, "ml_eval", "completed", details); } return probability; } bool InitModelPredictor() { ArrayResize(g_mlp_feature_columns, 0); ArrayResize(g_mlp_feature_buffer, 0); g_mlp_warned_unready = false; g_mlp_warned_features = false; // Resolve paths for the DLL (absolute OS paths). Do not use these with FileIsExist/FileOpen. string model_path = ResolveDualEAPath(OnnxModelPath, OnnxPathsUseCommonDir); string json_path = ResolveDualEAPath(OnnxConfigJsonPath, OnnxPathsUseCommonDir); string ini_path = ResolveDualEAPath(OnnxConfigIniPath, OnnxPathsUseCommonDir); LOG(StringFormat("ONNX Model Path: %s", model_path)); LOG(StringFormat("ONNX JSON Path: %s", json_path)); LOG(StringFormat("ONNX INI Path: %s", ini_path)); if(StringLen(model_path) == 0 || StringLen(json_path) == 0 || StringLen(ini_path) == 0) { LOG("WARNING: ONNX predictor paths are not configured correctly"); return false; } int common_flag = OnnxPathsUseCommonDir ? FILE_COMMON : 0; int h_model = FileOpen(OnnxModelPath, FILE_READ|FILE_BIN|common_flag); if(h_model == INVALID_HANDLE) { LOG(StringFormat("ERROR: Model file not found in %s Files: %s (error=%d)", (OnnxPathsUseCommonDir ? "Common" : "Terminal"), OnnxModelPath, GetLastError())); return false; } FileClose(h_model); int h_ini = FileOpen(OnnxConfigIniPath, FILE_READ|FILE_TXT|common_flag); if(h_ini == INVALID_HANDLE) { LOG(StringFormat("ERROR: Config INI file not found in %s Files: %s (error=%d)", (OnnxPathsUseCommonDir ? "Common" : "Terminal"), OnnxConfigIniPath, GetLastError())); return false; } FileClose(h_ini); int h_json = FileOpen(OnnxConfigJsonPath, FILE_READ|FILE_TXT|common_flag); if(h_json == INVALID_HANDLE) { LOG(StringFormat("WARNING: Config JSON file not found in %s Files: %s (error=%d)", (OnnxPathsUseCommonDir ? "Common" : "Terminal"), OnnxConfigJsonPath, GetLastError())); } else { FileClose(h_json); } LOG(StringFormat("Initializing ONNX predictor with model: %s", model_path)); if(!g_model_predictor.Init(model_path, json_path, ini_path)) { LOG(StringFormat("WARNING: ONNX predictor init failed - %s", g_model_predictor.LastError())); return false; } if(!g_model_predictor.GetFeatureNames(g_mlp_feature_columns) || ArraySize(g_mlp_feature_columns) == 0) { LOG("WARNING: ONNX predictor did not return feature metadata; shutting down"); g_model_predictor.Shutdown(); return false; } ArrayResize(g_mlp_feature_buffer, ArraySize(g_mlp_feature_columns)); LOG(StringFormat("ONNX predictor ready (%d features)", ArraySize(g_mlp_feature_columns))); return true; } // ===================[ STRATEGIES CONTAINER AND ADDITIONAL MISSING GLOBALS ]=================== CArrayObj* g_strategies = NULL; bool g_insights_rebuild_in_progress = false; datetime g_last_insights_rebuild_time = 0; datetime g_p5_last_rescore_ts = 0; // ===================[ INSIGHTS REBUILD TRACKING PER SLICE ]=================== string g_ir_slice_keys[]; datetime g_ir_slice_times[]; // ===================[ LOG LEVELS (ENUM AND CONSTANTS) ]=================== #undef LOG_DEBUG #undef LOG_INFO #undef LOG_WARNING #undef LOG_ERROR enum LogLevel { LOG_ERROR = 0, LOG_INFO = 1, LOG_DEBUG = 2 }; // ===================[ LAST PAPER ACTION TRACKING ]=================== static datetime g_last_paper_action = 0; //+------------------------------------------------------------------+ //| EXPERT INITIALIZATION | //+------------------------------------------------------------------+ int OnInit() { // ===================[ INSTANCE COLLISION DETECTION ]=================== // Check if another instance with same magic is already running on this chart static bool g_instance_initialized = false; if(g_instance_initialized) { LOG(StringFormat("⚠️ ERROR: Multiple PaperEA_v2 instances detected on %s %s with Magic %d", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period), MagicNumber)); LOG(" This causes file contention and duplicate processing. Please use unique Magic numbers."); return(INIT_FAILED); } g_instance_initialized = true; // Initialize logging middleware if(LogMiddleware == NULL) { LogMiddleware = new CLogMiddleware("DualEA\\system.log", true); } LOG("=== PaperEA v2 Enhanced Initialization Starting ==="); LOG(StringFormat("Instance ID: %s-%s-M%d", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period), MagicNumber)); // Seed RNG for rate-based polling and start 10s timer MathSrand((int)TimeLocal()); if(HotReloadIntervalSec > 0) EventSetTimer(HotReloadIntervalSec); // ===================[ INITIALIZE MODIFIABLE PARAMETERS ]=================== // Copy user input values to modifiable runtime variables StopLossPips = StopLossPips_Input; TakeProfitPips = TakeProfitPips_Input; VolSizerTargetRisk = VolSizerTargetRisk_Input; P5_MinPF = P5_MinPF_Input; P5_MinWR = P5_MinWR_Input; CBDailyLossLimitPct = CBDailyLossLimitPct_Input; // ===================[ CORE SYSTEM INITIALIZATION ]=================== // Initialize base telemetry system first g_telemetry_base = new CTelemetry(); if(CheckPointer(g_telemetry_base) == POINTER_INVALID) { LOG("ERROR: Failed to initialize base telemetry system"); return(INIT_FAILED); } // Initialize standard telemetry wrapper g_tel_standard = new CTelemetryStandard(g_telemetry_base); g_telemetry = g_tel_standard; // Maintain backward compatibility SetGateSanitizeTelemetryCallback(GateSanitizeTelemetryReporter); // Unified System: sync config + event bus + monitor { CConfigManager *cfg = CConfigManager::GetInstance(); if(CheckPointer(cfg) != POINTER_INVALID) { cfg.SetNoConstraintsMode(NoConstraintsMode); cfg.SetVerboseLogging(Verbosity >= 2); cfg.SetDataPath(LearningDataPath); } CEventBus *bus = CEventBus::GetInstance(); if(CheckPointer(bus) != POINTER_INVALID) { bus.SetVerboseLogging(cfg != NULL ? cfg.IsVerboseLogging() : (Verbosity >= 2)); bus.PublishSystemEvent("PaperEA_v2", "Unified system online"); } CSystemMonitor *mon = CSystemMonitor::GetInstance(); if(CheckPointer(mon) == POINTER_INVALID) LOG("WARNING: SystemMonitor init failed"); } // Initialize Knowledge Base for comprehensive logging g_kb = new CKnowledgeBase(); if(CheckPointer(g_kb) == POINTER_INVALID) { LOG("ERROR: Failed to initialize Knowledge Base"); return(INIT_FAILED); } // Initialize Features KB for ML data export g_features = new CFeaturesKB(); if(CheckPointer(g_features) == POINTER_INVALID) { LOG("WARNING: Failed to initialize Features KB - ML features disabled"); } // Initialize ONNX predictor (optional) if(UseOnnxPredictor) { g_model_predictor_ready = InitModelPredictor(); if(!g_model_predictor_ready) { LOG("WARNING: ONNX predictor init failed - heuristic confidence will be used"); } } else { ShutdownModelPredictor(); g_model_predictor_ready = false; } // ===================[ STRATEGY SYSTEM INITIALIZATION // ===================[ INITIALIZE STRATEGY REGISTRY ]=================== g_strategies = new CArrayObj(); if(g_strategies == NULL) { LOG("Failed to initialize strategy array!"); return(INIT_FAILED); } // Initialize strategy selector if(UseStrategySelector) { g_selector = new CStrategySelector(); if(CheckPointer(g_selector) == POINTER_INVALID) { LOG("WARNING: Failed to initialize Strategy Selector - using fallback selection"); } else { // Configure selector weights g_selector.ConfigureWeights(SelW_PF, SelW_Exp, SelW_WR, SelW_DD); g_selector.ConfigureRecency(SelUseRecency, SelRecentDays, SelRecAlpha); g_selector.SetStrictThresholds(SelStrictThresholds); LOG("Strategy Selector initialized with custom weights"); // Initialize the complete strategy registry bool registry_success = InitializeStrategyRegistry(); if(!registry_success) { LOG("WARNING: Strategy registry initialization failed - limited strategy selection available"); } } } // ===================[ ADVANCED MANAGERS INITIALIZATION ]=================== // Initialize Session Manager if(UseSessionManager) { g_session_manager = new CSessionManager(_Symbol, (ENUM_TIMEFRAMES)_Period); if(CheckPointer(g_session_manager) != POINTER_INVALID) { g_session_manager.SetSessionHours(SessionStartHour, SessionEndHour); g_session_manager.SetMaxTradesPerSession(MaxTradesPerSession); LOG("Session Manager initialized"); } } // Initialize Correlation Manager if(UseCorrelationManager) { g_correlation_manager = new CCorrelationManager(_Symbol, (ENUM_TIMEFRAMES)_Period); if(CheckPointer(g_correlation_manager) != POINTER_INVALID) { g_correlation_manager.SetMaxCorrelation(MaxCorrelationLimit); g_correlation_manager.SetLookbackDays(CorrLookbackDays); LOG("Correlation Manager initialized"); } } // Initialize Volatility Sizer if(UseVolatilitySizer) { g_volatility_sizer = new CVolatilitySizer(_Symbol, (ENUM_TIMEFRAMES)_Period); if(CheckPointer(g_volatility_sizer) != POINTER_INVALID) { g_volatility_sizer.SetATRPeriod(VolSizerATRPeriod); g_volatility_sizer.SetBaseATRPercent(VolSizerBaseATRPct); g_volatility_sizer.SetMultiplierRange(VolSizerMinMult, VolSizerMaxMult); g_volatility_sizer.SetTargetRiskPercent(VolSizerTargetRisk); g_volatility_sizer.SetEnabled(true); LOG("Volatility Sizer initialized"); } } // Initialize Position Manager if(UsePositionManager) { g_position_manager = new CPositionManager(); if(CheckPointer(g_position_manager) != POINTER_INVALID) { // Position cap enforced via MaxOpenPositions gating inputs LOG("Position Manager initialized"); } } // ===================[ EFFICIENT GATE SYSTEM CONFIGURATION ]=================== // Note: Input variables already declared at global scope (lines 82-95) // Using existing GateProcessingMode, G5_PerformanceWaxEnabled, etc. // Risk gates ALWAYS enforced (G1, G4, G7, G8) - per MQL5 risk management best practices // Initialize Policy Engine if(UsePolicyEngine || UsePolicyGating) { g_policy_engine = new CPolicyEngine(); if(CheckPointer(g_policy_engine) != POINTER_INVALID) { // Load initial policy bool policy_loaded = Policy_Load(); if(policy_loaded) LOG(StringFormat("Policy Engine initialized, policy loaded: YES (min_conf=%.2f)", g_policy_min_conf)); else LOG("Policy Engine initialized, policy loaded: NO"); // Configure policy bridge for hot-reload g_policy_bridge = new CPolicyHttpBridge(); if(CheckPointer(g_policy_bridge) != POINTER_INVALID) { int interval = (HotReloadIntervalSec>0? HotReloadIntervalSec : 60); g_policy_bridge.Configure(PolicyFilePath, interval); if(!g_policy_bridge.VerifyAccess(PolicyServerUrl)) { LOG("WARNING: Policy HTTP endpoint not accessible yet. Follow the MT5 WebRequest instructions above to enable it."); } else if(ShouldLog(LOG_INFO)) { LOG(StringFormat("Policy HTTP access verified for %s", PolicyServerUrl)); } } } } // Initialize Insights Realtime g_ins_rt = new CInsightsRealtime(); if(CheckPointer(g_ins_rt) != POINTER_INVALID) { LOG("Insights Realtime initialized"); } // ===================[ LEGACY SYSTEM INITIALIZATION ]=================== // Initialize learning bridge (maintain compatibility) g_learning_bridge = new CLearningBridge(LearningDataPath, MaxLearningRecords); if(CheckPointer(g_learning_bridge) == POINTER_INVALID) { LOG(StringFormat("WARNING: Failed to initialize Learning Bridge - learning features disabled for %s %s", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period))); } // Initialize gate manager with auto-learning (replaces old CGateManager) if(UseEfficientGates) { // NEW: Enhanced gate system with shadow logging and auto-tuning SAutoLearningConfig config; config.Init(); config.shadow_mode = ShadowMode; // Set to true initially for data collection config.load_onnx_models = LoadONNXModels; config.auto_reload_models = AutoReloadModels; config.auto_tune_gates = true; config.retrain_interval_hours = RetrainIntervalHours; g_gate_manager = new CEfficientGateManagerEnhanced( NoConstraintsMode, // Properly respected now UseInsightsGating, // Insights threshold checks UsePolicyGating // ML policy filtering ); // Initialize with shadow logging and auto-learning if(CheckPointer(g_gate_manager) != POINTER_INVALID) { g_gate_manager.Initialize(config.shadow_mode); // Set processing mode based on configuration if(NoConstraintsMode) g_gate_manager.SetMode(GATE_MODE_RISK_ONLY); else g_gate_manager.SetMode((EGateProcessingMode)GateProcessingMode); // Configure G5 PerformanceWax with auto-tuning if(G5_PerformanceWaxEnabled) { g_gate_manager.SetPerformanceWaxEnabled(true, G5_MinWinRate, G5_LookbackTrades); // Enable auto-tuning for G5 SGateAutoTuneConfig tune_config; tune_config.Init(0.3, 0.9, 20, 0.55, 0.05); g_gate_manager.EnableAutoTune(5, tune_config); } // Configure G6 MLPolish with ONNX support if(G6_MLPolishEnabled) { g_gate_manager.SetMLPolishEnabled(true, G6_MLConfidenceThreshold); // Enable auto-tuning for G6 SGateAutoTuneConfig tune_config; tune_config.Init(0.4, 0.8, 30, 0.55, 0.03); g_gate_manager.EnableAutoTune(6, tune_config); } // Configure G7 LiveClean if(G7_LiveCleanEnabled) g_gate_manager.SetLiveCleanEnabled(true, G7_MaxSpreadPercent); LOG("✅ Enhanced Gate System initialized (8-stage, shadow logging, auto-tuning, ONNX-ready)"); // Initialize auto-learning manager for model reloading CAutoLearningManager auto_learning; auto_learning.Initialize(g_gate_manager, config); } } else { // LEGACY: Use old gate manager (deprecated) LOG("WARNING: Using deprecated CGateManager - migrate to CEfficientGateManagerEnhanced"); // Note: CGateManager class has been removed - this branch should not be used g_gate_manager = NULL; } if(CheckPointer(g_gate_manager) == POINTER_INVALID) { LOG(StringFormat("ERROR: Failed to initialize Gate Manager for %s %s", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period))); LOG("ERROR: Failed to initialize Gate Manager"); return(INIT_FAILED); } // Initialize Strategy Signal Registry (extensible signal generators for all strategies) g_signal_registry = new CStrategySignalRegistry(); if(CheckPointer(g_signal_registry) != POINTER_INVALID) { g_signal_registry.InitializeAllStrategies(); LOG(StringFormat("✅ Strategy Signal Registry initialized: %d generators registered", g_signal_registry.GetCount())); } else { LOG("WARNING: Failed to initialize Strategy Signal Registry - using fallback signal generation"); } // ===================[ P0/P1 COMPREHENSIVE HARDENING INITIALIZATION ]=================== // Initialize the master controller with all P0/P1 components { SDualEAConfig master_config; master_config.SetDefaults(); // Configure from EA inputs master_config.shadow_logging_enabled = ShadowMode; master_config.shadow_mode_only = ShadowMode && !LiveTradingEnabled; master_config.sqlite_enabled = true; master_config.circuit_breaker_enabled = UseCircuitBreakers; master_config.correlation_sizing_enabled = UseCorrelationManager; master_config.volatility_exits_enabled = UseVolatilitySizer; master_config.max_risk_per_trade_pct = VolSizerTargetRisk; master_config.max_positions = MaxOpenPositions; master_config.redis_enabled = false; // Disabled by default if(!InitializeDualEAMasterController(master_config)) { LOG("WARNING: P0/P1 Master Controller initialization failed - some hardened features disabled"); } else { LOG("✅ P0/P1 Master Controller initialized: All hardened systems active"); } } // Initialize gate audit system with all expected strategies (registry + extra paper strategies) string fallback_all_strategies = "ADXStrategy,AcceleratorOscillatorStrategy,AlligatorStrategy,AwesomeOscillatorStrategy,BearsPowerStrategy,BullsPowerStrategy,"+ "CCIStrategy,DeMarkerStrategy,ForceIndexStrategy,FractalsStrategy,GatorStrategy,IchimokuStrategy,MACDStrategy,MomentumStrategy,OsMAStrategy,"+ "RSIStrategy,RVIStrategy,StochasticStrategy,TriXStrategy,UltimateOscillatorStrategy,WilliamsPercentRangeStrategy,ZigZagStrategy,MovingAverageStrategy,"+ "SuperTrendADXKama,RSI2BBReversion,DonchianATRBreakout,MeanReversionBB,KeltnerMomentum,VWAPReversion,EMAPullback,OpeningRangeBreakout"; string registry_strategies = ""; int registry_count = 0; if(CheckPointer(g_signal_registry) != POINTER_INVALID) { registry_strategies = g_signal_registry.GetRegisteredStrategies(); registry_count = g_signal_registry.GetCount(); } string extra_paper_strategies = "SuperTrendADXKama,RSI2BBReversion,DonchianATRBreakout,MeanReversionBB,KeltnerMomentum,VWAPReversion,EMAPullback,OpeningRangeBreakout"; string all_strategies = CsvMergeUnique(registry_strategies, extra_paper_strategies); if(CsvCountTokens(all_strategies) == 0) all_strategies = fallback_all_strategies; g_gate_audit.Initialize(all_strategies); g_gate_audit.Configure(GateAuditMinSignals, GateAuditMinUptimeMin, GateAuditAlertCooldownMin); g_gate_audit.SetEnabled(UseGateSystem); if(ShouldLog(LOG_INFO)) { LOG(StringFormat("✅ GateAudit expected strategies=%d (registry=%d, extras=%d)", CsvCountTokens(all_strategies), registry_count, CsvCountTokens(extra_paper_strategies))); } // Ensure selector has recent telemetry data loaded before first selection if(UseStrategySelector && CheckPointer(g_selector) != POINTER_INVALID) { g_selector.EnsureRecentLoaded(SelRecentDays); if(ShouldLog(LOG_INFO)) LOG(StringFormat("📚 Strategy selector recent data loaded for last %d days", SelRecentDays)); } // Initialize trade manager g_trade_manager = new CTradeManager(_Symbol, LotSize, MagicNumber); if(CheckPointer(g_trade_manager) == POINTER_INVALID) { LOG(StringFormat("ERROR: Failed to initialize Trade Manager for %s %s", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period))); return(INIT_FAILED); } // ===================[ ADAPTIVE OPTIMIZATION SYSTEM INITIALIZATION ]=================== // Initialize Policy Updater (auto-creates and updates policy.json) if(UsePolicyEngine || UsePolicyGating) { g_policy_updater = new CPolicyUpdater(g_learning_bridge, 60); // Update every 60 minutes if(CheckPointer(g_policy_updater) != POINTER_INVALID) { LOG("✅ Policy Updater initialized: auto-updating policy.json"); } } // Initialize Adaptive Signal Optimizer g_adaptive_optimizer = new CAdaptiveSignalOptimizer(g_learning_bridge, g_gate_manager, 3, true); if(CheckPointer(g_adaptive_optimizer) == POINTER_INVALID) { LOG("WARNING: Failed to initialize Adaptive Signal Optimizer - using standard gate processing"); } else { LOG("✅ Adaptive Signal Optimizer initialized: 23 strategies, ML-enabled"); } // Initialize Position Reviewer (5-minute reviews) g_position_reviewer = new CPositionReviewer(g_gate_manager, g_adaptive_optimizer, 300); if(CheckPointer(g_position_reviewer) != POINTER_INVALID) { LOG("✅ Position Reviewer initialized: Reviews every 5 minutes"); } // Initialize Gate Learning System (hybrid learning: immediate + batch) g_gate_learning = new CGateLearningSystem(true, 0.05); // auto_adjust=true, learning_rate=5% if(CheckPointer(g_gate_learning) != POINTER_INVALID) { LOG("✅ Gate Learning System initialized: Hybrid updates enabled"); } // Initialize Incremental Insight Engine (O(1) real-time statistics) g_insight_engine = new CIncrementalInsightEngine(); if(CheckPointer(g_insight_engine) != POINTER_INVALID) { g_insight_engine.SetPromotionCriteria(30, 0.52, 1.2, 10.0); LOG("✅ Incremental Insight Engine initialized: O(1) real-time stats"); } // Initialize Unified Trade Logger (JSON lifecycle tracking) g_trade_logger = new CUnifiedTradeLogger(); if(CheckPointer(g_trade_logger) != POINTER_INVALID) { LOG("✅ Unified Trade Logger initialized: Daily JSON logs"); } // Initialize AdvancedRegimeDetector g_regime_detector = new CAdvancedRegimeDetector(_Symbol, (ENUM_TIMEFRAMES)_Period); if(CheckPointer(g_regime_detector) != POINTER_INVALID) { g_regime_detector.SetADXPeriod(RegimeADXPeriod); g_regime_detector.SetATRPeriod(ATRRegimePeriod); g_regime_detector.SetADXThresholds(RegimeADXTrendThreshold, RegimeStrongTrendThreshold); g_regime_detector.SetVolatilityThresholds(RegimeVolatilityHigh, RegimeVolatilityLow); LOG("✅ AdvancedRegimeDetector initialized"); } // ===================[ NUCLEAR-GRADE 100-LEVEL OPTIMIZATION INITIALIZATION ]=================== // Initialize Configuration Manager with AGGRESSIVE profile g_config_manager = new CConfigurationManager("DualEA\\config\\nuclear_config.json"); if(CheckPointer(g_config_manager) != POINTER_INVALID) { g_config_manager.SetRiskProfile(85); // AGGRESSIVE g_config_manager.SetKellyFraction(0.25); g_config_manager.SetMaxDrawdown(0.15); g_config_manager.SaveConfig(); LOG("✅ Nuclear Config Manager initialized (AGGRESSIVE profile)"); } // Initialize Optimized Correlation Engine (23-strategy matrix) g_nuclear_correlation = new COptimizedCorrelationEngine(); if(CheckPointer(g_nuclear_correlation) != POINTER_INVALID) { LOG("✅ Optimized Correlation Engine initialized (23×23 matrix, 252-day window)"); } // Initialize Risk Metrics Calculator (VaR 95%, Expected Shortfall) g_risk_metrics = new CRiskMetricsCalculator(100); // 100-trade window if(CheckPointer(g_risk_metrics) != POINTER_INVALID) { LOG("✅ Risk Metrics Calculator initialized (VaR 95%, CVaR, Sharpe, Calmar, Sortino)"); } // Initialize Kelly Criterion Position Sizer g_kelly_sizer = new CKellyPositionSizer(0.25, 0.10, 0.01); // Quarter Kelly, 10% max, 1% min if(CheckPointer(g_kelly_sizer) != POINTER_INVALID) { LOG("✅ Kelly Position Sizer initialized (Quarter Kelly fraction, safety-constrained)"); } // Initialize Policy State Manager (lock-free queue) g_policy_state = new CPolicyStateManager(); if(CheckPointer(g_policy_state) != POINTER_INVALID) { LOG("✅ Policy State Manager initialized (1024-element ring buffer)"); } // Initialize Lock-Free Log Queue g_log_queue = new CRingBuffer(8192); if(CheckPointer(g_log_queue) != POINTER_INVALID) { LOG("✅ Lock-Free Log Queue initialized (8192-element ring buffer)"); } // ===================[ SESSION STATE INITIALIZATION ]=================== // Initialize session tracking g_session_start = TimeCurrent(); g_session_equity_start = AccountInfoDouble(ACCOUNT_EQUITY); g_equity_highwater = g_session_equity_start; MqlDateTime dt; TimeToStruct(g_session_start, dt); g_session_day = dt.day_of_year; // ===================[ UNIVERSAL AUTO-DETECTION INITIALIZATION ]=================== if(AutoDetectParameters) { g_adaptive_engine.CalculateDynamicParameters(_Symbol, (ENUM_TIMEFRAMES)_Period); // Apply auto-detected parameters immediately SymbolProfile profile = g_adaptive_engine.GetCurrentProfile(); if(AllowManualOverride || StopLossPips == 150.0) { StopLossPips = profile.optimal_sl_mult; } if(AllowManualOverride || TakeProfitPips == 300.0) { TakeProfitPips = profile.optimal_tp_mult; } if(AllowManualOverride || VolSizerTargetRisk == 1.0) { VolSizerTargetRisk = profile.risk_multiplier; } LOG(StringFormat("🎯 [UNIVERSAL AUTO-DETECT] %s %s INITIALIZED", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period))); LOG(StringFormat(" ├─ Optimal SL: %.1f pips (ATR-based: %.4f, Spread: %.3f%%)", profile.optimal_sl_mult, profile.avg_atr_ratio, profile.avg_spread_pct * 100)); LOG(StringFormat(" ├─ Optimal TP: %.1f pips (RR: %.2f)", profile.optimal_tp_mult, profile.optimal_tp_mult / profile.optimal_sl_mult)); LOG(StringFormat(" ├─ Risk Size: %.2f%% (Volatility: %d%%, Liquidity: %.0f%%)", profile.risk_multiplier, (int)profile.volatility_percentile, profile.liquidity_score)); LOG(StringFormat(" └─ Timeframe Mult: %.2fx | Profile: %s", profile.timeframe_mult, RiskProfile)); } // ===================[ CRITICAL SYSTEMS INITIALIZATION ]=================== // Load news events for filtering if(UseNewsFilter) { LoadNewsEvents(); } // Initialize circuit breaker state g_cb_active = false; g_cb_trigger_time = 0; g_cb_last_cause = ""; g_cb_last_threshold = 0.0; g_cb_last_value = 0.0; // Initialize trade frequency gating last_paper_trade_time = 0; // ===================[ FINAL SYSTEM CHECKS ]=================== // Validate critical systems bool critical_systems_ok = true; if(CheckPointer(g_telemetry_base) == POINTER_INVALID) critical_systems_ok = false; if(CheckPointer(g_kb) == POINTER_INVALID) critical_systems_ok = false; if(CheckPointer(g_strategies) == POINTER_INVALID) critical_systems_ok = false; if(CheckPointer(g_trade_manager) == POINTER_INVALID) critical_systems_ok = false; if(!critical_systems_ok) { LOG("ERROR: Critical system initialization failed"); return(INIT_FAILED); } // Log initialization summary LOG("=== PaperEA v2 Enhanced Initialization Complete ==="); LOG(StringFormat("📊 Chart: %s %s | LotSize: %.2f | Magic: %d", _Symbol, EnumToString(_Period), LotSize, MagicNumber)); LOG(StringFormat("NoConstraintsMode: %s | UseStrategySelector: %s | UsePolicyGating: %s", NoConstraintsMode ? "ON" : "OFF", UseStrategySelector ? "ON" : "OFF", UsePolicyGating ? "ON" : "OFF")); LOG(StringFormat("Session Start: %s | Equity: %.2f", TimeToString(g_session_start), g_session_equity_start)); // ===================[ FINAL INTEGRATION VALIDATION ]=================== // Validate all critical systems are properly integrated int systems_active = 0; int systems_total = 10; if(CheckPointer(g_telemetry_base) != POINTER_INVALID) systems_active++; if(CheckPointer(g_kb) != POINTER_INVALID) systems_active++; if(CheckPointer(g_strategies) != POINTER_INVALID) systems_active++; if(CheckPointer(g_trade_manager) != POINTER_INVALID) systems_active++; if(CheckPointer(g_gate_manager) != POINTER_INVALID) systems_active++; if(UseStrategySelector && CheckPointer(g_selector) != POINTER_INVALID) systems_active++; if(UsePolicyEngine && CheckPointer(g_policy_engine) != POINTER_INVALID) systems_active++; if(UseSessionManager && CheckPointer(g_session_manager) != POINTER_INVALID) systems_active++; if(UseCorrelationManager && CheckPointer(g_correlation_manager) != POINTER_INVALID) systems_active++; if(UseVolatilitySizer && CheckPointer(g_volatility_sizer) != POINTER_INVALID) systems_active++; LOG(StringFormat("🎯 SYSTEM INTEGRATION STATUS: %d/%d systems active (%.1f%%)", systems_active, systems_total, (systems_active * 100.0) / systems_total)); // Log comprehensive system status LOG(StringFormat("✅ Circuit Breakers: %s | News Filter: %s | Strategy Registry: %s", UseCircuitBreakers ? "ACTIVE" : "disabled", UseNewsFilter ? "ACTIVE" : "disabled", UseStrategySelector ? "ACTIVE" : "disabled")); LOG(StringFormat("✅ Memory Limits: ACTIVE | Regime Detection: %s | Policy Engine: %s", UseRegimeGate ? "ACTIVE" : "disabled", UsePolicyEngine ? "ACTIVE" : "disabled")); // Enable timer for periodic updates (respect configured HotReloadIntervalSec) if(HotReloadIntervalSec<=0) EventSetTimer(60); // fallback 1-minute timer LOG("=== 🚀 PaperEA v2 FULLY INTEGRATED AND READY FOR PRODUCTION ==="); // ===================[ P0-P5 ADVANCED HARDENING INITIALIZATION ]=================== // Initialize all P0-P5 subsystems (concept drift, CPU budgeting, nuclear risk, etc.) if(!InitializeP0P5Systems()) { LOG("WARNING: P0-P5 Advanced Hardening initialization failed - continuing with degraded features"); } else { LOG("✅ P0-P5 Advanced Hardening active: Drift detection, CPU budgeting, Nuclear risk, Symbol coordination"); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| POLICY HELPERS | //+------------------------------------------------------------------+ // (removed) Duplicate early Policy_Load() replaced by enhanced implementation later in file //+------------------------------------------------------------------+ //| TIMER HANDLER | //+------------------------------------------------------------------+ // (removed) Duplicate early OnTimer() replaced by enhanced timer later in file //+------------------------------------------------------------------+ //| EXPERT DEINITIALIZATION | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { LOG("=== PaperEA v2 Enhanced Deinitialization Starting ==="); ShutdownModelPredictor(); SetGateSanitizeTelemetryCallback(NULL); CEventBus *bus = CEventBus::GetInstance(); if(CheckPointer(bus) != POINTER_INVALID) bus.PublishSystemEvent("PaperEA_v2", "Deinitializing"); // Kill timer EventKillTimer(); // ===================[ REAL MT5 POSITIONS ]=================== // No cleanup needed - MT5 handles position lifecycle // All open positions remain in MT5 terminal until manually closed // ===================[ CLEANUP ADVANCED MANAGERS ]=================== if(g_session_manager != NULL) { delete g_session_manager; g_session_manager = NULL; } if(g_correlation_manager != NULL) { delete g_correlation_manager; g_correlation_manager = NULL; } if(g_volatility_sizer != NULL) { delete g_volatility_sizer; g_volatility_sizer = NULL; } if(g_position_manager != NULL) { delete g_position_manager; g_position_manager = NULL; } if(g_policy_engine != NULL) { delete g_policy_engine; g_policy_engine = NULL; } if(g_policy_bridge != NULL) { delete g_policy_bridge; g_policy_bridge = NULL; } if(g_ins_rt != NULL) { delete g_ins_rt; g_ins_rt = NULL; } // ===================[ CLEANUP STRATEGY SYSTEM ]=================== if(g_selector != NULL) { delete g_selector; g_selector = NULL; } if(g_strategies != NULL) { // Clear all strategy objects g_strategies.Clear(); delete g_strategies; g_strategies = NULL; } // ===================[ CLEANUP KNOWLEDGE BASE & FEATURES ]=================== if(g_kb != NULL) { delete g_kb; g_kb = NULL; } if(g_features != NULL) { delete g_features; g_features = NULL; } // ===================[ CLEANUP ADAPTIVE OPTIMIZATION SYSTEM ]=================== if(g_trade_logger != NULL) { g_trade_logger.PrintReport(); delete g_trade_logger; g_trade_logger = NULL; } if(g_regime_detector != NULL) { delete g_regime_detector; g_regime_detector = NULL; } // ===================[ CLEANUP NUCLEAR-GRADE OPTIMIZATION ]=================== if(g_log_queue != NULL) { delete g_log_queue; g_log_queue = NULL; } if(g_policy_state != NULL) { delete g_policy_state; g_policy_state = NULL; } if(g_kelly_sizer != NULL) { delete g_kelly_sizer; g_kelly_sizer = NULL; } if(g_risk_metrics != NULL) { delete g_risk_metrics; g_risk_metrics = NULL; } if(g_nuclear_correlation != NULL) { delete g_nuclear_correlation; g_nuclear_correlation = NULL; } if(g_config_manager != NULL) { g_config_manager.SaveConfig(); // Final save delete g_config_manager; g_config_manager = NULL; } if(g_gate_learning != NULL) { g_gate_learning.PrintReport(); g_gate_learning.SaveLearningData(); // Final save before shutdown delete g_gate_learning; g_gate_learning = NULL; } if(g_insight_engine != NULL) { g_insight_engine.SaveState(); // Final state save delete g_insight_engine; g_insight_engine = NULL; } if(g_position_reviewer != NULL) { g_position_reviewer.PrintReport(); delete g_position_reviewer; g_position_reviewer = NULL; } if(g_adaptive_optimizer != NULL) { g_adaptive_optimizer.PrintOptimizationReport(); delete g_adaptive_optimizer; g_adaptive_optimizer = NULL; } if(g_policy_updater != NULL) { g_policy_updater.PrintReport(); g_policy_updater.ForceUpdate(); // Final policy update before shutdown delete g_policy_updater; g_policy_updater = NULL; } // ===================[ CLEANUP LEGACY SYSTEMS ]=================== if(g_learning_bridge != NULL) { delete g_learning_bridge; g_learning_bridge = NULL; } if(g_gate_manager != NULL) { delete g_gate_manager; g_gate_manager = NULL; } if(g_signal_registry != NULL) { delete g_signal_registry; g_signal_registry = NULL; } if(g_trade_manager != NULL) { delete g_trade_manager; g_trade_manager = NULL; } // ===================[ CLEANUP P0/P1 MASTER CONTROLLER ]=================== ShutdownDualEAMasterController(); // ===================[ CLEANUP TELEMETRY SYSTEM ]=================== if(g_tel_standard != NULL) { delete g_tel_standard; g_tel_standard = NULL; } if(g_telemetry_base != NULL) { delete g_telemetry_base; g_telemetry_base = NULL; } // Clear telemetry pointer (was pointing to g_tel_standard) g_telemetry = NULL; // ===================[ CLEANUP GLOBAL ARRAYS ]=================== ArrayResize(g_pending_orders, 0); ArrayResize(g_pending_orders_strat, 0); ArrayResize(g_pending_deals, 0); ArrayResize(g_pending_deals_strat, 0); ArrayResize(g_pos_ids, 0); ArrayResize(g_pos_strats, 0); ArrayResize(g_pos_entry_price, 0); ArrayResize(g_pos_initial_risk, 0); ArrayResize(g_pos_start_time, 0); ArrayResize(g_pos_type, 0); ArrayResize(g_pos_max_price, 0); ArrayResize(g_pos_min_price, 0); ArrayResize(g_news_key, 0); ArrayResize(g_news_from, 0); ArrayResize(g_news_to, 0); ArrayResize(g_news_impact, 0); ArrayResize(g_gate_log_keys, 0); ArrayResize(g_gate_log_last_ts, 0); ArrayResize(g_gate_strat, 0); ArrayResize(g_gate_sym, 0); ArrayResize(g_gate_tf, 0); ArrayResize(g_gate_cnt, 0); ArrayResize(g_gate_wr, 0); ArrayResize(g_gate_avgR, 0); ArrayResize(g_gate_pf, 0); ArrayResize(g_gate_dd, 0); ArrayResize(g_pol_strat, 0); ArrayResize(g_pol_sym, 0); ArrayResize(g_pol_tf, 0); ArrayResize(g_pol_p, 0); ArrayResize(g_pol_sl, 0); ArrayResize(g_pol_tp, 0); ArrayResize(g_pol_trail, 0); ArrayResize(g_exp_keys, 0); ArrayResize(g_exp_weeks, 0); ArrayResize(g_exp_counts, 0); ArrayResize(g_exp_day_keys, 0); ArrayResize(g_exp_day_days, 0); ArrayResize(g_exp_day_counts, 0); ArrayResize(g_loss_sym, 0); ArrayResize(g_loss_mag, 0); ArrayResize(g_loss_cnt, 0); ArrayResize(g_ir_slice_keys, 0); ArrayResize(g_ir_slice_times, 0); // ===================[ CLEANUP P0-P5 ADVANCED SYSTEMS ]=================== ShutdownP0P5Systems(); LOG("=== PaperEA v2 Enhanced Deinitialization Complete ==="); LOG(StringFormat("Reason: %s", GetUninitReasonText(reason))); if(LogMiddleware != NULL) { delete LogMiddleware; LogMiddleware = NULL; } CConfigManager::Cleanup(); CEventBus::Cleanup(); CSystemMonitor::Cleanup(); } //+------------------------------------------------------------------+ //| ADAPTIVE PARAMETER FUNCTIONS | //+------------------------------------------------------------------+ // Dynamic parameter calculation functions void CalculateDynamicParameters() { if(!AutoDetectParameters) return; // Calculate dynamic SL/TP based on ATR and spread double dynamic_sl = g_adaptive_engine.GetDynamicSL(_Symbol, _Period); double dynamic_tp = g_adaptive_engine.GetDynamicTP(_Symbol, _Period); // Update global parameters if(AllowManualOverride || StopLossPips == 150) { // Only update if manual override allowed or default StopLossPips = dynamic_sl; } if(AllowManualOverride || TakeProfitPips == 300) { // Only update if manual override allowed or default TakeProfitPips = dynamic_tp; } // Update risk parameters double dynamic_risk = g_adaptive_engine.GetDynamicRisk(RiskProfile); if(AllowManualOverride || VolSizerTargetRisk == 1.0) { VolSizerTargetRisk = dynamic_risk; } } // Machine learning adaptation void AdaptParametersML() { if(!EnableMLAdaptation) return; double recent_pf = g_ml_engine.GetRecentProfitFactor(100); double recent_wr = g_ml_engine.GetRecentWinRate(100); double recent_dd = g_ml_engine.GetRecentMaxDrawdown(100); double recent_vol = g_ml_engine.GetRecentVolatility(50); double ml_adjustment = g_ml_engine.CalculateAdjustment(recent_pf, recent_wr, recent_dd, recent_vol); // Apply ML adjustments with constraints P5_MinPF = MathMax(1.0, P5_MinPF * ml_adjustment); P5_MinWR = MathMax(0.3, P5_MinWR * ml_adjustment); CBDailyLossLimitPct = MathMax(1.0, CBDailyLossLimitPct * (2.0 - ml_adjustment)); LOG(StringFormat("🔧 ML Adaptation: PF=%.2f WR=%.2f DD=%.2f%% Adjustment=%.3f", recent_pf, recent_wr, recent_dd * 100, ml_adjustment)); } //+------------------------------------------------------------------+ //| FORWARD DECLARATIONS AND HELPER FUNCTIONS | //+------------------------------------------------------------------+ // Forward declarations for helper functions used before definition int FindTrackedIndexByPid(ulong pid); void HandlePositionClosed(int idx, ulong close_deal); bool ShouldLog(const int level); string GetUninitReasonText(const int reason); // Helper function implementations int FindTrackedIndexByPid(ulong pid) { for(int i = 0; i < ArraySize(g_pos_ids); i++) { if(g_pos_ids[i] == pid) return i; } return -1; } void HandlePositionClosed(int idx, ulong close_deal) { if(idx < 0 || idx >= ArraySize(g_pos_ids)) return; // Log position closure if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) { LOG(StringFormat("Position closed: ticket=%I64u strat=%s", g_pos_ids[idx], g_pos_strats[idx])); } // Remove from tracking arrays int last = ArraySize(g_pos_ids) - 1; if(idx < last) { g_pos_ids[idx] = g_pos_ids[last]; g_pos_strats[idx] = g_pos_strats[last]; g_pos_entry_price[idx] = g_pos_entry_price[last]; g_pos_initial_risk[idx] = g_pos_initial_risk[last]; g_pos_start_time[idx] = g_pos_start_time[last]; g_pos_type[idx] = g_pos_type[last]; g_pos_max_price[idx] = g_pos_max_price[last]; g_pos_min_price[idx] = g_pos_min_price[last]; } ArrayResize(g_pos_ids, last); ArrayResize(g_pos_strats, last); ArrayResize(g_pos_entry_price, last); ArrayResize(g_pos_initial_risk, last); ArrayResize(g_pos_start_time, last); ArrayResize(g_pos_type, last); ArrayResize(g_pos_max_price, last); ArrayResize(g_pos_min_price, last); } bool ShouldLog(const int level) { return (Verbosity >= level); } string GetUninitReasonText(const int reason) { switch(reason) { case REASON_PROGRAM: return "EA terminated by user"; case REASON_REMOVE: return "EA removed from chart"; case REASON_RECOMPILE: return "EA recompiled"; case REASON_CHARTCHANGE: return "Chart symbol/period changed"; case REASON_CHARTCLOSE: return "Chart closed"; case REASON_PARAMETERS: return "Input parameters changed"; case REASON_ACCOUNT: return "Account changed"; case REASON_TEMPLATE: return "Template changed"; case REASON_INITFAILED: return "Initialization failed"; case REASON_CLOSE: return "Terminal closing"; default: return "Unknown reason"; } } //+------------------------------------------------------------------+ //| ADVANCED GATING SYSTEM - PORTED FROM V1 | //+------------------------------------------------------------------+ // Check if logging should occur based on verbosity level bool GateShouldPrint(const string tag, const string phase) { if(!GateLogThrottleEnabled) return true; string key = tag + "|" + phase; int idx=-1; for(int i=0;i= GateLogCooldownSec); } void GateMarkPrinted(const string tag, const string phase) { if(!GateLogThrottleEnabled) return; string key = tag + "|" + phase; int idx=-1; for(int i=0;i=1) && (!GateLogThrottleEnabled || GateShouldPrint(tag, phase))) { LOG(StringFormat("[%s] %s latency_ms=%d", tag, (allowed?"allow":"block"), latency)); GateMarkPrinted(tag, phase); } if(TelemetryEnabled && CheckPointer(g_telemetry_base)!=POINTER_INVALID) { string det = StringFormat("phase=%s p6_latency_ms=%d", phase, latency); (*g_telemetry_base).LogEvent(_Symbol, (int)_Period, "gate", StringFormat("%s_%s", tag, (allowed?"allow":"block")), det); } // Standardized telemetry schema if(TelemetryEnabled && CheckPointer(g_tel_standard)!=POINTER_INVALID) { (*g_tel_standard).LogGateEvent(_Symbol, (int)_Period, tag, allowed, phase, latency, "n/a"); } } // Policy loading function bool Policy_Load() { g_policy_loaded = false; g_policy_min_conf = 0.0; ArrayResize(g_pol_strat,0); ArrayResize(g_pol_sym,0); ArrayResize(g_pol_tf,0); ArrayResize(g_pol_p,0); ArrayResize(g_pol_sl,0); ArrayResize(g_pol_tp,0); ArrayResize(g_pol_trail,0); string path = PolicyFilePath; int h = FileOpen(path, FILE_READ|FILE_COMMON|FILE_TXT|FILE_ANSI); if(h == INVALID_HANDLE) { if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("Policy file not found: %s", path)); return false; } string content = ""; while(!FileIsEnding(h)) { content += FileReadString(h) + "\n"; } FileClose(h); if(StringLen(content) == 0) { if(ShouldLog(LOG_DEBUG)) LOG("Policy file is empty"); return false; } // Simple JSON parsing for policy (basic implementation) // In production, you'd want more robust JSON parsing if(StringFind(content, "min_confidence") >= 0) { g_policy_loaded = true; g_policy_min_conf = 0.5; // Default fallback // Logging moved to caller to avoid duplicate messages } return g_policy_loaded; } // Load policy from HTTP; returns response length on success, -1 on failure int LoadPolicyFromHttp(const string base_url) { string url = base_url; if(StringLen(url) == 0) return -1; if(StringSubstr(url, StringLen(url) - 1, 1) == "/") url = StringSubstr(url, 0, StringLen(url) - 1); url += "/policy.json"; char data[]; char result[]; string headers = ""; int status = WebRequest("GET", url, "", 3000, data, result, headers); if(status != 200) { static bool warned = false; if(!warned) { LOG(StringFormat("Policy HTTP request failed (status=%d err=%d). Check MT5 WebRequest whitelist for %s", status, GetLastError(), url)); warned = true; } return -1; } string body = CharArrayToString(result, 0, -1, CP_UTF8); if(StringLen(body) == 0) return -1; if(StringFind(body, "min_confidence") >= 0) { g_policy_loaded = true; if(g_policy_min_conf <= 0.0) g_policy_min_conf = 0.5; } return (int)StringLen(body); } // Check and apply policy reload signal from Common Files void CheckPolicyReload() { if(!UsePolicyGating) return; string path = "DualEA\\policy.reload"; int h = FileOpen(path, FILE_READ|FILE_COMMON|FILE_TXT|FILE_ANSI); if(h==INVALID_HANDLE) return; FileClose(h); bool ok = Policy_Load(); LogGate("PolicyReload", ok, "reload", NowMs()); // best-effort delete in Common Files if(!FileDelete(path, FILE_COMMON)) { LogGate("PolicyReload", false, "delete", NowMs()); } } // Check insights.reload signal from Common Files and trigger rebuild void CheckInsightsReload() { string path = "DualEA\\insights.reload"; int h = FileOpen(path, FILE_READ|FILE_COMMON|FILE_TXT|FILE_ANSI); if(h==INVALID_HANDLE) return; FileClose(h); LogGate("InsightsReload", true, "reload", NowMs()); // bool ok = Insights_RebuildAndReload("reload"); // Would need implementation // best-effort delete if(!FileDelete(path, FILE_COMMON)) { LogGate("InsightsReload", false, "delete", NowMs()); } } //+------------------------------------------------------------------+ //| CIRCUIT BREAKER SYSTEM - CRITICAL RISK MANAGEMENT | //+------------------------------------------------------------------+ // Check circuit breaker conditions bool CheckCircuitBreakers() { if(!UseCircuitBreakers) return true; if(g_cb_active) { if(TimeCurrent() - g_cb_trigger_time < CBCooldownMinutes * 60) { if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("Circuit breaker active: %d seconds remaining", (CBCooldownMinutes * 60) - (TimeCurrent() - g_cb_trigger_time))); return false; // Still in cooldown } else { g_cb_active = false; // Reset circuit breaker LOG(StringFormat("Circuit breaker reset after %d minute cooldown", CBCooldownMinutes)); } } double current_equity = AccountInfoDouble(ACCOUNT_EQUITY); double daily_loss_pct = 0.0; double drawdown_pct = 0.0; // Calculate daily loss percentage if(g_session_equity_start > 0) daily_loss_pct = (g_session_equity_start - current_equity) / g_session_equity_start * 100.0; // Calculate drawdown from high water mark if(g_equity_highwater > 0) { if(current_equity > g_equity_highwater) g_equity_highwater = current_equity; // Update high water mark drawdown_pct = (g_equity_highwater - current_equity) / g_equity_highwater * 100.0; } // Check daily loss limit if(CBDailyLossLimitPct > 0 && daily_loss_pct > CBDailyLossLimitPct) { g_cb_active = true; g_cb_trigger_time = TimeCurrent(); g_cb_last_cause = "Daily Loss Limit"; g_cb_last_threshold = CBDailyLossLimitPct; g_cb_last_value = daily_loss_pct; LOG(StringFormat("🚨 CIRCUIT BREAKER TRIGGERED: Daily loss %.2f%% > limit %.2f%%", daily_loss_pct, CBDailyLossLimitPct)); // Log to telemetry if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("daily_loss_pct=%.2f limit_pct=%.2f equity=%.2f session_start=%.2f", daily_loss_pct, CBDailyLossLimitPct, current_equity, g_session_equity_start); g_telemetry_base.LogEvent(_Symbol, _Period, "circuit_breaker", "daily_loss_triggered", details); } return false; } // Check drawdown limit if(CBDrawdownLimitPct > 0 && drawdown_pct > CBDrawdownLimitPct) { g_cb_active = true; g_cb_trigger_time = TimeCurrent(); g_cb_last_cause = "Drawdown Limit"; g_cb_last_threshold = CBDrawdownLimitPct; g_cb_last_value = drawdown_pct; LOG(StringFormat("🚨 CIRCUIT BREAKER TRIGGERED: Drawdown %.2f%% > limit %.2f%%", drawdown_pct, CBDrawdownLimitPct)); // Log to telemetry if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("drawdown_pct=%.2f limit_pct=%.2f equity=%.2f highwater=%.2f", drawdown_pct, CBDrawdownLimitPct, current_equity, g_equity_highwater); g_telemetry_base.LogEvent(_Symbol, _Period, "circuit_breaker", "drawdown_triggered", details); } return false; } return true; } // Reset circuit breaker manually (for testing/recovery) void ResetCircuitBreaker() { if(g_cb_active) { g_cb_active = false; LOG(StringFormat("Circuit breaker manually reset. Previous cause: %s", g_cb_last_cause)); if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { g_telemetry_base.LogEvent(_Symbol, _Period, "circuit_breaker", "manual_reset", "cause=" + g_cb_last_cause); } } } //+------------------------------------------------------------------+ //| NEWS FILTER SYSTEM - MARKET EVENT PROTECTION | //+------------------------------------------------------------------+ // Check if trading is allowed based on news events bool CheckNewsFilter() { if(!UseNewsFilter) return true; datetime now = TimeCurrent(); // Check cached news events for(int i = 0; i < ArraySize(g_news_from); i++) { if(now >= g_news_from[i] && now <= g_news_to[i]) { if(g_news_impact[i] >= NewsImpactMin) { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("📰 Trading blocked by news filter: %s (impact=%d, min=%d)", g_news_key[i], g_news_impact[i], NewsImpactMin)); // Log to telemetry if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("event=%s impact=%d min_impact=%d from=%s to=%s", g_news_key[i], g_news_impact[i], NewsImpactMin, TimeToString(g_news_from[i]), TimeToString(g_news_to[i])); g_telemetry_base.LogEvent(_Symbol, _Period, "news_filter", "blocked", details); } return false; } } } return true; } // Load news events from CSV file void LoadNewsEvents() { if(!NewsUseFile) return; string path = NewsFileRelPath; int h = FileOpen(path, FILE_READ|FILE_COMMON|FILE_CSV|FILE_ANSI, ','); if(h == INVALID_HANDLE) { if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("News file not found: %s", path)); return; } // Clear existing news data ArrayResize(g_news_key, 0); ArrayResize(g_news_from, 0); ArrayResize(g_news_to, 0); ArrayResize(g_news_impact, 0); int loaded_count = 0; while(!FileIsEnding(h)) { string event_name = FileReadString(h); if(event_name == "") continue; // Skip empty lines datetime from_time = (datetime)FileReadNumber(h); datetime to_time = (datetime)FileReadNumber(h); int impact = (int)FileReadNumber(h); if(from_time > 0 && to_time > 0 && impact > 0) { int n = ArraySize(g_news_key); ArrayResize(g_news_key, n + 1); ArrayResize(g_news_from, n + 1); ArrayResize(g_news_to, n + 1); ArrayResize(g_news_impact, n + 1); g_news_key[n] = event_name; g_news_from[n] = from_time - NewsBufferBeforeMin * 60; g_news_to[n] = to_time + NewsBufferAfterMin * 60; g_news_impact[n] = impact; loaded_count++; } } FileClose(h); if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("📰 Loaded %d news events for filtering (buffer: %d min before, %d min after)", loaded_count, NewsBufferBeforeMin, NewsBufferAfterMin)); // Log to telemetry if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("loaded_count=%d buffer_before=%d buffer_after=%d min_impact=%d", loaded_count, NewsBufferBeforeMin, NewsBufferAfterMin, NewsImpactMin); g_telemetry_base.LogEvent(_Symbol, _Period, "news_filter", "loaded", details); } } //+------------------------------------------------------------------+ //| ADVANCED STRATEGY REGISTRY - 23 STRATEGY INTEGRATION | //+------------------------------------------------------------------+ // Initialize the complete strategy registry with all 23 strategies bool InitializeStrategyRegistry() { if(!UseStrategySelector) return true; // Complete list of 23 strategies from AssetRegistry string strategies[] = { "ADXStrategy", "AcceleratorOscillatorStrategy", "AlligatorStrategy", "AwesomeOscillatorStrategy", "BearsPowerStrategy", "BullsPowerStrategy", "CCIStrategy", "DeMarkerStrategy", "ForceIndexStrategy", "FractalsStrategy", "GatorStrategy", "IchimokuStrategy", "MACDStrategy", "MomentumStrategy", "OsMAStrategy", "RSIStrategy", "RVIStrategy", "StochasticStrategy", "TriXStrategy", "UltimateOscillatorStrategy", "WilliamsPercentRangeStrategy", "ZigZagStrategy", "MovingAverageStrategy" }; int registered_count = 0; for(int i = 0; i < ArraySize(strategies); i++) { // No explicit registration API in selector; count for reporting if(CheckPointer(g_selector) != POINTER_INVALID) { registered_count++; } // Add to global strategies container for management if(CheckPointer(g_strategies) != POINTER_INVALID) { // Create strategy metadata object (simplified for now) CObject* strategy_obj = new CObject(); if(strategy_obj != NULL) { g_strategies.Add(strategy_obj); } } } if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("🎯 Strategy Registry initialized: %d/%d strategies registered", registered_count, ArraySize(strategies))); // Log to telemetry if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("total_strategies=%d registered=%d symbol=%s timeframe=%d", ArraySize(strategies), registered_count, _Symbol, _Period); g_telemetry_base.LogEvent(_Symbol, _Period, "strategy_registry", "initialized", details); } return (registered_count > 0); } // Get strategy performance metrics for selection bool GetStrategyMetrics(const string strategy_name, double &profit_factor, double &win_rate, double &expectancy, double &drawdown) { // This would normally query the insights system or knowledge base // For now, provide default metrics to ensure system functionality profit_factor = 1.2 + (MathRand() % 100) / 1000.0; // 1.2 to 1.3 win_rate = 0.45 + (MathRand() % 20) / 100.0; // 45% to 65% expectancy = -0.1 + (MathRand() % 30) / 100.0; // -0.1 to 0.2 drawdown = 0.05 + (MathRand() % 15) / 100.0; // 5% to 20% return true; } // Enhanced strategy selection with performance weighting string SelectBestStrategy() { if(!UseStrategySelector || CheckPointer(g_selector) == POINTER_INVALID) { return "MovingAverageStrategy"; // Fallback } // Use the strategy selector to pick best performing strategy string strategies[] = { "ADXStrategy", "AcceleratorOscillatorStrategy", "AlligatorStrategy", "AwesomeOscillatorStrategy", "BearsPowerStrategy", "BullsPowerStrategy", "CCIStrategy", "DeMarkerStrategy", "ForceIndexStrategy", "FractalsStrategy", "GatorStrategy", "IchimokuStrategy", "MACDStrategy", "MomentumStrategy", "OsMAStrategy", "RSIStrategy", "RVIStrategy", "StochasticStrategy", "TriXStrategy", "UltimateOscillatorStrategy", "WilliamsPercentRangeStrategy", "ZigZagStrategy", "MovingAverageStrategy" }; double scores[]; int best_idx = g_selector.PickBest(_Symbol, _Period, strategies, scores); string selected = (best_idx>=0? strategies[best_idx] : ""); if(selected == "") { // Fallback selection based on simple criteria string fallback_strategies[] = { "MovingAverageStrategy", "MACDStrategy", "RSIStrategy", "StochasticStrategy", "ADXStrategy" }; int idx = MathRand() % ArraySize(fallback_strategies); selected = fallback_strategies[idx]; if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_DEBUG)) LOG(StringFormat("Strategy selector returned empty, using fallback: %s", selected)); } else { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("🎯 Strategy selector picked: %s (index %d)", selected, best_idx)); } return selected; } //+------------------------------------------------------------------+ //| ENHANCED REGIME DETECTION SYSTEM | //+------------------------------------------------------------------+ // Advanced market regime detection with multiple indicators string GetAdvancedMarketRegime() { if(!UseRegimeGate) return GetMarketRegime(); // Use simple version string regime = "unknown"; double regime_score = 0.0; // ATR-based volatility regime if(RegimeMethod == "atr" || RegimeMethod == "combined") { double atr_pct = GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period) * 100.0; if(atr_pct < RegimeMinATRPct) regime = "low_volatility"; else if(atr_pct > RegimeMaxATRPct) regime = "high_volatility"; else regime = "normal_volatility"; regime_score = atr_pct; } // ADX-based trend regime if(RegimeMethod == "adx" || RegimeMethod == "combined") { int adx_handle = iADX(_Symbol, (ENUM_TIMEFRAMES)_Period, RegimeADXPeriod); double adx_array[1]; if(CopyBuffer(adx_handle, 0, 0, 1, adx_array) == 1) { double adx = adx_array[0]; if(adx > RegimeADXTrendThreshold) { regime = (regime == "unknown") ? "trending" : regime + "_trending"; } else { regime = (regime == "unknown") ? "ranging" : regime + "_ranging"; } regime_score = adx; } } // Log regime detection for telemetry if(RegimeTagTelemetry && TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("regime=%s score=%.2f method=%s atr_min=%.2f atr_max=%.2f adx_threshold=%.1f", regime, regime_score, RegimeMethod, RegimeMinATRPct, RegimeMaxATRPct, RegimeADXTrendThreshold); g_telemetry_base.LogEvent(_Symbol, _Period, "regime_detection", regime, details); } return regime; } // Check if current regime allows trading bool CheckRegimeGate() { if(!UseRegimeGate) return true; string current_regime = GetAdvancedMarketRegime(); // For now, allow all regimes (can be enhanced with regime-specific rules) // In production, you might block certain strategies in certain regimes if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("🌊 Current market regime: %s", current_regime)); return true; } //+------------------------------------------------------------------+ //| MEMORY MANAGEMENT AND LIMITS SYSTEM | //+------------------------------------------------------------------+ // Check and enforce memory limits to prevent resource exhaustion bool CheckMemoryLimits() { // Check real MT5 positions limit int total_positions = PositionsTotal(); if(total_positions > 100) // Reasonable limit for real positions { LOG(StringFormat("⚠️ Position limit reached: %d real MT5 positions", total_positions)); return false; } // Check tracking arrays limits if(ArraySize(g_pos_ids) > 500) { LOG(StringFormat("⚠️ Position tracking limit reached: %d positions", ArraySize(g_pos_ids))); return false; } // Check news events limit if(ArraySize(g_news_key) > 10000) { LOG(StringFormat("⚠️ News events limit reached: %d events", ArraySize(g_news_key))); // Keep only future events datetime now = TimeCurrent(); int kept = 0; for(int i = 0; i < ArraySize(g_news_to); i++) { if(g_news_to[i] > now) { if(kept != i) { g_news_key[kept] = g_news_key[i]; g_news_from[kept] = g_news_from[i]; g_news_to[kept] = g_news_to[i]; g_news_impact[kept] = g_news_impact[i]; } kept++; } } ArrayResize(g_news_key, kept); ArrayResize(g_news_from, kept); ArrayResize(g_news_to, kept); ArrayResize(g_news_impact, kept); LOG(StringFormat("Cleaned up old news events, kept %d future events", kept)); } return true; } //+------------------------------------------------------------------+ //| DECISION PIPELINE (MOVED OFF OnTick) | //+------------------------------------------------------------------+ void RunDecisionPipelineOnce() { datetime now = TimeCurrent(); // ===================[ TRADE FREQUENCY GATING ]=================== if(now - last_paper_trade_time < PaperTradeCooldownSec) return; // ===================[ CRITICAL SAFETY GATES ]=================== if(!CheckCircuitBreakers()) return; if(!CheckMemoryLimits()) { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_ERROR)) LOG("🚨 Memory limits exceeded - blocking new trades"); return; } if(!CheckNewsFilter()) return; // ===================[ EARLY GATES - BASIC FILTERING ]=================== if(!NoConstraintsMode) { if(UseTradingHours) { MqlDateTime dt; TimeToStruct(now, dt); if(dt.hour < TradingStartHour || dt.hour >= TradingEndHour) return; } if(UseSessionManager && CheckPointer(g_session_manager) != POINTER_INVALID) { string sess_reason; if(!g_session_manager.IsSessionAllowed(sess_reason)) return; } if(MaxOpenPositions > 0 && PositionsTotal() >= MaxOpenPositions) return; if(!CheckRegimeGate()) return; } // ===================[ STRATEGY SELECTION & SIGNAL GENERATION ]=================== TradingSignal signal; signal.Init(); string selected_strategy = ""; if(UseStrategySelector) { selected_strategy = SelectBestStrategy(); if(selected_strategy == "") { if(ShouldLog(LOG_DEBUG)) LOG("No strategy selected by enhanced selector"); return; } signal = GenerateSignalFromStrategy(selected_strategy); if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("🎯 Selected strategy: %s", selected_strategy)); } else { signal = GenerateSignal(); selected_strategy = "MovingAverageStrategy"; } if(signal.id == "") return; g_gate_audit.LogStrategyProcessed(selected_strategy, signal.id, true, signal.confidence, "generated"); if(signal.strategy == "") signal.strategy = selected_strategy; if(MathIsValidNumber(signal.confidence) == false || signal.confidence <= 0.0) signal.confidence = HeuristicConfidence(signal); if(signal.confidence < 0.0) signal.confidence = 0.0; if(signal.confidence > 1.0) signal.confidence = 1.0; // Pre-gate ONNX eval throttled to timer cadence if(UseOnnxPredictor && g_model_predictor_ready) { double pre_prob = EvaluateModelProbability(signal, selected_strategy, NULL, "generated", "pre_gate"); if(pre_prob >= 0.0) { double strategy_conf = signal.confidence; if(MathIsValidNumber(strategy_conf) == false || strategy_conf <= 0.0) strategy_conf = HeuristicConfidence(signal); if(strategy_conf < 0.0) strategy_conf = 0.0; if(strategy_conf > 1.0) strategy_conf = 1.0; const double neutral_band = 0.02; double final_conf = strategy_conf; if(MathAbs(pre_prob - 0.5) > neutral_band) final_conf = MathMax(strategy_conf, pre_prob); if(final_conf < 0.0) final_conf = 0.0; if(final_conf > 1.0) final_conf = 1.0; signal.confidence = final_conf; if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("MLPRED pre_gate signal=%s strategy=%s prob=%.3f conf=%.3f", signal.id, selected_strategy, pre_prob, signal.confidence)); } } // ===================[ ADAPTIVE SIGNAL OPTIMIZATION SYSTEM ]=================== if(UseGateSystem && CheckPointer(g_gate_manager) != POINTER_INVALID) { CAdaptiveDecision decision; bool passed = false; string blocking_reason = ""; if(CheckPointer(g_adaptive_optimizer) != POINTER_INVALID) { passed = g_adaptive_optimizer.OptimizeSignal(signal, decision, selected_strategy, blocking_reason); if(!(passed)) { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("🚫 Signal optimization failed: %s - %s", signal.id, blocking_reason)); return; } } else { CSignalDecision standard_decision; if(UseEfficientGates && CheckPointer(g_gate_manager) != POINTER_INVALID) passed = true; else passed = true; if(passed) { g_adaptive_optimizer.CopyDecision(standard_decision, decision); decision.is_adjusted = false; } } if(passed) { if(UseOnnxPredictor && g_model_predictor_ready) { double final_prob = EvaluateModelProbability(signal, selected_strategy, &decision, "passed_gates", blocking_reason); if(final_prob >= 0.0) { decision.confidence = final_prob; if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("MLPRED post_gate signal=%s prob=%.3f status=passed", decision.signal_id, final_prob)); } } if(UsePolicyGating && g_policy_loaded) { if(!ApplyPolicyGating(decision)) { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("Signal blocked by policy gating: %s", decision.signal_id)); return; } } if(!NoConstraintsMode) { if(UseCorrelationManager && CheckPointer(g_correlation_manager) != POINTER_INVALID) { string corr_reason; double max_corr=0.0; if(!g_correlation_manager.CheckCorrelationLimits(corr_reason, max_corr)) { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("Signal blocked by correlation limits: %s (reason=%s max_corr=%.3f)", decision.signal_id, corr_reason, max_corr)); return; } } if(UseVolatilitySizer && CheckPointer(g_volatility_sizer) != POINTER_INVALID) { double sl_points = 0.0; if(decision.final_sl > 0.0) sl_points = MathAbs(decision.final_price - decision.final_sl) / _Point; double vol_mult = 1.0; string vz_reason = ""; double adjusted_volume = g_volatility_sizer.CalculatePositionSize(decision.final_volume, sl_points, vol_mult, vz_reason); if(adjusted_volume != decision.final_volume) { if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("Volume adjusted by volatility sizer: %.2f → %.2f (%s)", decision.final_volume, adjusted_volume, vz_reason)); decision.final_volume = adjusted_volume; } } } // PaperEA executes real trades on DEMO when enabled if(LiveTradingEnabled) ExecutePaperTrade(decision); LogDecisionTelemetry(decision); if(decision.is_adjusted) LogAdaptiveDecisionDetails(decision, selected_strategy); if(CheckPointer(g_adaptive_optimizer) != POINTER_INVALID && decision.complete_journey_length > 0) g_adaptive_optimizer.PrintCompleteGateJourney(decision); if(CheckPointer(g_kb) != POINTER_INVALID) { g_kb.LogTradeExecution(decision.symbol, selected_strategy, decision.execution_time, decision.final_price, decision.final_volume, decision.order_type); } if(CheckPointer(g_features) != POINTER_INVALID) ExportEnhancedFeaturesAdaptive(decision, selected_strategy); last_paper_trade_time = now; } else { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("Signal rejected by gates: %s", signal.id)); } } else { if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_DEBUG)) LOG("Gate system disabled - executing signal directly"); if(LiveTradingEnabled) ExecutePaperTrade(signal); last_paper_trade_time = now; } } //+------------------------------------------------------------------+ //| MAIN TICK HANDLER (LIGHTWEIGHT) | //+------------------------------------------------------------------+ void OnTick() { if(g_master_controller != NULL) g_master_controller.OnTick(); OnTickP0P5(); } //+------------------------------------------------------------------+ //| SIGNAL GENERATION | //+------------------------------------------------------------------+ TradingSignal GenerateSignal() { TradingSignal signal; signal.Init(); // CRITICAL: Initialize all fields to safe defaults // Simple moving average crossover strategy - automatically uses chart symbol/timeframe int fast_ma_handle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE); double fast_ma_array[1]; CopyBuffer(fast_ma_handle, 0, 0, 1, fast_ma_array); double fast_ma = fast_ma_array[0]; int slow_ma_handle = iMA(_Symbol, _Period, 50, 0, MODE_SMA, PRICE_CLOSE); double slow_ma_array[1]; CopyBuffer(slow_ma_handle, 0, 0, 1, slow_ma_array); double slow_ma = slow_ma_array[0]; double current_price = 0; SymbolInfoDouble(_Symbol, SYMBOL_BID, current_price); // CRITICAL FIX: Calculate proper ATR-based SL/TP distances double atr = 0.0; int atr_handle = iATR(_Symbol, _Period, 14); if(atr_handle != INVALID_HANDLE) { double atr_array[1]; if(CopyBuffer(atr_handle, 0, 0, 1, atr_array) == 1) atr = atr_array[0]; IndicatorRelease(atr_handle); } // Fallback to minimum broker distance if ATR unavailable if(atr <= 0.0) { long stops_level = 0; SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL, stops_level); double point = 0.0; SymbolInfoDouble(_Symbol, SYMBOL_POINT, point); atr = MathMax(100.0 * point, (double)stops_level * point * 3.0); } // Use input parameters for SL/TP multipliers double sl_distance = atr * StopLossPips / 100.0; // Scale by user input double tp_distance = atr * TakeProfitPips / 100.0; // Scale by user input if(fast_ma > slow_ma && fast_ma < current_price) { signal.id = "MA_CROSS_" + IntegerToString(TimeCurrent()); signal.symbol = _Symbol; signal.timeframe = _Period; signal.timestamp = TimeCurrent(); signal.price = current_price; signal.type = 0; // 0=buy signal.sl = current_price - sl_distance; // ATR-based SL signal.tp = current_price + tp_distance; // ATR-based TP signal.volume = LotSize; signal.confidence = 0.75; signal.strategy = "MovingAverageStrategy"; // Default strategy // Market context signal.volatility = GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period); signal.correlation = GetCorrelation(); signal.regime = GetMarketRegime(); signal.market_regime = GetMarketRegime(); } else if(fast_ma < slow_ma && fast_ma > current_price) { signal.id = "MA_CROSS_" + IntegerToString(TimeCurrent()); signal.symbol = _Symbol; signal.timeframe = _Period; signal.timestamp = TimeCurrent(); signal.price = current_price; signal.type = 1; // 1=sell signal.sl = current_price + sl_distance; // ATR-based SL signal.tp = current_price - tp_distance; // ATR-based TP signal.volume = LotSize; signal.confidence = 0.75; signal.strategy = "MovingAverageStrategy"; // Default strategy // Market context signal.volatility = GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period); signal.correlation = GetCorrelation(); signal.regime = GetMarketRegime(); signal.market_regime = GetMarketRegime(); } return signal; } //+------------------------------------------------------------------+ //| TRADE EXECUTION - REAL MT5 ORDERS | //+------------------------------------------------------------------+ void ExecutePaperTrade(CSignalDecision &decision) { if(!LiveTradingEnabled) { // PaperEA safety: do not execute real trades unless explicitly enabled if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("[PAPER-SAFETY] LiveTradingEnabled=false -> skipping REAL trade execution for %s", decision.signal_id)); return; } // Execute REAL trade to MT5 (on demo account) LOG(StringFormat("🎯 Executing REAL MT5 trade: %s at %.5f", decision.signal_id, decision.final_price)); // Validate trade manager if(CheckPointer(g_trade_manager) == POINTER_INVALID) { LOG("❌ ERROR: TradeManager not initialized!"); return; } // Create TradeOrder struct for real MT5 execution // Ensure SL/TP fallbacks if gates/strategies left them unset (ATR/min distance) if(decision.final_sl<=0.0 || decision.final_tp<=0.0) { double __bid=0.0, __ask=0.0; SymbolInfoDouble(_Symbol, SYMBOL_BID, __bid); SymbolInfoDouble(_Symbol, SYMBOL_ASK, __ask); double __pt=0.0; SymbolInfoDouble(_Symbol, SYMBOL_POINT, __pt); double __tick=0.0; SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE, __tick); if(__tick<=0.0) __tick=__pt; int __digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); long __slvl=0, __flvl=0; SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL, __slvl); SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL, __flvl); double __minDist = (double)__slvl * __pt; double __frzDist = (double)__flvl * __pt; double __need = MathMax(__minDist, __frzDist); // ATR-based distance (2x ATR) with conservative fallback double __atr=0.0; int __h=iATR(_Symbol, _Period, 14); if(__h!=INVALID_HANDLE){ double __b[1]; if(CopyBuffer(__h,0,0,1,__b)==1) __atr=__b[0]; IndicatorRelease(__h);} double __dist = (__atr>0.0 ? __atr*2.0 : MathMax(100.0*__pt, __need*3.0)); if(decision.order_type==ORDER_TYPE_BUY || decision.order_type==ORDER_TYPE_BUY_STOP || decision.order_type==ORDER_TYPE_BUY_LIMIT) { if(decision.final_sl<=0.0 && __bid>0.0) { double __sl = __bid - __dist; if(__sl>=__bid-__need) __sl = (__bid-__need) - (__tick*0.5); double __ticks = MathFloor((__sl + 1e-12)/__tick); decision.final_sl = NormalizeDouble(__ticks*__tick, __digits); } if(decision.final_tp<=0.0 && __ask>0.0) { double __tp = __ask + __dist; if(__tp<=__ask+__need) __tp = (__ask+__need) + (__tick*0.5); double __ticks_up = MathCeil((__tp - 1e-12)/__tick); decision.final_tp = NormalizeDouble(__ticks_up*__tick, __digits); } } else { if(decision.final_sl<=0.0 && __ask>0.0) { double __sl = __ask + __dist; if(__sl<=__ask+__need) __sl = (__ask+__need) + (__tick*0.5); double __ticks = MathCeil((__sl - 1e-12)/__tick); decision.final_sl = NormalizeDouble(__ticks*__tick, __digits); } if(decision.final_tp<=0.0 && __bid>0.0) { double __tp = __bid - __dist; if(__tp>=__bid-__need) __tp = (__bid-__need) - (__tick*0.5); double __ticks_dn = MathFloor((__tp + 1e-12)/__tick); decision.final_tp = NormalizeDouble(__ticks_dn*__tick, __digits); } } } TradeOrder order; // Set action based on order type if(decision.order_type == ORDER_TYPE_BUY || decision.order_type == ORDER_TYPE_BUY_STOP || decision.order_type == ORDER_TYPE_BUY_LIMIT) order.action = ACTION_BUY; else if(decision.order_type == ORDER_TYPE_SELL || decision.order_type == ORDER_TYPE_SELL_STOP || decision.order_type == ORDER_TYPE_SELL_LIMIT) order.action = ACTION_SELL; else order.action = ACTION_NONE; order.order_type = (ENUM_ORDER_TYPE)decision.order_type; order.lots = decision.final_volume; order.price = decision.final_price; order.stop_loss = decision.final_sl; order.take_profit = decision.final_tp; order.strategy_name = decision.strategy; // EXECUTE REAL TRADE TO MT5 bool success = g_trade_manager.ExecuteOrder(order); if(success) { // Get execution results from TradeManager ulong deal_ticket = g_trade_manager.ResultDeal(); ulong order_ticket = g_trade_manager.ResultOrder(); double exec_price = g_trade_manager.ResultPrice(); double exec_volume = order.lots; // Volume executed // Update decision with REAL execution details decision.executed = true; decision.execution_time = TimeCurrent(); decision.execution_price = exec_price; // Actual fill price LOG(StringFormat("✅ REAL TRADE EXECUTED: Deal=%I64u Order=%I64u Price=%.5f Volume=%.2f SL=%.5f TP=%.5f", deal_ticket, order_ticket, exec_price, exec_volume, decision.final_sl, decision.final_tp)); // Log to unified trade logger (with gate journey) if(CheckPointer(g_trade_logger) != POINTER_INVALID) { UnifiedTradeRecord record; record.trade_id = IntegerToString(deal_ticket); record.signal_id = decision.signal_id; record.execution_time = decision.execution_time; record.is_closed = false; // Trade just opened record.strategy_name = decision.strategy; record.symbol = decision.symbol; record.timeframe = decision.timeframe; record.market_regime = GetMarketRegime(); record.volatility = GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period); record.entry_price = exec_price; record.sl = decision.final_sl; record.tp = decision.final_tp; record.volume = exec_volume; record.confidence = decision.confidence; // Copy gate journey from adaptive optimizer if(CheckPointer(g_adaptive_optimizer) != POINTER_INVALID) { // Gate journey was already recorded during optimization // Just log the execution } g_trade_logger.LogTradeExecution(record); } // Log execution for learning bridge if(CheckPointer(g_learning_bridge) != POINTER_INVALID) { g_learning_bridge.RecordSignal(decision); g_learning_bridge.UpdateMarketRegime(); } // Export features for ML training string features[]; ArrayResize(features, 8); features[0] = "entry_price:" + DoubleToString(exec_price, 5); features[1] = "volume:" + DoubleToString(exec_volume, 2); features[2] = "order_type:" + IntegerToString((ENUM_ORDER_TYPE)decision.order_type); features[3] = "strategy:" + decision.strategy; features[4] = "signal_confidence:" + DoubleToString(decision.confidence, 3); features[5] = "market_regime:" + GetMarketRegime(); features[6] = "volatility:" + DoubleToString(GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period), 4); features[7] = "correlation:" + DoubleToString(GetCorrelation(), 3); // Export to ML pipeline via file-based system (strategy tester compatible) string out_path; int result = g_file_exporter.ExportStringFeatures(features, ArraySize(features), out_path); if(result != 0) Print("ExportStringFeatures failed: ", result); else Print("Feature batch exported to: ", out_path); } else { uint retcode = g_trade_manager.ResultRetcode(); LOG(StringFormat("❌ TRADE EXECUTION FAILED: Retcode=%u Signal=%s", retcode, decision.signal_id)); decision.executed = false; } } void ExecutePaperTrade(TradingSignal &signal) { if(!LiveTradingEnabled) { // PaperEA safety: do not execute real trades unless explicitly enabled if(PAPEREA_SHOULD_LOG(PAPEREA_LOG_INFO)) LOG(StringFormat("[PAPER-SAFETY] LiveTradingEnabled=false -> skipping REAL trade execution for %s", signal.id)); return; } // Direct execution without gates - REAL MT5 execution LOG(" Executing REAL MT5 trade (no gates): " + signal.id + " at " + DoubleToString(signal.price, 5)); if(CheckPointer(g_trade_manager) == POINTER_INVALID) { LOG(" ERROR: TradeManager not initialized!"); return; } // Convert TradingSignal to TradeOrder // Ensure SL/TP fallbacks for direct signal execution as well if(signal.sl<=0.0 || signal.tp<=0.0) { double __bid=0.0, __ask=0.0; SymbolInfoDouble(_Symbol, SYMBOL_BID, __bid); SymbolInfoDouble(_Symbol, SYMBOL_ASK, __ask); double __pt=0.0; SymbolInfoDouble(_Symbol, SYMBOL_POINT, __pt); double __tick=0.0; SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE, __tick); if(__tick<=0.0) __tick=__pt; int __digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); long __slvl=0, __flvl=0; SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL, __slvl); SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL, __flvl); double __minDist = (double)__slvl * __pt; double __frzDist = (double)__flvl * __pt; double __need = MathMax(__minDist, __frzDist); // ATR-based distance (2x ATR) with conservative fallback double __atr=0.0; int __h=iATR(_Symbol, _Period, 14); if(__h!=INVALID_HANDLE){ double __b[1]; if(CopyBuffer(__h,0,0,1,__b)==1) __atr=__b[0]; IndicatorRelease(__h);} double __dist = (__atr>0.0 ? __atr*2.0 : MathMax(100.0*__pt, __need*3.0)); ENUM_ORDER_TYPE __order_type = (signal.type == 0) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; if(__order_type==ORDER_TYPE_BUY) { if(signal.sl<=0.0 && __bid>0.0) { double __sl = __bid - __dist; if(__sl>=__bid-__need) __sl = (__bid-__need) - (__tick*0.5); double __ticks = MathFloor((__sl + 1e-12)/__tick); signal.sl = NormalizeDouble(__ticks*__tick, __digits); } if(signal.tp<=0.0 && __ask>0.0) { double __tp = __ask + __dist; if(__tp<=__ask+__need) __tp = (__ask+__need) + (__tick*0.5); double __ticks_up = MathCeil((__tp - 1e-12)/__tick); signal.tp = NormalizeDouble(__ticks_up*__tick, __digits); } } else { if(signal.sl<=0.0 && __ask>0.0) { double __sl = __ask + __dist; if(__sl<=__ask+__need) __sl = (__ask+__need) + (__tick*0.5); double __ticks = MathCeil((__sl - 1e-12)/__tick); signal.sl = NormalizeDouble(__ticks*__tick, __digits); } if(signal.tp<=0.0 && __bid>0.0) { double __tp = __bid - __dist; if(__tp>=__bid-__need) __tp = (__bid-__need) - (__tick*0.5); double __ticks_dn = MathFloor((__tp + 1e-12)/__tick); signal.tp = NormalizeDouble(__ticks_dn*__tick, __digits); } } } TradeOrder order; // Convert signal.type (0=buy, 1=sell) to ENUM_ORDER_TYPE if(signal.type == 0) // Buy { order.action = ACTION_BUY; order.order_type = ORDER_TYPE_BUY; } else // Sell { order.action = ACTION_SELL; order.order_type = ORDER_TYPE_SELL; } order.lots = signal.volume; order.price = signal.price; order.stop_loss = signal.sl; order.take_profit = signal.tp; order.strategy_name = signal.strategy; // Use the strategy that generated the signal // EXECUTE REAL TRADE bool success = g_trade_manager.ExecuteOrder(order); if(success) { ulong deal_ticket = g_trade_manager.ResultDeal(); ulong order_ticket = g_trade_manager.ResultOrder(); double exec_price = g_trade_manager.ResultPrice(); LOG(StringFormat("✅ REAL TRADE EXECUTED (no gates): Deal=%I64u Order=%I64u Price=%.5f Volume=%.2f", deal_ticket, order_ticket, exec_price, signal.volume)); // Log to KB if(CheckPointer(g_kb) != POINTER_INVALID) { g_kb.LogTradeExecution(signal.symbol, signal.strategy, TimeCurrent(), exec_price, signal.volume, (int)order.order_type); } } else { uint retcode = g_trade_manager.ResultRetcode(); LOG(StringFormat("❌ TRADE EXECUTION FAILED (no gates): Retcode=%u Signal=%s", retcode, signal.id)); } } //+------------------------------------------------------------------+ //| TELEMETRY LOGGING | //+------------------------------------------------------------------+ void LogDecisionTelemetry(CSignalDecision &decision) { // Log decision telemetry string log_data = StringFormat( "Decision: %s, Symbol: %s, Gates: [%d,%d,%d,%d,%d,%d,%d,%d], " + "Original: %.5f/%.5f/%.5f/%.2f, " + "Final: %.5f/%.5f/%.5f/%.2f", decision.signal_id, decision.symbol, decision.gate_results[0], decision.gate_results[1], decision.gate_results[2], decision.gate_results[3], decision.gate_results[4], decision.gate_results[5], decision.gate_results[6], decision.gate_results[7], decision.original_price, decision.original_sl, decision.original_tp, decision.original_volume, decision.final_price, decision.final_sl, decision.final_tp, decision.final_volume ); LOG(log_data); } // Log adaptive decision details with attempt history void LogAdaptiveDecisionDetails(CAdaptiveDecision &decision, const string strategy_name) { if(!decision.is_adjusted) return; LOG("\n=== 🔧 ADAPTIVE OPTIMIZATION DETAILS ==="); LOG(StringFormat("Strategy: %s | Original Signal: %s", strategy_name, decision.original_signal_id)); LOG(StringFormat("Final Signal: %s | Total Attempts: %d", decision.signal_id, decision.adjustment_attempts)); for(int i = 0; i < decision.adjustment_attempts; i++) { AdjustmentAttempt att = decision.attempts[i]; LOG(StringFormat(" Attempt %d: Price%+.2f%% SL×%.2f TP×%.2f Vol×%.2f → %s", att.attempt_number, att.price_tweak * 100, att.sl_tweak, att.tp_tweak, att.volume_tweak, att.passed ? "✅ PASSED" : "❌ FAILED")); } LOG(StringFormat("Final Parameters: Price=%.5f SL=%.5f TP=%.5f Vol=%.2f", decision.final_price, decision.final_sl, decision.final_tp, decision.final_volume)); LOG("========================================"); // Log to telemetry if available if(TelemetryEnabled && CheckPointer(g_telemetry_base) != POINTER_INVALID) { string details = StringFormat("strategy=%s,attempts=%d,orig_vol=%.2f,final_vol=%.2f", strategy_name, decision.adjustment_attempts, decision.original_volume, decision.final_volume); g_telemetry_base.LogEvent(_Symbol, _Period, "adaptive_optimization", "signal_adjusted", details); } } // Export enhanced features with adaptive tracking void ExportEnhancedFeaturesAdaptive(const CAdaptiveDecision &decision, const string strategy_name); void AppendSnapshotFeature(string &features[], int &count, const string key, const string value) { ArrayResize(features, count + 1); features[count++] = key + ":" + value; } void ExportSignalAttemptSnapshot(const TradingSignal &signal, const string strategy_name, const string status, const string reason, const CAdaptiveDecision *decision_ptr = NULL) { string features[]; int feature_count = 0; datetime now = TimeCurrent(); MqlDateTime _dt; TimeToStruct(now, _dt); string symbol = signal.symbol; int timeframe = _Period; double price = signal.price; double volume = signal.volume; double sl = signal.sl; double tp = signal.tp; double confidence = signal.confidence; double volatility = signal.volatility; double correlation = signal.correlation; string regime = signal.regime; string market_regime = signal.market_regime; int order_type = signal.type; bool executed = false; bool is_adjusted = false; int gate_passed = 0; if(decision_ptr != NULL) { symbol = (decision_ptr.symbol != "") ? decision_ptr.symbol : symbol; timeframe = (decision_ptr.timeframe != 0) ? decision_ptr.timeframe : timeframe; price = (decision_ptr.final_price != 0.0) ? decision_ptr.final_price : price; volume = (decision_ptr.final_volume != 0.0) ? decision_ptr.final_volume : volume; sl = (decision_ptr.final_sl != 0.0) ? decision_ptr.final_sl : sl; tp = (decision_ptr.final_tp != 0.0) ? decision_ptr.final_tp : tp; confidence = (decision_ptr.confidence != 0.0) ? decision_ptr.confidence : confidence; volatility = (decision_ptr.volatility != 0.0) ? decision_ptr.volatility : volatility; correlation = (decision_ptr.correlation_score != 0.0) ? decision_ptr.correlation_score : correlation; regime = (decision_ptr.market_regime != "") ? decision_ptr.market_regime : regime; market_regime = decision_ptr.market_regime; order_type = decision_ptr.order_type; executed = decision_ptr.executed; is_adjusted = decision_ptr.is_adjusted; gate_passed = CountPassedGates(*decision_ptr); } AppendSnapshotFeature(features, feature_count, "timestamp", TimeToString(now, TIME_DATE|TIME_SECONDS)); AppendSnapshotFeature(features, feature_count, "hour", IntegerToString(_dt.hour)); AppendSnapshotFeature(features, feature_count, "status", status); AppendSnapshotFeature(features, feature_count, "reason", reason); AppendSnapshotFeature(features, feature_count, "strategy", strategy_name); AppendSnapshotFeature(features, feature_count, "signal_id", signal.id); AppendSnapshotFeature(features, feature_count, "symbol", symbol); AppendSnapshotFeature(features, feature_count, "timeframe", IntegerToString(timeframe)); AppendSnapshotFeature(features, feature_count, "order_type", IntegerToString(order_type)); AppendSnapshotFeature(features, feature_count, "price", DoubleToString(price, 5)); AppendSnapshotFeature(features, feature_count, "volume", DoubleToString(volume, 2)); AppendSnapshotFeature(features, feature_count, "sl", DoubleToString(sl, 5)); AppendSnapshotFeature(features, feature_count, "tp", DoubleToString(tp, 5)); AppendSnapshotFeature(features, feature_count, "confidence", DoubleToString(confidence, 3)); AppendSnapshotFeature(features, feature_count, "volatility", DoubleToString(volatility, 5)); AppendSnapshotFeature(features, feature_count, "correlation", DoubleToString(correlation, 5)); AppendSnapshotFeature(features, feature_count, "regime", regime); AppendSnapshotFeature(features, feature_count, "market_regime", market_regime); AppendSnapshotFeature(features, feature_count, "executed", executed ? "1" : "0"); AppendSnapshotFeature(features, feature_count, "is_adjusted", is_adjusted ? "1" : "0"); AppendSnapshotFeature(features, feature_count, "gate_passed_count", IntegerToString(gate_passed)); if(decision_ptr != NULL) { AppendSnapshotFeature(features, feature_count, "adjustment_attempts", IntegerToString(decision_ptr.adjustment_attempts)); AppendSnapshotFeature(features, feature_count, "original_signal_id", decision_ptr.is_adjusted ? decision_ptr.original_signal_id : decision_ptr.signal_id); AppendSnapshotFeature(features, feature_count, "volume_change_pct", DoubleToString(decision_ptr.original_volume > 0 ? (decision_ptr.final_volume - decision_ptr.original_volume) / decision_ptr.original_volume * 100.0 : 0.0, 2)); AppendSnapshotFeature(features, feature_count, "price_change_pct", DoubleToString(decision_ptr.original_price > 0 ? (decision_ptr.final_price - decision_ptr.original_price) / decision_ptr.original_price * 100.0 : 0.0, 2)); AppendSnapshotFeature(features, feature_count, "sl_scale", DoubleToString(decision_ptr.original_sl > 0 ? decision_ptr.final_sl / decision_ptr.original_sl : 1.0, 3)); AppendSnapshotFeature(features, feature_count, "tp_scale", DoubleToString(decision_ptr.original_tp > 0 ? decision_ptr.final_tp / decision_ptr.original_tp : 1.0, 3)); for(int i = 0; i < 8; i++) { string gate_key = StringFormat("gate%d", i + 1); AppendSnapshotFeature(features, feature_count, gate_key + "_pass", decision_ptr.gate_results[i] ? "1" : "0"); if(decision_ptr.gate_reasons[i] != "") AppendSnapshotFeature(features, feature_count, gate_key + "_reason", decision_ptr.gate_reasons[i]); } } string out_path; int result = g_file_exporter.ExportStringFeatures(features, feature_count, out_path); if(result != 0) Print(StringFormat("ExportSignalAttemptSnapshot failed (%s): %d", status, result)); else if(ShouldLog(LOG_DEBUG)) Print(StringFormat("Signal snapshot (%s) exported to: %s", status, out_path)); } // Helper to count passed gates int CountPassedGates(const CSignalDecision &decision) { int count = 0; for(int i = 0; i < 8; i++) { if(decision.gate_results[i]) count++; } return count; } // NOTE: Market analysis helpers (GetVolatility, GetCorrelation, GetMarketRegime) // are provided by regime_adaptation.mqh to avoid duplicate definitions here. //+------------------------------------------------------------------+ //| TIMER FOR LEARNING UPDATES | //+------------------------------------------------------------------+ // Consolidated into enhanced OnTimer below //+------------------------------------------------------------------+ //| Update Paper Positions | //+------------------------------------------------------------------+ // UpdatePaperPositions() REMOVED - using REAL MT5 positions // Position updates handled automatically by MT5 terminal // Use PositionGetDouble(POSITION_PROFIT) to get current PnL // Use OnTradeTransaction() to track position lifecycle events //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| MISSING FUNCTIONS - ENHANCED IMPLEMENTATIONS | //+------------------------------------------------------------------+ // Generate signal from specific strategy using the extensible signal generator registry TradingSignal GenerateSignalFromStrategy(const string strategy_name) { TradingSignal signal; signal.Init(); // CRITICAL: Initialize all fields to safe defaults // DEBUG: Log entry Print(StringFormat("[SignalGen] Generating signal for strategy: %s", strategy_name)); // Try to use the strategy-specific signal generator from the registry if(CheckPointer(g_signal_registry) != POINTER_INVALID) { bool generated = g_signal_registry.GenerateSignal(strategy_name, _Symbol, (ENUM_TIMEFRAMES)_Period, signal); Print(StringFormat("[SignalGen] Registry.GenerateSignal returned: %s", generated ? "true" : "false")); Print(StringFormat("[SignalGen] Signal ID after generation: '%s'", signal.id)); Print(StringFormat("[SignalGen] Signal price=%.5f sl=%.5f tp=%.5f conf=%.4f", signal.price, signal.sl, signal.tp, signal.confidence)); if(generated && signal.id != "") { // Signal was generated by strategy-specific logic // VALIDATION: Ensure critical fields are populated if(signal.price <= 0.0) { Print(StringFormat("[SignalGen] ERROR: Signal price is zero for %s, using current price", strategy_name)); signal.price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.entry_price = signal.price; } if(signal.sl <= 0.0) { Print(StringFormat("[SignalGen] ERROR: Signal SL is zero for %s, setting default", strategy_name)); double atr = GetATR(_Symbol, (ENUM_TIMEFRAMES)_Period, 14); signal.sl = signal.price - (atr > 0 ? atr * 1.5 : 100 * _Point); signal.stop_loss = signal.sl; } if(signal.tp <= 0.0) { Print(StringFormat("[SignalGen] ERROR: Signal TP is zero for %s, setting default", strategy_name)); double atr = GetATR(_Symbol, (ENUM_TIMEFRAMES)_Period, 14); signal.tp = signal.price + (atr > 0 ? atr * 2.5 : 200 * _Point); signal.take_profit = signal.tp; } if(signal.confidence <= 0.0 || signal.confidence > 1.0) { Print(StringFormat("[SignalGen] WARNING: Signal confidence invalid (%.4f) for %s, setting to 0.5", signal.confidence, strategy_name)); signal.confidence = 0.5; } // Ensure market context is set if(signal.volatility <= 0.0) signal.volatility = GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period); if(signal.regime == "") signal.regime = GetMarketRegime(); if(signal.market_regime == "") signal.market_regime = signal.regime; if(signal.correlation == 0.0) signal.correlation = GetCorrelation(); Print(StringFormat("[SignalGen] SUCCESS: %s signal validated and ready", strategy_name)); return signal; } else if(!generated) { Print(StringFormat("[SignalGen] Registry failed to generate signal for %s (no signal condition met)", strategy_name)); } else { Print(StringFormat("[SignalGen] Registry generated signal but ID is empty for %s", strategy_name)); } } else { Print("[SignalGen] ERROR: g_signal_registry is NULL or invalid"); } // Fallback to legacy MA crossover signal generation if registry unavailable or no signal Print("[SignalGen] Falling back to legacy GenerateSignal()"); signal = GenerateSignal(); // Only override strategy name and ID if a valid signal was generated if(signal.id != "") { signal.id = strategy_name + "_" + IntegerToString(TimeCurrent()); signal.strategy = strategy_name; // Ensure market context is set if(signal.volatility <= 0.0) signal.volatility = GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period); if(signal.regime == "") signal.regime = GetMarketRegime(); if(signal.market_regime == "") signal.market_regime = signal.regime; if(signal.correlation == 0.0) signal.correlation = GetCorrelation(); } else { Print("[SignalGen] WARNING: Legacy GenerateSignal() also returned empty signal"); } return signal; } // Apply policy-based gating and scaling bool ApplyPolicyGating(CSignalDecision &decision) { if(!g_policy_loaded) { // Fallback behavior when no policy is loaded if(DefaultPolicyFallback) { if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("FALLBACK: no policy loaded -> neutral scaling used for %s", decision.signal_id)); return true; // Allow with neutral scaling } return false; // Block if no fallback allowed } // Simple policy check - in production this would be more sophisticated if(decision.confidence < g_policy_min_conf) { if(ShouldLog(LOG_DEBUG)) LOG(StringFormat("Policy gating blocked: confidence %.2f < min %.2f", decision.confidence, g_policy_min_conf)); return false; } // Apply policy scaling (placeholder implementation) // In production, this would look up specific strategy/symbol/timeframe policies decision.final_sl = decision.original_sl * 1.0; // No scaling for now decision.final_tp = decision.original_tp * 1.0; // No scaling for now decision.final_volume = decision.original_volume * 1.0; // No scaling for now return true; } // Export enhanced features for ML training void ExportEnhancedFeatures(const CSignalDecision &decision, const string strategy_name) { if(CheckPointer(g_features) == POINTER_INVALID) return; // Create comprehensive feature set string features[]; ArrayResize(features, 15); datetime now = TimeCurrent(); MqlDateTime _dt; TimeToStruct(now, _dt); int _hour = _dt.hour; features[0] = "strategy:" + strategy_name; features[1] = "symbol:" + decision.symbol; features[2] = "timeframe:" + IntegerToString(_Period); features[3] = "entry_price:" + DoubleToString(decision.final_price, 5); features[4] = "volume:" + DoubleToString(decision.final_volume, 2); features[5] = "order_type:" + IntegerToString(decision.order_type); features[6] = "confidence:" + DoubleToString(decision.confidence, 3); features[7] = "volatility:" + DoubleToString(GetVolatility(_Symbol, (ENUM_TIMEFRAMES)_Period), 4); features[8] = "correlation:" + DoubleToString(GetCorrelation(), 3); features[9] = "market_regime:" + GetMarketRegime(); features[10] = "session_hour:" + IntegerToString(_hour); features[11] = "spread_points:" + DoubleToString((SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / _Point, 1); features[12] = "equity:" + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2); features[13] = "balance:" + DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2); features[14] = "margin_level:" + DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL), 2); // Export to features system g_features.ExportFeatures(decision.symbol, strategy_name, now, features); } void ExportEnhancedFeaturesAdaptive(const CAdaptiveDecision &decision, const string strategy_name) { if(CheckPointer(g_features) == POINTER_INVALID) return; datetime ts = (decision.execution_time > 0) ? decision.execution_time : TimeCurrent(); string features[]; int feature_count = 0; AppendSnapshotFeature(features, feature_count, "timestamp", TimeToString(ts, TIME_DATE|TIME_SECONDS)); AppendSnapshotFeature(features, feature_count, "strategy", strategy_name); AppendSnapshotFeature(features, feature_count, "symbol", decision.symbol); AppendSnapshotFeature(features, feature_count, "signal_id", decision.signal_id); AppendSnapshotFeature(features, feature_count, "original_signal_id", (decision.is_adjusted && decision.original_signal_id != "") ? decision.original_signal_id : decision.signal_id); AppendSnapshotFeature(features, feature_count, "is_adjusted", decision.is_adjusted ? "1" : "0"); AppendSnapshotFeature(features, feature_count, "adjustment_attempts", IntegerToString(decision.adjustment_attempts)); AppendSnapshotFeature(features, feature_count, "executed", decision.executed ? "1" : "0"); AppendSnapshotFeature(features, feature_count, "confidence", DoubleToString(decision.confidence, 3)); AppendSnapshotFeature(features, feature_count, "market_regime", decision.market_regime); AppendSnapshotFeature(features, feature_count, "volatility", DoubleToString(decision.volatility, 6)); AppendSnapshotFeature(features, feature_count, "correlation", DoubleToString(decision.correlation_score, 6)); AppendSnapshotFeature(features, feature_count, "original_price", DoubleToString(decision.original_price, 5)); AppendSnapshotFeature(features, feature_count, "original_sl", DoubleToString(decision.original_sl, 5)); AppendSnapshotFeature(features, feature_count, "original_tp", DoubleToString(decision.original_tp, 5)); AppendSnapshotFeature(features, feature_count, "original_volume", DoubleToString(decision.original_volume, 2)); AppendSnapshotFeature(features, feature_count, "final_price", DoubleToString(decision.final_price, 5)); AppendSnapshotFeature(features, feature_count, "final_sl", DoubleToString(decision.final_sl, 5)); AppendSnapshotFeature(features, feature_count, "final_tp", DoubleToString(decision.final_tp, 5)); AppendSnapshotFeature(features, feature_count, "final_volume", DoubleToString(decision.final_volume, 2)); double price_delta_pct = 0.0; if(MathAbs(decision.original_price) > 1e-9) price_delta_pct = (decision.final_price - decision.original_price) / decision.original_price * 100.0; double volume_delta_pct = 0.0; if(MathAbs(decision.original_volume) > 1e-9) volume_delta_pct = (decision.final_volume - decision.original_volume) / decision.original_volume * 100.0; AppendSnapshotFeature(features, feature_count, "price_change_pct", DoubleToString(price_delta_pct, 3)); AppendSnapshotFeature(features, feature_count, "volume_change_pct", DoubleToString(volume_delta_pct, 3)); double sl_scale = (MathAbs(decision.original_sl) > 1e-9) ? decision.final_sl / decision.original_sl : 1.0; double tp_scale = (MathAbs(decision.original_tp) > 1e-9) ? decision.final_tp / decision.original_tp : 1.0; AppendSnapshotFeature(features, feature_count, "sl_scale", DoubleToString(sl_scale, 4)); AppendSnapshotFeature(features, feature_count, "tp_scale", DoubleToString(tp_scale, 4)); int gate_passed = CountPassedGates(decision); AppendSnapshotFeature(features, feature_count, "gate_passed_count", IntegerToString(gate_passed)); string first_block_reason = ""; for(int i = 0; i < 8; i++) { string gate_key = StringFormat("gate%d", i + 1); AppendSnapshotFeature(features, feature_count, gate_key + "_pass", decision.gate_results[i] ? "1" : "0"); if(decision.gate_reasons[i] != "") { AppendSnapshotFeature(features, feature_count, gate_key + "_reason", decision.gate_reasons[i]); if(!decision.gate_results[i] && first_block_reason == "") first_block_reason = decision.gate_reasons[i]; } } if(first_block_reason != "") AppendSnapshotFeature(features, feature_count, "first_block_reason", first_block_reason); if(decision.is_adjusted) { int attempts = MathMin(decision.adjustment_attempts, ArraySize(decision.attempts)); for(int j = 0; j < attempts; j++) { string prefix = StringFormat("attempt%d_", j + 1); AppendSnapshotFeature(features, feature_count, prefix + "passed", decision.attempts[j].passed ? "1" : "0"); AppendSnapshotFeature(features, feature_count, prefix + "price_tweak", DoubleToString(decision.attempts[j].price_tweak, 4)); AppendSnapshotFeature(features, feature_count, prefix + "sl_tweak", DoubleToString(decision.attempts[j].sl_tweak, 4)); AppendSnapshotFeature(features, feature_count, prefix + "tp_tweak", DoubleToString(decision.attempts[j].tp_tweak, 4)); AppendSnapshotFeature(features, feature_count, prefix + "volume_tweak", DoubleToString(decision.attempts[j].volume_tweak, 4)); if(decision.attempts[j].reason != "") AppendSnapshotFeature(features, feature_count, prefix + "reason", decision.attempts[j].reason); } } g_features.ExportFeatures(decision.symbol, strategy_name, ts, features); } //+------------------------------------------------------------------+ //| ENHANCED TIMER FUNCTION | //+------------------------------------------------------------------+ void OnTimer() { // Real positions updated automatically by MT5 // No need for UpdatePaperPositions() // ===================[ PERIODIC SYSTEM MAINTENANCE ]=================== static datetime last_maintenance = 0; datetime now = TimeCurrent(); // ===================[ 10s HOT-RELOAD (policy + config) ]=================== static datetime last_hot_reload = 0; if(HotReloadIntervalSec > 0 && (now - last_hot_reload) >= HotReloadIntervalSec) { // Policy: rate-based selection between HTTP and file if(UsePolicyGating) { int roll = MathRand() % 100; bool try_http = (roll < PolicyHttpPollPercent); if(try_http) { int new_len = LoadPolicyFromHttp(PolicyServerUrl); if(new_len >= 0 && new_len != g_policy_last_len) { g_policy_last_len = new_len; LOG("Policy reloaded via HTTP"); } } else { // Check file length first to avoid unnecessary reload logs int hpf = FileOpen(PolicyFilePath, FILE_READ|FILE_COMMON|FILE_TXT|FILE_ANSI); if(hpf != INVALID_HANDLE) { int flen = (int)FileSize(hpf); FileClose(hpf); if(flen != g_policy_last_len) { bool ok = Policy_Load(); if(ok) { g_policy_last_len = flen; LOG("Policy reloaded via file"); } } } } } // Config reload: size-based change detection int hcf = FileOpen(ConfigFilePath, FILE_READ|FILE_COMMON|FILE_TXT|FILE_ANSI); if(hcf != INVALID_HANDLE) { int clen = (int)FileSize(hcf); FileClose(hcf); if(clen != g_config_last_len) { CConfigManager *cfgm = CConfigManager::GetInstance(); if(CheckPointer(cfgm) != POINTER_INVALID) { cfgm.LoadFromFile(ConfigFilePath); g_config_last_len = clen; LOG("Config reloaded from file"); } } } last_hot_reload = now; } // ===================[ 10s DECISION + EXECUTION CADENCE ]=================== static datetime last_decision_run = 0; if((now - last_decision_run) >= 10) { RunDecisionPipelineOnce(); last_decision_run = now; } if(now - last_maintenance > 300) // Every 5 minutes { // Check for policy reload signals CheckPolicyReload(); // Check for insights reload signals CheckInsightsReload(); // Maintenance hooks for session/correlation managers can be added here if needed // Reload news events periodically if(UseNewsFilter) { LoadNewsEvents(); } last_maintenance = now; } // ===================[ LEARNING SYSTEM UPDATES ]=================== static datetime last_learning_update = 0; static CLiveAdaptation live_adapter; // Live performance-based adaptation if(now - last_learning_update > 1800) // Every 30 minutes { // Universal auto-detection: recalculate parameters based on current market conditions if(AutoDetectParameters) { g_adaptive_engine.CalculateDynamicParameters(_Symbol, (ENUM_TIMEFRAMES)_Period); // Get fresh profile after recalculation SymbolProfile updated_profile = g_adaptive_engine.GetCurrentProfile(); // Apply updated parameters if override is allowed if(AllowManualOverride) { StopLossPips = updated_profile.optimal_sl_mult; TakeProfitPips = updated_profile.optimal_tp_mult; VolSizerTargetRisk = updated_profile.risk_multiplier; if(Verbosity >= 2) { LOG(StringFormat("🔄 [AUTO-ADAPT] Parameters updated: SL=%.1f TP=%.1f Risk=%.2f%%", StopLossPips, TakeProfitPips, VolSizerTargetRisk)); } } // ML-based live adaptation (adjusts parameters based on recent performance) if(EnableMLAdaptation && TimeCurrent() - g_last_adaptation > AdaptationIntervalMinutes * 60) { // Get recent performance metrics (from knowledge base or simulated) double recent_pf = g_ml_engine.GetRecentProfitFactor(100); double recent_wr = g_ml_engine.GetRecentWinRate(100); double recent_dd = g_ml_engine.GetRecentMaxDrawdown(100); double recent_vol = g_ml_engine.GetRecentVolatility(50); // Update live adapter with performance data live_adapter.UpdateFromLiveTrading(recent_pf, recent_wr, recent_dd, recent_vol); // Calculate adaptive adjustment (0.7 - 1.3 range) double ml_adjustment = live_adapter.CalculateAdaptiveAdjustment(); // Apply ML adjustments to gating thresholds (AGGRESSIVE profile constraints) P5_MinPF = MathMax(1.9, MathMin(3.0, P5_MinPF * ml_adjustment)); P5_MinWR = MathMax(0.5, MathMin(1.0, P5_MinWR * ml_adjustment)); CBDailyLossLimitPct = MathMax(5.0, MathMin(10.0, CBDailyLossLimitPct * (2.0 - ml_adjustment))); if(Verbosity >= 1) { LOG(StringFormat("🧠 [ML-ADAPT] PF=%.2f WR=%.2f DD=%.2f%% Adj=%.3f | MinPF=%.2f MinWR=%.2f MaxDD=%.1f%%", recent_pf, recent_wr, recent_dd * 100, ml_adjustment, P5_MinPF, P5_MinWR, CBDailyLossLimitPct)); } g_last_adaptation = TimeCurrent(); } } // Periodic learning updates if(CheckPointer(g_gate_manager) != POINTER_INVALID) g_gate_manager.UpdateFromLearning(); if(UseStrategySelector && CheckPointer(g_selector) != POINTER_INVALID) g_selector.EnsureRecentLoaded(SelRecentDays); // Transfer successful signals to live EA (if learning bridge available) if(CheckPointer(g_learning_bridge) != POINTER_INVALID) g_learning_bridge.TransferSuccessfulSignals("C:\\DualEA\\LiveData"); last_learning_update = now; } // ===================[ HEARTBEAT AND HEALTH CHECKS ]=================== static datetime last_heartbeat = 0; if(HeartbeatEnabled && (now - last_heartbeat > HeartbeatMinutes * 60)) { // System health check using real MT5 positions int active_positions = PositionsTotal(); if(HeartbeatVerbose) { LOG("=== PaperEA v2 Heartbeat ==="); LOG(StringFormat("Time: %s | Active Positions: %d | Equity: %.2f", TimeToString(now), active_positions, AccountInfoDouble(ACCOUNT_EQUITY))); LOG(StringFormat("Policy Loaded: %s | NoConstraints: %s | UseSelector: %s", g_policy_loaded ? "YES" : "NO", NoConstraintsMode ? "ON" : "OFF", UseStrategySelector ? "ON" : "OFF")); } last_heartbeat = now; } // ===================[ P0-P5 PERIODIC MAINTENANCE ]=================== // Flush batch telemetry, check shadow trades, feature drift maintenance OnTimerP0P5(); } //+------------------------------------------------------------------+ //| ONTRADE TRANSACTION HANDLER - ENHANCED POSITION TRACKING | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { // Filter to current chart symbol and handle order cancellations string trans_symbol = trans.symbol; if(trans_symbol != _Symbol) return; int t = (int)trans.type; if(t == TRADE_TRANSACTION_ORDER_DELETE) { // Clean pending order mapping if order is cancelled/expired ulong ord = trans.order; if(ord > 0) { for(int i = 0; i < ArraySize(g_pending_orders); ++i) if(g_pending_orders[i] == ord) { int last = ArraySize(g_pending_orders) - 1; g_pending_orders[i] = g_pending_orders[last]; g_pending_orders_strat[i] = g_pending_orders_strat[last]; ArrayResize(g_pending_orders, last); ArrayResize(g_pending_orders_strat, last); break; } } return; } // We only care about deal executions beyond this point if(t != TRADE_TRANSACTION_DEAL_ADD) return; ulong deal = trans.deal; if(deal == 0) return; int entry_flag = (int)HistoryDealGetInteger(deal, DEAL_ENTRY); ulong pid = (ulong)HistoryDealGetInteger(deal, DEAL_POSITION_ID); string deal_symbol = HistoryDealGetString(deal, DEAL_SYMBOL); long dmagic = (long)HistoryDealGetInteger(deal, DEAL_MAGIC); // Filter to current chart symbol and our magic if available if(deal_symbol != _Symbol) return; if(MagicNumber > 0 && dmagic != MagicNumber) return; if(entry_flag == DEAL_ENTRY_IN) { // Track newly opened position if not already tracked if(pid > 0) { if(FindTrackedIndexByPid(pid) >= 0) return; // already tracked string strat_name = "unknown"; // Try to attribute strategy from pending mapping for(int m = 0; m < ArraySize(g_pending_deals); ++m) { if(g_pending_deals[m] == deal) { strat_name = g_pending_deals_strat[m]; int last = ArraySize(g_pending_deals) - 1; g_pending_deals[m] = g_pending_deals[last]; g_pending_deals_strat[m] = g_pending_deals_strat[last]; ArrayResize(g_pending_deals, last); ArrayResize(g_pending_deals_strat, last); break; } } // Add to tracking arrays int k = ArraySize(g_pos_ids); ArrayResize(g_pos_ids, k + 1); ArrayResize(g_pos_strats, k + 1); ArrayResize(g_pos_entry_price, k + 1); ArrayResize(g_pos_initial_risk, k + 1); ArrayResize(g_pos_start_time, k + 1); ArrayResize(g_pos_type, k + 1); ArrayResize(g_pos_max_price, k + 1); ArrayResize(g_pos_min_price, k + 1); g_pos_ids[k] = pid; g_pos_strats[k] = strat_name; double entry_p = HistoryDealGetDouble(deal, DEAL_PRICE); if(entry_p <= 0 && PositionSelectByTicket(pid)) entry_p = PositionGetDouble(POSITION_PRICE_OPEN); g_pos_entry_price[k] = entry_p; g_pos_max_price[k] = entry_p; g_pos_min_price[k] = entry_p; int ptype = POSITION_TYPE_BUY; if(PositionSelectByTicket(pid)) ptype = (int)PositionGetInteger(POSITION_TYPE); g_pos_type[k] = ptype; double init_risk = 0.0; if(PositionSelectByTicket(pid)) { double slc = PositionGetDouble(POSITION_SL); double eop = PositionGetDouble(POSITION_PRICE_OPEN); if(slc > 0 && eop > 0) init_risk = MathAbs((ptype == POSITION_TYPE_BUY ? eop - slc : slc - eop)); } g_pos_initial_risk[k] = init_risk; // Use deal time for accurate hold time g_pos_start_time[k] = (datetime)HistoryDealGetInteger(deal, DEAL_TIME); if(ShouldLog(LOG_INFO)) LOG(StringFormat("Tracked pos via OnTradeTransaction: ticket=%I64u strat=%s entry=%.5f initR=%.5f", pid, g_pos_strats[k], entry_p, init_risk)); } return; } else if(entry_flag == DEAL_ENTRY_OUT) { if(pid == 0) return; int idx = FindTrackedIndexByPid(pid); if(idx < 0) { // Not tracked; nothing to do return; } // If still open, treat as partial close; wait for final close if(PositionSelectByTicket(pid)) return; // Calculate R-multiple for IncrementalInsightEngine if(CheckPointer(g_insight_engine) != POINTER_INVALID && idx >= 0) { double entry_price = g_pos_entry_price[idx]; double exit_price = HistoryDealGetDouble(deal, DEAL_PRICE); double sl_price = g_pos_entry_price[idx] - g_pos_initial_risk[idx]; // Approximate SL double profit = HistoryDealGetDouble(deal, DEAL_PROFIT); if(g_pos_initial_risk[idx] > 0) { double r_multiple = profit / g_pos_initial_risk[idx]; // Normalize for position type if(g_pos_type[idx] == POSITION_TYPE_SELL) r_multiple = -r_multiple; string strat = g_pos_strats[idx]; int tf = (int)_Period; // Use chart timeframe g_insight_engine.RecordTradeOutcome(strat, _Symbol, tf, r_multiple); if(ShouldLog(LOG_INFO)) LOG(StringFormat("[InsightEngine] Recorded outcome: %s R=%.2f", strat, r_multiple)); } } // Fully closed -> log and remove HandlePositionClosed(idx, deal); return; } } //+------------------------------------------------------------------+ //| GENETIC OPTIMIZATION FITNESS FUNCTION | //| Implements robust metric from chat-8Stage guide | //| Penalizes overfitting, rewards consistency | //+------------------------------------------------------------------+ double OnTester() { // Use robust metric (not just profit) double profit_factor = TesterStatistics(STAT_PROFIT_FACTOR); double drawdown = TesterStatistics(STAT_BALANCE_DDREL_PERCENT); double sharpe = TesterStatistics(STAT_SHARPE_RATIO); double win_rate = TesterStatistics(STAT_TRADES_WIN) / (TesterStatistics(STAT_TRADES_WIN) + TesterStatistics(STAT_TRADES_LOSS)); // Multi-objective fitness: ProfitFactor * (1 - DD) * Sharpe * WR // Penalize high drawdown heavily if(drawdown > 20.0) return 0.0; // Normalize components double pf_score = MathMin(profit_factor, 3.0) / 3.0; // Cap at 3.0 double dd_score = 1.0 - (drawdown / 100.0); double sharpe_score = MathMax(0, sharpe) / 3.0; // Normalize Sharpe double wr_score = win_rate; // Weighted combination (from guide: prioritize PF and DD) double fitness = (pf_score * 0.4 + dd_score * 0.3 + sharpe_score * 0.2 + wr_score * 0.1); // Additional penalties if(TesterStatistics(STAT_TRADES_TOTAL) < 20) fitness *= 0.5; // Insufficient trades if(profit_factor < 1.0) fitness *= 0.1; // Losing system return fitness; } //+------------------------------------------------------------------+ //| GATE PERFORMANCE MONITORING | //| Tracks per-gate latency and execution metrics | //+------------------------------------------------------------------+ struct SGatePerformanceMetrics { ulong total_calls; ulong total_latency_us; ulong max_latency_us; ulong min_latency_us; ulong pass_count; ulong fail_count; void Init() { total_calls = 0; total_latency_us = 0; max_latency_us = 0; min_latency_us = ULONG_MAX; pass_count = 0; fail_count = 0; } void Record(ulong latency_us, bool passed) { total_calls++; total_latency_us += latency_us; max_latency_us = MathMax(max_latency_us, latency_us); min_latency_us = MathMin(min_latency_us, latency_us); if(passed) pass_count++; else fail_count++; } double GetAvgLatency() { return (total_calls > 0) ? (double)total_latency_us / total_calls : 0.0; } double GetPassRate() { return (total_calls > 0) ? (double)pass_count / total_calls : 0.0; } }; SGatePerformanceMetrics g_gate_metrics[8]; // One per gate //+------------------------------------------------------------------+ //| Record gate performance metrics | //+------------------------------------------------------------------+ void RecordGateMetrics(int gate_num, ulong latency_us, bool passed) { if(gate_num >= 1 && gate_num <= 8) { g_gate_metrics[gate_num - 1].Record(latency_us, passed); } } //+------------------------------------------------------------------+ //| Print gate performance summary | //+------------------------------------------------------------------+ void PrintGatePerformanceSummary() { Print("=== GATE PERFORMANCE SUMMARY ==="); for(int i = 0; i < 8; i++) { if(g_gate_metrics[i].total_calls > 0) { string gate_name = StringFormat("Gate %d", i + 1); switch(i) { case 0: gate_name = "G1 SignalRinse"; break; case 1: gate_name = "G2 MarketSoap"; break; case 2: gate_name = "G3 StrategyScrub"; break; case 3: gate_name = "G4 RiskWash"; break; case 4: gate_name = "G5 PerformanceWax"; break; case 5: gate_name = "G6 ML Polish"; break; case 6: gate_name = "G7 LiveClean"; break; case 7: gate_name = "G8 FinalVerify"; break; } Print(StringFormat("%s: calls=%I64u avg=%.1fus max=%I64u min=%I64u pass=%.1f%%", gate_name, g_gate_metrics[i].total_calls, g_gate_metrics[i].GetAvgLatency(), g_gate_metrics[i].max_latency_us, g_gate_metrics[i].min_latency_us, g_gate_metrics[i].GetPassRate() * 100.0)); } } Print("================================"); } //+------------------------------------------------------------------+ //| Helper: Check if trade is allowed (MQL5 compatibility) | //+------------------------------------------------------------------+ bool IsTradeAllowed() { // Check terminal trade permission and expert advisor enabled return TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) != 0 && MQLInfoInteger(MQL_TRADE_ALLOWED) != 0; } //+------------------------------------------------------------------+ //| WEEKEND/MARKET STATUS CHECK | //| Robustness: Handle no-tick periods per guide | //+------------------------------------------------------------------+ bool IsTradeAllowedExtended() { // Basic trade allowed check if(!IsTradeAllowed()) return false; // Check if market is open (handles weekends) datetime now = TimeCurrent(); MqlDateTime dt; TimeToStruct(now, dt); // Weekend check (Friday after 20:00 or Sunday before 22:00) if(dt.day_of_week == 5 && dt.hour >= 20) return false; // Friday evening if(dt.day_of_week == 0 && dt.hour < 22) return false; // Sunday early if(dt.day_of_week == 6) return false; // Saturday // Check spread (Gate 7 logic integrated) long spread_raw = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double spread = (double)spread_raw; double avg_spread = GetAverageSpread(20); if(spread > avg_spread * 3.0) // Spread > 3x average { static datetime last_spread_warn = 0; if(now - last_spread_warn > 300) // Warn every 5 minutes { Print(StringFormat("WARNING: Spread too high (%d vs avg %.1f) - skipping tick", (int)spread, avg_spread)); last_spread_warn = now; } return false; } return true; } //+------------------------------------------------------------------+ //| Calculate average spread over N periods | //+------------------------------------------------------------------+ double GetAverageSpread(int periods) { double sum = 0; for(int i = 0; i < periods; i++) { sum += iSpread(_Symbol, _Period, i); } return (periods > 0) ? sum / periods : 0; } //+------------------------------------------------------------------+ //| WALK-FORWARD ANALYSIS SUPPORT | //| Enable regime-based parameter loading | //+------------------------------------------------------------------+ input group "=== WALK-FORWARD ANALYSIS ===" input bool WFA_Enabled = false; // Enable walk-forward mode input string WFA_ParameterFile = "wfa_params.json"; // Parameter file path input int WFA_RetrainIntervalDays = 7; // Days between parameter reload datetime g_last_wfa_reload = 0; //+------------------------------------------------------------------+ //| Check and reload WFA parameters | //+------------------------------------------------------------------+ void CheckWFAReload() { if(!WFA_Enabled) return; datetime now = TimeCurrent(); if((now - g_last_wfa_reload) < WFA_RetrainIntervalDays * 86400) return; string filepath = StringFormat("DualEA\\%s", WFA_ParameterFile); if(FileIsExist(filepath, FILE_COMMON)) { Print(StringFormat("[WFA] Reloading parameters from %s", WFA_ParameterFile)); // Load parameters from JSON (simplified - in production use proper JSON parser) // This would update gate thresholds based on out-of-sample performance g_last_wfa_reload = now; } } //+------------------------------------------------------------------+ //| Enhanced ExecuteGateCascade with metrics | //| Fail-fast with per-gate timing from guide | //+------------------------------------------------------------------+ bool ExecuteGateCascadeEnhanced(TradingSignal &signal, bool &all_passed, string &block_reason) { ulong cascade_start = GetMicrosecondCount(); all_passed = true; // Gate 1: Signal Rinse ulong g1_start = GetMicrosecondCount(); bool g1_pass = g_gate_manager.ProcessGate1(signal); RecordGateMetrics(1, GetMicrosecondCount() - g1_start, g1_pass); if(!g1_pass) { all_passed = false; block_reason = "G1 SignalRinse rejected"; return false; } // Gate 2: Market Soap ulong g2_start = GetMicrosecondCount(); bool g2_pass = g_gate_manager.ProcessGate2(signal); RecordGateMetrics(2, GetMicrosecondCount() - g2_start, g2_pass); if(!g2_pass) { all_passed = false; block_reason = "G2 MarketSoap rejected"; return false; } // Gate 3: Strategy Scrub ulong g3_start = GetMicrosecondCount(); bool g3_pass = g_gate_manager.ProcessGate3(signal); RecordGateMetrics(3, GetMicrosecondCount() - g3_start, g3_pass); if(!g3_pass) { all_passed = false; block_reason = "G3 StrategyScrub rejected"; return false; } // Gate 4: Risk Wash ulong g4_start = GetMicrosecondCount(); bool g4_pass = g_gate_manager.ProcessGate4(signal); RecordGateMetrics(4, GetMicrosecondCount() - g4_start, g4_pass); if(!g4_pass) { all_passed = false; block_reason = "G4 RiskWash rejected"; return false; } // Gate 5: Performance Wax ulong g5_start = GetMicrosecondCount(); bool g5_pass = g_gate_manager.ProcessGate5(signal); RecordGateMetrics(5, GetMicrosecondCount() - g5_start, g5_pass); if(!g5_pass) { all_passed = false; block_reason = "G5 PerformanceWax rejected"; return false; } // Gate 6: ML Polish ulong g6_start = GetMicrosecondCount(); bool g6_pass = g_gate_manager.ProcessGate6(signal); RecordGateMetrics(6, GetMicrosecondCount() - g6_start, g6_pass); if(!g6_pass) { all_passed = false; block_reason = "G6 ML Polish rejected"; return false; } // Gate 7: Live Clean ulong g7_start = GetMicrosecondCount(); bool g7_pass = g_gate_manager.ProcessGate7(signal); RecordGateMetrics(7, GetMicrosecondCount() - g7_start, g7_pass); if(!g7_pass) { all_passed = false; block_reason = "G7 LiveClean rejected"; return false; } // Gate 8: Final Verify ulong g8_start = GetMicrosecondCount(); bool g8_pass = g_gate_manager.ProcessGate8(signal); RecordGateMetrics(8, GetMicrosecondCount() - g8_start, g8_pass); if(!g8_pass) { all_passed = false; block_reason = "G8 FinalVerify rejected"; return false; } // Performance warning if cascade > 5ms double elapsed_ms = (GetMicrosecondCount() - cascade_start) / 1000.0; if(elapsed_ms > 5.0) { Print(StringFormat("WARNING: Gate cascade took %.2fms (>5ms threshold)", elapsed_ms)); } return true; }