mql5/Experts/Advisors/DualEA/PaperEA/PaperEA_v2.mq5
Princeec13 143c06ef3e
2026-03-06 17:12:54 -05:00

4535 lines
174 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Arrays/ArrayObj.mqh>
#include <Files/File.mqh>
// 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<ArraySize(g_gate_log_keys);++i) if(g_gate_log_keys[i]==key){ idx=i; break; }
datetime now = TimeCurrent();
if(idx<0)
{
int n=ArraySize(g_gate_log_keys);
ArrayResize(g_gate_log_keys,n+1); ArrayResize(g_gate_log_last_ts,n+1);
g_gate_log_keys[n]=key; g_gate_log_last_ts[n]=0;
return true;
}
datetime last = g_gate_log_last_ts[idx];
if(last==0) return true;
return ((now - last) >= GateLogCooldownSec);
}
void GateMarkPrinted(const string tag, const string phase)
{
if(!GateLogThrottleEnabled) return;
string key = tag + "|" + phase;
int idx=-1;
for(int i=0;i<ArraySize(g_gate_log_keys);++i) if(g_gate_log_keys[i]==key){ idx=i; break; }
if(idx<0)
{
int n=ArraySize(g_gate_log_keys);
ArrayResize(g_gate_log_keys,n+1); ArrayResize(g_gate_log_last_ts,n+1);
g_gate_log_keys[n]=key; g_gate_log_last_ts[n]=TimeCurrent();
return;
}
g_gate_log_last_ts[idx]=TimeCurrent();
}
// Undefine macros from GatingPipeline.mqh before defining actual functions
#ifdef NowMs
#undef NowMs
#endif
#ifdef LogGate
#undef LogGate
#endif
ulong NowMs(){ return (ulong)GetTickCount(); }
void LogGate(const string tag, const bool allowed, const string phase, const ulong t0)
{
int latency = (int)(NowMs() - t0);
if((Verbosity>=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;
}