412 lines
16 KiB
MQL5
412 lines
16 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| PaperEA_v2_P0P5_Integration.mqh - Integration Layer |
|
||
|
|
//| Ties all P0-P5 components into PaperEA_v2.mq5 with user specs |
|
||
|
|
//| Auto-drift adjustment, CPU budgeting, risk gate protection |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#ifndef PAPEREA_V2_P0P5_INTEGRATION_MQH
|
||
|
|
#define PAPEREA_V2_P0P5_INTEGRATION_MQH
|
||
|
|
|
||
|
|
#include "CDualEAController.mqh"
|
||
|
|
#include "CSymbolCoordinator.mqh"
|
||
|
|
#include "CGateFeatureCache.mqh"
|
||
|
|
#include "CShadowLogger.mqh"
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Input Parameters - P0-P5 Features (Enabled by Default) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
// === P0-P5 Hardening & Intelligence ===
|
||
|
|
input bool InpEnableAutoDriftAdjustment = true; // P2-1: Auto-adjust gate thresholds
|
||
|
|
input bool InpEnableCPUBudgeting = true; // P3-1: 10ms fail-fast budgeting
|
||
|
|
input bool InpEnableNuclearRiskEngine = true; // P4-3: VaR/Kelly/Correlation risk
|
||
|
|
input bool InpEnableMultiTimeframeGate = true; // P2-4: MTF confirmation
|
||
|
|
input bool InpEnableCorrelationPruning = true; // P2-5: Strategy diversification
|
||
|
|
input bool InpEnableShadowBridge = true; // P2-3: Parallel demo execution
|
||
|
|
input bool InpEnableFeatureDriftDetection = true; // P4-4: Distribution shift alerts
|
||
|
|
input bool InpEnableTimerScanner = true; // P3-5: Low-tick signal capture
|
||
|
|
input bool InpEnableSQLiteParallel = true; // P1-1: ACID parallel logging
|
||
|
|
input bool InpEnablePositionManager = true; // P1-6: Kelly sizing + vol exits
|
||
|
|
input bool InpEnableSymbolCoordination = true; // P4-5: Redis cross-chart coordination
|
||
|
|
|
||
|
|
// === Risk Gate Protection (Never Skip) ===
|
||
|
|
input bool InpProtectG1_SignalRinse = true; // G1: Always process
|
||
|
|
input bool InpProtectG4_Frequency = true; // G4: Always process
|
||
|
|
input bool InpProtectG7_AdaptiveML = true; // G7: Always process
|
||
|
|
input bool InpProtectG8_Execution = true; // G8: Always process
|
||
|
|
|
||
|
|
// === Thresholds & Limits ===
|
||
|
|
input double InpDriftZScoreThreshold = 2.5; // Drift detection sensitivity
|
||
|
|
input double InpCPUBudgetMs = 10.0; // Per-tick CPU limit
|
||
|
|
input double InpMaxPortfolioRiskPct = 15.0; // Portfolio risk cap
|
||
|
|
input double InpMaxCorrelation = 0.70; // Strategy correlation limit
|
||
|
|
input int InpMaxPositionsPerSymbol = 5; // Symbol position limit
|
||
|
|
input int InpSQLiteMigrationDays = 30; // Parallel run duration
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Global Controller Instance |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CDualEAController* g_dual_ea_controller = NULL;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Initialize P0-P5 Systems |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool InitializeP0P5Systems()
|
||
|
|
{
|
||
|
|
Print("=== [PaperEA_v2] Initializing P0-P5 Hardening Systems ===");
|
||
|
|
|
||
|
|
// Build configuration from inputs
|
||
|
|
SDualEAConfig config;
|
||
|
|
config.SetDefaults();
|
||
|
|
|
||
|
|
// Map P0-P5 inputs into available SDualEAConfig fields
|
||
|
|
config.shadow_logging_enabled = InpEnableShadowBridge;
|
||
|
|
config.shadow_mode_only = ShadowMode;
|
||
|
|
config.sqlite_enabled = InpEnableSQLiteParallel;
|
||
|
|
config.sqlite_db_name = "dualea_kb.db";
|
||
|
|
config.circuit_breaker_enabled = InpEnableNuclearRiskEngine;
|
||
|
|
config.correlation_sizing_enabled = InpEnablePositionManager;
|
||
|
|
config.volatility_exits_enabled = InpEnablePositionManager;
|
||
|
|
config.max_risk_per_trade_pct = (InpMaxPortfolioRiskPct > 0.0 ? InpMaxPortfolioRiskPct : config.max_risk_per_trade_pct);
|
||
|
|
config.max_positions = (InpMaxPositionsPerSymbol > 0 ? InpMaxPositionsPerSymbol : config.max_positions);
|
||
|
|
|
||
|
|
// Initialize controller singleton
|
||
|
|
g_dual_ea_controller = CDualEAController::Instance();
|
||
|
|
|
||
|
|
if(!g_dual_ea_controller.Initialize(config))
|
||
|
|
{
|
||
|
|
Print("[PaperEA_v2] CRITICAL: Failed to initialize DualEA Controller");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize symbol coordinator if enabled
|
||
|
|
if(InpEnableSymbolCoordination)
|
||
|
|
{
|
||
|
|
if(!InitializeSymbolCoordinator(InpMaxPortfolioRiskPct))
|
||
|
|
{
|
||
|
|
Print("[PaperEA_v2] WARNING: Symbol coordinator init failed, running solo mode");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Print("[PaperEA_v2] ✓ Symbol coordinator active (Redis cross-chart)");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize SQLite if parallel mode enabled
|
||
|
|
if(InpEnableSQLiteParallel)
|
||
|
|
{
|
||
|
|
CSQLiteKnowledgeBase* sqlite_kb = g_dual_ea_controller.GetSQLiteKB();
|
||
|
|
if(sqlite_kb != NULL)
|
||
|
|
{
|
||
|
|
Print("[PaperEA_v2] ✓ SQLite parallel logging active (30-day migration)");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize indicator cache
|
||
|
|
CIndicatorCache* cache = g_dual_ea_controller.GetIndicatorCache();
|
||
|
|
if(cache != NULL)
|
||
|
|
{
|
||
|
|
// Pre-warm common indicators (disabled - PreWarmIndicator not available)
|
||
|
|
// cache.PreWarmIndicator(_Symbol, PERIOD_CURRENT, INDI_MA, 20);
|
||
|
|
// cache.PreWarmIndicator(_Symbol, PERIOD_CURRENT, INDI_RSI, 14);
|
||
|
|
// cache.PreWarmIndicator(_Symbol, PERIOD_CURRENT, INDI_ATR, 14);
|
||
|
|
// cache.PreWarmIndicator(_Symbol, PERIOD_CURRENT, INDI_MACD, 12);
|
||
|
|
Print("[PaperEA_v2] ✓ Indicator cache ready (pre-warming disabled)");
|
||
|
|
}
|
||
|
|
|
||
|
|
Print("=== [PaperEA_v2] P0-P5 Systems Ready ===");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Shutdown P0-P5 Systems |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ShutdownP0P5Systems()
|
||
|
|
{
|
||
|
|
Print("=== [PaperEA_v2] Shutting Down P0-P5 Systems ===");
|
||
|
|
|
||
|
|
// Print final reports
|
||
|
|
if(g_dual_ea_controller != NULL)
|
||
|
|
{
|
||
|
|
Print(g_dual_ea_controller.GetStatusReport());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Shutdown coordinator
|
||
|
|
ShutdownSymbolCoordinator();
|
||
|
|
|
||
|
|
// Destroy controller (singleton)
|
||
|
|
CDualEAController::Destroy();
|
||
|
|
g_dual_ea_controller = NULL;
|
||
|
|
|
||
|
|
Print("=== [PaperEA_v2] P0-P5 Systems Shutdown Complete ===");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Overload that requires explicit intent; use this from OnDeinit only.
|
||
|
|
void ShutdownP0P5Systems(const bool from_deinit)
|
||
|
|
{
|
||
|
|
if(!from_deinit)
|
||
|
|
{
|
||
|
|
Print("[PaperEA_v2] WARNING: ShutdownP0P5Systems(from_deinit=false) ignored to protect SQLite lifecycle");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
ShutdownP0P5Systems();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| PRE-GATE VALIDATOR: Concept Drift + MTF Check |
|
||
|
|
//| Called before each gate in 8-stage cascade |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool PreGateValidate(const string &gate_name, double &threshold, const double &features[])
|
||
|
|
{
|
||
|
|
if(g_dual_ea_controller == NULL) return true;
|
||
|
|
|
||
|
|
// P2-1: Concept drift auto-adjustment + P2-4: MTF confirmation
|
||
|
|
return g_dual_ea_controller.PreGateValidation(gate_name, features, threshold);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CPU BUDGET CHECK: Risk Gates Protected |
|
||
|
|
//| Returns true if gate should process, false if skipped |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CheckGateBudget(const string &gate_name, bool &was_skipped)
|
||
|
|
{
|
||
|
|
was_skipped = false;
|
||
|
|
|
||
|
|
if(g_dual_ea_controller == NULL) return true;
|
||
|
|
|
||
|
|
// P3-1: CPU budgeting with risk gate protection
|
||
|
|
// G1, G4, G7, G8 never skipped per user spec
|
||
|
|
return g_dual_ea_controller.ShouldProcessGate(gate_name, was_skipped);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| POST-GATE RECORD: Outcome tracking for drift detection |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void PostGateRecord(const string &gate_name, bool passed, double confidence)
|
||
|
|
{
|
||
|
|
if(g_dual_ea_controller == NULL) return;
|
||
|
|
|
||
|
|
g_dual_ea_controller.PostGateRecord(gate_name, passed, confidence);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| STRATEGY SELECTION: Correlation-pruned diversified subset |
|
||
|
|
//| Called before executing any strategy signal |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool SelectStrategy(const string &strategy_name, const double &signals[])
|
||
|
|
{
|
||
|
|
if(g_dual_ea_controller == NULL) return true;
|
||
|
|
|
||
|
|
// P2-5: Correlation-aware pruning during selection
|
||
|
|
return g_dual_ea_controller.SelectStrategyForExecution(strategy_name, signals);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CIRCUIT BREAKER: Nuclear Risk Engine (Interface Compatible) |
|
||
|
|
//| Replaces original CheckCircuitBreakers() in PaperEA_v2.mq5 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CheckCircuitBreakers(double daily_pnl_pct, double drawdown_pct,
|
||
|
|
int consecutive_losses, string &reason)
|
||
|
|
{
|
||
|
|
// P4-3: Nuclear risk engine with legacy fallback
|
||
|
|
if(g_dual_ea_controller != NULL)
|
||
|
|
{
|
||
|
|
return g_dual_ea_controller.CheckCircuitBreakers(
|
||
|
|
daily_pnl_pct, drawdown_pct, consecutive_losses, reason);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fallback if controller not available
|
||
|
|
if(daily_pnl_pct < -5.0)
|
||
|
|
{
|
||
|
|
reason = "Daily loss > 5%";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(drawdown_pct > 15.0)
|
||
|
|
{
|
||
|
|
reason = "Drawdown > 15%";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(consecutive_losses >= 3)
|
||
|
|
{
|
||
|
|
reason = "3+ consecutive losses";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| POSITION SIZE: Kelly-adjusted from nuclear risk engine |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double GetPositionSizeAdjusted(double base_size)
|
||
|
|
{
|
||
|
|
if(g_dual_ea_controller == NULL) return base_size;
|
||
|
|
|
||
|
|
// P4-3: Kelly criterion sizing
|
||
|
|
return g_dual_ea_controller.GetRecommendedPositionSize(base_size);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| PORTFOLIO COORDINATION: Check if trade allowed across symbols |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool ShouldTradePortfolioCheck(const string &direction, double risk_pct, string &reason)
|
||
|
|
{
|
||
|
|
if(!InpEnableSymbolCoordination || g_symbol_coordinator == NULL)
|
||
|
|
return true;
|
||
|
|
|
||
|
|
// Convert string direction to ENUM_ORDER_TYPE
|
||
|
|
ENUM_ORDER_TYPE order_type = (direction == "SELL") ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
|
||
|
|
return g_symbol_coordinator.ShouldTrade(order_type, risk_pct, reason);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| COORDINATION: Record trade execution |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void RecordTradeCoordination(const string &direction, double size, double risk_pct)
|
||
|
|
{
|
||
|
|
if(g_symbol_coordinator != NULL)
|
||
|
|
{
|
||
|
|
ENUM_ORDER_TYPE order_type = (direction == "SELL") ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
|
||
|
|
g_symbol_coordinator.RecordTrade(order_type, size, risk_pct);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| COORDINATION: Record position close |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void RecordCloseCoordination(double pnl, double risk_released)
|
||
|
|
{
|
||
|
|
if(g_symbol_coordinator != NULL)
|
||
|
|
{
|
||
|
|
g_symbol_coordinator.RecordClose(pnl, risk_released);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| ON TICK: P0-P5 processing cycle |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTickP0P5()
|
||
|
|
{
|
||
|
|
// P0-P5 OnTick processing
|
||
|
|
if(g_dual_ea_controller != NULL)
|
||
|
|
{
|
||
|
|
// Controller handles its own tick processing
|
||
|
|
}
|
||
|
|
|
||
|
|
if(InpEnableSymbolCoordination && g_symbol_coordinator != NULL)
|
||
|
|
{
|
||
|
|
// No per-tick maintenance method is defined on CSymbolCoordinator.
|
||
|
|
// Coordination is handled via RecordTrade/RecordClose.
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| ON TIMER: P0-P5 periodic tasks |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTimerP0P5()
|
||
|
|
{
|
||
|
|
// P0-P5 OnTimer processing
|
||
|
|
if(g_dual_ea_controller != NULL)
|
||
|
|
{
|
||
|
|
// Controller handles its own timer processing
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| BATCH LOGGING: Queue telemetry event |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void LogEventP0P5(const string &event, const string &context, const string &details)
|
||
|
|
{
|
||
|
|
// Telemetry logging through controller
|
||
|
|
if(g_dual_ea_controller != NULL)
|
||
|
|
{
|
||
|
|
// Log via Print for now - integrate with telemetry system
|
||
|
|
PrintFormat("[P0P5-EVENT] %s | %s | %s", event, context, details);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| FEATURE DRIFT: Record sample for distribution tracking |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void RecordFeatureSample(const string &feature_name, double value)
|
||
|
|
{
|
||
|
|
// Feature drift recording through controller
|
||
|
|
if(g_dual_ea_controller == NULL) return;
|
||
|
|
|
||
|
|
// Log feature sample for drift detection
|
||
|
|
if(InpEnableFeatureDriftDetection)
|
||
|
|
{
|
||
|
|
PrintFormat("[DRIFT-SAMPLE] %s = %.4f", feature_name, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| INDICATOR CACHE: Get cached value |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double GetCachedIndicator(int indicator_type, int period, int shift = 0)
|
||
|
|
{
|
||
|
|
// Indicator cache - returns 0.0 if controller not available
|
||
|
|
if(g_dual_ea_controller == NULL) return 0.0;
|
||
|
|
|
||
|
|
// For now, calculate indicator directly via handles + CopyBuffer.
|
||
|
|
// In future: integrate with CIndicatorCache for caching.
|
||
|
|
int handle = INVALID_HANDLE;
|
||
|
|
int buffer_index = 0;
|
||
|
|
|
||
|
|
switch(indicator_type)
|
||
|
|
{
|
||
|
|
case 1: // MA
|
||
|
|
handle = iMA(_Symbol, (ENUM_TIMEFRAMES)period, 20, 0, MODE_SMA, PRICE_CLOSE);
|
||
|
|
buffer_index = 0;
|
||
|
|
break;
|
||
|
|
case 2: // RSI
|
||
|
|
handle = iRSI(_Symbol, (ENUM_TIMEFRAMES)period, 14, PRICE_CLOSE);
|
||
|
|
buffer_index = 0;
|
||
|
|
break;
|
||
|
|
case 3: // ATR
|
||
|
|
handle = iATR(_Symbol, (ENUM_TIMEFRAMES)period, 14);
|
||
|
|
buffer_index = 0;
|
||
|
|
break;
|
||
|
|
case 4: // MACD main
|
||
|
|
handle = iMACD(_Symbol, (ENUM_TIMEFRAMES)period, 12, 26, 9, PRICE_CLOSE);
|
||
|
|
buffer_index = 0;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(handle == INVALID_HANDLE)
|
||
|
|
return 0.0;
|
||
|
|
|
||
|
|
double buf[1];
|
||
|
|
if(CopyBuffer(handle, buffer_index, shift, 1, buf) != 1)
|
||
|
|
{
|
||
|
|
IndicatorRelease(handle);
|
||
|
|
return 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
IndicatorRelease(handle);
|
||
|
|
return buf[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| STATUS: Full system health report |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
string GetP0P5StatusReport()
|
||
|
|
{
|
||
|
|
string report = "";
|
||
|
|
|
||
|
|
if(g_dual_ea_controller != NULL)
|
||
|
|
{
|
||
|
|
report += g_dual_ea_controller.GetStatusReport() + "\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
if(InpEnableSymbolCoordination && g_symbol_coordinator != NULL)
|
||
|
|
{
|
||
|
|
// report += g_symbol_coordinator.GetStatusReport() + "\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
return report;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // PAPEREA_V2_P0P5_INTEGRATION_MQH
|