2025-09-20 14:14:30 -04:00
//+------------------------------------------------------------------+
2025-09-24 15:10:43 -04:00
//| LiveEA.mq5 |
//| DualEA Live Trading Expert - Reconstructed |
2025-09-20 14:14:30 -04:00
//+------------------------------------------------------------------+
2025-09-24 15:10:43 -04:00
# property copyright " DualEA "
# property version " 2.0 "
2025-09-10 13:27:03 -04:00
# property strict
2025-09-20 02:15:03 -04:00
2025-09-24 15:10:43 -04:00
//+------------------------------------------------------------------+
//| INCLUDES |
//+------------------------------------------------------------------+
2026-02-05 23:31:20 -05:00
// New Architecture: Auto-learning gate system (unified with PaperEA)
# include "..\Include\GateSystemAutoLearning.mqh"
# include "..\Include\IStrategy.mqh"
# include "..\Include\CPositionManager.mqh"
# include "..\Include\TradeManager.mqh"
# include "..\Include\Telemetry.mqh"
# include "..\Include\TelemetryStandard.mqh"
# include "..\Include\SessionManager.mqh"
# include "..\Include\CorrelationManager.mqh"
# include "..\Include\VolatilitySizer.mqh"
# include "..\Include\InsightsLoader.mqh"
# include "..\Include\IncrementalInsightEngine.mqh" // Real-time O(1) statistics engine
2025-09-18 13:42:21 -04:00
# include "LiveEA_StrategyBridge.mqh"
2025-08-27 12:44:21 -04:00
2026-02-05 23:31:20 -05:00
// Undefine logging macros from included headers to avoid clashes with LogLevel enum
# ifdef LOG_DEBUG
# undef LOG_DEBUG
# endif
# ifdef LOG_INFO
# undef LOG_INFO
# endif
# ifdef LOG_WARNING
# undef LOG_WARNING
# endif
# ifdef LOG_ERROR
# undef LOG_ERROR
# endif
2025-09-24 15:10:43 -04:00
//+------------------------------------------------------------------+
//| ENUMS AND CONSTANTS |
//+------------------------------------------------------------------+
2025-09-10 13:27:03 -04:00
enum LogLevel { LOG_ERROR = 0 , LOG_WARN = 1 , LOG_INFO = 2 , LOG_DEBUG = 3 } ;
2025-09-18 13:42:21 -04:00
2025-09-24 15:10:43 -04:00
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - CORE SETTINGS |
//+------------------------------------------------------------------+
input long MagicNumber = 123456 ;
input int Verbosity = 1 ; // 0=errors,1=warn,2=info,3=debug
input bool TelemetryEnabled = false ; // enable telemetry emission
input bool NoConstraintsMode = false ; // shadow-gate only, do not block
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - TELEMETRY |
//+------------------------------------------------------------------+
input string TelemetryExperiment = " live " ;
input int TelemetryBufferMax = 1000 ;
2025-09-10 13:27:03 -04:00
2025-09-24 15:10:43 -04:00
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - RISK MANAGEMENT |
//+------------------------------------------------------------------+
input double SpreadMaxPoints = 0.0 ; // 0=disabled
input int SessionStartHour = 0 ; // 0..23
input int SessionMaxTrades = 0 ; // 0=disabled
input double MaxDailyLossPct = 0.0 ; // 0=disabled
input double MaxDrawdownPct = 0.0 ; // 0=disabled
input double MinMarginLevel = 0.0 ; // 0=disabled, e.g. 100.0
input int ConsecutiveLossLimit = 0 ; // 0=disabled
input bool P5_PersistLossCounters = false ; // persist counters (minimal in-memory here)
2025-08-13 19:41:04 -04:00
2025-09-24 15:10:43 -04:00
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - INSIGHTS GATING |
//+------------------------------------------------------------------+
input bool UseInsightsGating = true ;
input int GateMinTrades = 0 ; // loosened for bootstrap
input double GateMinWinRate = 0.00 ; // loosened for bootstrap
input double GateMinExpectancyR = -10.0 ; // loosened for bootstrap
input double GateMaxDrawdownR = 1000000.0 ; // loosened for bootstrap
input double GateMinProfitFactor = 0.00 ; // loosened for bootstrap
2025-08-13 19:41:04 -04:00
2025-09-24 15:10:43 -04:00
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - EXPLORATION MODE |
//+------------------------------------------------------------------+
input bool ExploreOnNoSlice = true ; // allow limited trades when slice has no data
input int ExploreMaxPerSlice = 100 ; // loosened for bootstrap
input int ExploreMaxPerSlicePerDay = 100 ; // loosened for bootstrap
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - POLICY GATING |
//+------------------------------------------------------------------+
input bool UsePolicyGating = true ;
input bool DefaultPolicyFallback = true ; // allow neutral trading when policy slice is missing
input bool FallbackDemoOnly = true ; // restrict fallback to demo accounts
input bool FallbackWhenNoPolicy = true ; // allow fallback when policy file is not loaded
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - CORRELATION MANAGEMENT |
//+------------------------------------------------------------------+
input double P5_CorrMax = 1.0 ; // 1.0=no pruning by default
input int P5_CorrLookbackDays = 5 ; // used if PositionManager is not available
input bool UsePositionManager = false ; // optional PositionManager usage
// News / Promotion / Regime / Cooldown inputs
input bool UseNewsFilter = false ; // block around defined news windows
input int NewsBufferBeforeMin = 30 ; // minutes before an event
input int NewsBufferAfterMin = 30 ; // minutes after an event
input int NewsImpactMin = 2 ; // 1=low, 2=medium, 3=high
input bool NewsUseFile = true ; // read blackouts from CSV in Common files
input string NewsFileRelPath = " DualEA \\ news_blackouts.csv " ;
// --- Insights live auto-reload controls
input bool InsightsAutoReload = true ; // if stale/missing, request rebuild and auto-reload when ready
input int InsightsLiveFreshMinutes = 10 ; // consider insights fresh if modified within N minutes
input int InsightsReadyPollSec = 5 ; // poll frequency for ready signal
input bool UsePromotionGate = false ; // allow only during configured windows
input bool PromoLiveOnly = false ; // apply only on live accounts
input int PromoStartHour = 0 ; // inclusive, server time
input int PromoEndHour = 24 ; // exclusive, supports wrap if less than start
input bool UseRegimeGate = false ; // filter by volatility regimes
input int RegimeATRPeriod = 14 ;
input double RegimeMinATRPct = 0.0 ; // 0=disabled
input double RegimeMaxATRPct = 1000.0 ; // 1000=disabled
input int CircuitCooldownSec = 0 ; // 0=disabled
// --- FR-02 Session Manager inputs
input bool UseSessionManager = true ;
input int SessionEndHour = 20 ;
input int MaxTradesPerSession = 10 ;
// --- FR-05 Correlation Manager inputs
input bool UseCorrelationManager = true ;
input double MaxCorrelationLimit = 0.7 ;
input int CorrLookbackDays = 30 ;
// --- FR-04 Position Manager inputs
input int PMMaxOpenPositions = 10 ; // 0=disabled; cap total open positions before PM gate blocks
// --- FR-06 Volatility Sizer inputs
input bool UseVolatilitySizer = false ;
input int VolSizerATRPeriod = 14 ;
input double VolSizerBaseATRPct = 1.0 ;
input double VolSizerMinMult = 0.1 ;
input double VolSizerMaxMult = 3.0 ;
input double VolSizerTargetRisk = 1.0 ;
input double BaseLotSize = 0.01 ; // base lot size used when sizing via VolatilitySizer
//+------------------------------------------------------------------+
//| GLOBAL VARIABLES |
//+------------------------------------------------------------------+
2026-02-05 23:31:20 -05:00
CEfficientGateManagerEnhanced * g_gate_manager = NULL ; // New unified gate system
2025-09-24 15:10:43 -04:00
CTelemetry * g_telemetry = NULL ;
CTelemetryStandard * g_tel_standard = NULL ;
CSessionManager * g_session_manager = NULL ;
CCorrelationManager * g_correlation_manager = NULL ;
CVolatilitySizer * g_volatility_sizer = NULL ;
2026-02-05 23:31:20 -05:00
// g_position_manager and g_insight_engine are defined in their respective modules
// CPositionManager* g_position_manager;
// CIncrementalInsightEngine* g_insight_engine;
2025-09-24 15:10:43 -04:00
bool g_eval_busy = false ;
datetime g_last_trade_placed = 0 ;
2025-08-13 19:41:04 -04:00
2025-09-24 15:10:43 -04:00
// --- Insights gating cache (loaded from insights.json via InsightsLoader)
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 gating cache (loaded from policy.json)
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 [ ] ;
// Live insights reload state
bool g_waiting_insights = false ; // set after we request reload; reset when ready is consumed
// --- Exploration Mode tracking (weekly + daily persistent)
string g_exp_keys [ ] ; // slice key: strategy|symbol|timeframe
int g_exp_weeks [ ] ; // week bucket id (Monday yyyymmdd)
int g_exp_counts [ ] ; // count within week
string g_explore_pending_key = " " ; // set by Insights_Allow when allowing explore
// Daily exploration tracking (persistent)
string g_exp_day_keys [ ] ;
int g_exp_day_days [ ] ;
int g_exp_day_counts [ ] ;
// --- News blackout cache (from CSV)
string g_news_key [ ] ;
datetime g_news_from [ ] ;
datetime g_news_to [ ] ;
int g_news_impact [ ] ;
//+------------------------------------------------------------------+
//| UTILITY FUNCTIONS |
//+------------------------------------------------------------------+
bool ShouldLog ( const int level ) { return ( Verbosity > = level ) ; }
void LogMessage ( const int level , const string message )
2025-09-20 14:14:30 -04:00
{
2025-09-24 15:10:43 -04:00
if ( ! ShouldLog ( level ) ) return ;
2025-09-20 14:14:30 -04:00
2025-09-24 15:10:43 -04:00
string prefix = " " ;
switch ( level )
2025-09-18 13:42:21 -04:00
{
2025-09-24 15:10:43 -04:00
case LOG_ERROR : prefix = " [ERROR] " ; break ;
case LOG_WARN : prefix = " [WARN] " ; break ;
case LOG_INFO : prefix = " [INFO] " ; break ;
case LOG_DEBUG : prefix = " [DEBUG] " ; break ;
2025-09-18 13:42:21 -04:00
}
2025-09-20 14:14:30 -04:00
2025-09-24 15:10:43 -04:00
Print ( prefix + message ) ;
}
//+------------------------------------------------------------------+
//| SERVICE INITIALIZATION FUNCTIONS |
//+------------------------------------------------------------------+
void EnsureTelemetry ( )
{
if ( ! TelemetryEnabled ) return ;
if ( CheckPointer ( g_telemetry ) = = POINTER_INVALID )
{
g_telemetry = new CTelemetry ( _Symbol , TelemetryExperiment , TelemetryBufferMax , ( int ) _Period ) ;
if ( CheckPointer ( g_telemetry ) = = POINTER_INVALID )
{
Print ( " Failed to create telemetry instance " ) ;
return ;
}
g_tel_standard = new CTelemetryStandard ( g_telemetry ) ;
}
}
void EnsurePM ( )
{
if ( ! UsePositionManager ) return ;
if ( CheckPointer ( g_position_manager ) = = POINTER_INVALID )
{
g_position_manager = new CPositionManager ( ) ;
if ( CheckPointer ( g_position_manager ) ! = POINTER_INVALID )
( * g_position_manager ) . SetMagicNumber ( ( int ) MagicNumber ) ;
}
}
void EnsureSessionManager ( )
{
if ( ! UseSessionManager ) return ;
if ( CheckPointer ( g_session_manager ) = = POINTER_INVALID )
{
g_session_manager = new CSessionManager ( _Symbol , _Period ) ;
g_session_manager . SetEnabled ( true ) ;
g_session_manager . SetSessionHours ( SessionStartHour , SessionEndHour ) ;
g_session_manager . SetMaxTradesPerSession ( MaxTradesPerSession ) ;
g_session_manager . SetMaxDailyLossPct ( MaxDailyLossPct ) ;
}
}
void EnsureCorrelationManager ( )
{
if ( ! UseCorrelationManager ) return ;
if ( CheckPointer ( g_correlation_manager ) = = POINTER_INVALID )
{
g_correlation_manager = new CCorrelationManager ( _Symbol , _Period ) ;
g_correlation_manager . SetEnabled ( true ) ;
g_correlation_manager . SetMaxCorrelation ( MaxCorrelationLimit ) ;
g_correlation_manager . SetLookbackDays ( CorrLookbackDays ) ;
}
}
void EnsureVolatilitySizer ( )
{
if ( ! UseVolatilitySizer ) return ;
if ( CheckPointer ( g_volatility_sizer ) = = POINTER_INVALID )
{
g_volatility_sizer = new CVolatilitySizer ( _Symbol , _Period ) ;
g_volatility_sizer . SetEnabled ( true ) ;
g_volatility_sizer . SetATRPeriod ( VolSizerATRPeriod ) ;
g_volatility_sizer . SetBaseATRPercent ( VolSizerBaseATRPct ) ;
g_volatility_sizer . SetMultiplierRange ( VolSizerMinMult , VolSizerMaxMult ) ;
g_volatility_sizer . SetTargetRiskPercent ( VolSizerTargetRisk ) ;
}
}
// --- Spread/Session/Risk gating helpers and session tracking
datetime g_session_start = 0 ;
int g_session_day = 0 ;
double g_session_equity_start = 0.0 ;
double g_equity_highwater = 0.0 ;
double CurrentSpreadPoints ( )
{
double bid = 0.0 , ask = 0.0 ;
SymbolInfoDouble ( _Symbol , SYMBOL_BID , bid ) ;
SymbolInfoDouble ( _Symbol , SYMBOL_ASK , ask ) ;
if ( ask > 0.0 & & bid > 0.0 & & _Point > 0.0 ) return ( ask - bid ) / _Point ;
return 0.0 ;
}
datetime ComputeSessionBoundary ( datetime now )
{
MqlDateTime dt ; TimeToStruct ( now , dt ) ;
dt . hour = SessionStartHour ;
dt . min = 0 ;
dt . sec = 0 ;
datetime today_boundary = StructToTime ( dt ) ;
if ( now < today_boundary ) return today_boundary - 86400 ;
return today_boundary ;
}
void EnsureSessionRollover ( )
{
datetime b = ComputeSessionBoundary ( TimeCurrent ( ) ) ;
MqlDateTime dts ; TimeToStruct ( b , dts ) ;
int day = dts . year * 10000 + dts . mon * 100 + dts . day ;
if ( g_session_start = = 0 | | g_session_day ! = day )
{
g_session_start = b ;
g_session_day = day ;
g_session_equity_start = AccountInfoDouble ( ACCOUNT_EQUITY ) ;
g_equity_highwater = g_session_equity_start ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Session rollover: start=%s equity=%.2f " ,
TimeToString ( g_session_start , TIME_DATE | TIME_MINUTES ) , g_session_equity_start ) ;
}
}
int CountNewTradesSince ( const datetime t0 )
{
int count = 0 ;
if ( ! HistorySelect ( t0 , TimeCurrent ( ) ) ) return 0 ;
int total = ( int ) HistoryDealsTotal ( ) ;
for ( int i = 0 ; i < total ; + + i )
{
ulong ticket = HistoryDealGetTicket ( i ) ;
if ( HistoryDealGetString ( ticket , DEAL_SYMBOL ) ! = _Symbol ) continue ;
long mag = ( long ) HistoryDealGetInteger ( ticket , DEAL_MAGIC ) ;
if ( mag ! = MagicNumber ) continue ;
int entry = ( int ) HistoryDealGetInteger ( ticket , DEAL_ENTRY ) ;
if ( entry = = DEAL_ENTRY_IN ) count + + ;
}
return count ;
}
int CountConsecutiveLosses ( )
{
int consec = 0 ;
datetime t0 = TimeCurrent ( ) - 120 * 86400 ;
if ( ! HistorySelect ( t0 , TimeCurrent ( ) ) ) return 0 ;
for ( int i = HistoryDealsTotal ( ) -1 ; i > = 0 ; - - i )
{
ulong ticket = HistoryDealGetTicket ( i ) ;
if ( ( int ) HistoryDealGetInteger ( ticket , DEAL_ENTRY ) ! = DEAL_ENTRY_OUT ) continue ;
long mag = ( long ) HistoryDealGetInteger ( ticket , DEAL_MAGIC ) ;
if ( mag ! = MagicNumber ) continue ;
double profit = HistoryDealGetDouble ( ticket , DEAL_PROFIT ) ;
if ( profit < 0.0 ) consec + + ;
else break ;
}
return consec ;
}
// --- Minimal persistent consecutive loss counters (in-memory)
string g_loss_sym [ ] ;
long g_loss_mag [ ] ;
int g_loss_cnt [ ] ;
int GetConsecLossPersist ( const string sym , const long mag )
{
for ( int i = 0 ; i < ArraySize ( g_loss_sym ) ; + + i )
if ( g_loss_sym [ i ] = = sym & & g_loss_mag [ i ] = = mag ) return g_loss_cnt [ i ] ;
return 0 ;
}
void SetConsecLossPersist ( const string sym , const long mag , const int val )
{
for ( int i = 0 ; i < ArraySize ( g_loss_sym ) ; + + i )
if ( g_loss_sym [ i ] = = sym & & g_loss_mag [ i ] = = mag ) { g_loss_cnt [ i ] = ( val < 0 ? 0 : val ) ; return ; }
int n = ArraySize ( g_loss_sym ) ;
ArrayResize ( g_loss_sym , n + 1 ) ; ArrayResize ( g_loss_mag , n + 1 ) ; ArrayResize ( g_loss_cnt , n + 1 ) ;
g_loss_sym [ n ] = sym ; g_loss_mag [ n ] = mag ; g_loss_cnt [ n ] = ( val < 0 ? 0 : val ) ;
}
void IncConsecLossPersist ( const string sym , const long mag )
{
for ( int i = 0 ; i < ArraySize ( g_loss_sym ) ; + + i )
if ( g_loss_sym [ i ] = = sym & & g_loss_mag [ i ] = = mag ) { g_loss_cnt [ i ] = g_loss_cnt [ i ] + 1 ; return ; }
int n = ArraySize ( g_loss_sym ) ;
ArrayResize ( g_loss_sym , n + 1 ) ; ArrayResize ( g_loss_mag , n + 1 ) ; ArrayResize ( g_loss_cnt , n + 1 ) ;
g_loss_sym [ n ] = sym ; g_loss_mag [ n ] = mag ; g_loss_cnt [ n ] = 1 ;
}
bool SpreadAllowed ( string & reason )
{
reason = " ok " ;
if ( SpreadMaxPoints < = 0.0 ) return true ;
double spr = CurrentSpreadPoints ( ) ;
if ( spr < = SpreadMaxPoints ) return true ;
reason = " spread_cap " ;
return false ;
}
bool SessionAllowed ( string & reason )
{
reason = " ok " ;
if ( SessionMaxTrades < = 0 ) return true ;
int used = CountNewTradesSince ( g_session_start ) ;
if ( used < SessionMaxTrades ) return true ;
reason = " session_cap " ;
return false ;
}
bool RiskAllowed ( string & reason )
{
reason = " ok " ;
double eq = AccountInfoDouble ( ACCOUNT_EQUITY ) ;
if ( eq > g_equity_highwater ) g_equity_highwater = eq ;
if ( MaxDailyLossPct > 0.0 & & g_session_equity_start > 0.0 )
{
double dd = 100.0 * ( g_session_equity_start - eq ) / g_session_equity_start ;
if ( dd > = MaxDailyLossPct ) { reason = " max_daily_loss " ; return false ; }
}
if ( MaxDrawdownPct > 0.0 & & g_equity_highwater > 0.0 )
{
double ddh = 100.0 * ( g_equity_highwater - eq ) / g_equity_highwater ;
if ( ddh > = MaxDrawdownPct ) { reason = " max_drawdown " ; return false ; }
}
if ( MinMarginLevel > 0.0 )
{
double ml = AccountInfoDouble ( ACCOUNT_MARGIN_LEVEL ) ;
if ( ml > 0.0 & & ml < MinMarginLevel ) { reason = " margin_level " ; return false ; }
}
if ( ConsecutiveLossLimit > 0 )
{
int cl = ( P5_PersistLossCounters ? GetConsecLossPersist ( _Symbol , ( long ) MagicNumber )
: CountConsecutiveLosses ( ) ) ;
if ( cl > = ConsecutiveLossLimit ) { reason = " consec_losses " ; return false ; }
}
return true ;
}
// Helper: append unique key to a dynamic string array
void AddUniqueKey ( string & arr [ ] , const string k )
{
for ( int i = 0 ; i < ArraySize ( arr ) ; + + i )
if ( arr [ i ] = = k ) return ;
int n = ArraySize ( arr ) ;
ArrayResize ( arr , n + 1 ) ;
arr [ n ] = k ;
}
bool NewsAllowed ( string & reason )
{
reason = " ok " ;
if ( ! UseNewsFilter )
return true ;
// Derive keys to match against news file entries
string keys [ ] ;
string sU = _Symbol ; StringToUpper ( sU ) ;
// Add full symbol and wildcard
AddUniqueKey ( keys , sU ) ;
AddUniqueKey ( keys , " * " ) ;
// Try to extract currency codes
string ccy [ ] = { " USD " , " EUR " , " GBP " , " JPY " , " CHF " , " AUD " , " NZD " , " CAD " , " SEK " , " NOK " , " DKK " , " SGD " , " HKD " , " CNY " , " CNH " , " ZAR " , " MXN " , " TRY " } ;
for ( int i = 0 ; i < ArraySize ( ccy ) ; + + i ) if ( StringFind ( sU , ccy [ i ] ) > = 0 ) AddUniqueKey ( keys , ccy [ i ] ) ;
// Index heuristics -> base currency
if ( StringFind ( sU , " US30 " ) > = 0 | | StringFind ( sU , " US500 " ) > = 0 | | StringFind ( sU , " NAS " ) > = 0 | | StringFind ( sU , " DJ " ) > = 0 | | StringFind ( sU , " SPX " ) > = 0 )
AddUniqueKey ( keys , " USD " ) ;
if ( StringFind ( sU , " GER " ) > = 0 | | StringFind ( sU , " DAX " ) > = 0 | | StringFind ( sU , " FRA40 " ) > = 0 | | StringFind ( sU , " CAC " ) > = 0 )
AddUniqueKey ( keys , " EUR " ) ;
if ( StringFind ( sU , " UK100 " ) > = 0 | | StringFind ( sU , " FTSE " ) > = 0 )
AddUniqueKey ( keys , " GBP " ) ;
if ( StringFind ( sU , " JPN " ) > = 0 | | StringFind ( sU , " JP " ) > = 0 | | StringFind ( sU , " NIK " ) > = 0 )
AddUniqueKey ( keys , " JPY " ) ;
if ( StringFind ( sU , " HK50 " ) > = 0 | | StringFind ( sU , " HSI " ) > = 0 )
AddUniqueKey ( keys , " HKD " ) ;
if ( StringFind ( sU , " AUS " ) > = 0 )
AddUniqueKey ( keys , " AUD " ) ;
datetime now = TimeCurrent ( ) ;
// Scan loaded blackout windows
for ( int i = 0 ; i < ArraySize ( g_news_key ) ; + + i )
{
// impact threshold
if ( g_news_impact [ i ] < NewsImpactMin ) continue ;
// key match
bool match = false ;
for ( int j = 0 ; j < ArraySize ( keys ) & & ! match ; + + j )
if ( g_news_key [ i ] = = keys [ j ] ) match = true ;
if ( ! match ) continue ;
// window with buffers
datetime from = g_news_from [ i ] - ( NewsBufferBeforeMin * 60 ) ;
datetime to = g_news_to [ i ] + ( NewsBufferAfterMin * 60 ) ;
if ( now > = from & & now < = to )
{
reason = " news_window " ;
return false ;
}
}
return true ;
}
bool PromotionAllowed ( string & reason )
{
reason = " ok " ;
if ( ! UsePromotionGate )
return true ;
// Apply only on live accounts if configured
if ( PromoLiveOnly & & AccountInfoInteger ( ACCOUNT_TRADE_MODE ) ! = ACCOUNT_TRADE_MODE_REAL )
return true ;
MqlDateTime dt ; TimeToStruct ( TimeCurrent ( ) , dt ) ;
int h = dt . hour ;
// handle windows possibly spanning midnight
bool in_window = false ;
if ( PromoStartHour < = PromoEndHour )
in_window = ( h > = PromoStartHour & & h < PromoEndHour ) ;
else
in_window = ( h > = PromoStartHour | | h < PromoEndHour ) ;
if ( ! in_window )
{ reason = " promo_window " ; return false ; }
return true ;
}
bool RegimeAllowed ( string & reason )
{
reason = " ok " ;
if ( ! UseRegimeGate )
return true ;
int atr_handle = iATR ( _Symbol , _Period , RegimeATRPeriod ) ;
if ( atr_handle = = INVALID_HANDLE )
return true ;
double atr_buf [ ] ; ArrayResize ( atr_buf , 1 ) ;
int copied = CopyBuffer ( atr_handle , 0 , 0 , 1 , atr_buf ) ;
IndicatorRelease ( atr_handle ) ;
if ( copied ! = 1 )
return true ;
double atr = atr_buf [ 0 ] ;
double px = SymbolInfoDouble ( _Symbol , SYMBOL_BID ) ;
if ( atr < = 0.0 | | px < = 0.0 )
return true ; // cannot assess -> allow
double atr_pct = 100.0 * atr / px ;
if ( RegimeMinATRPct > 0.0 & & atr_pct < RegimeMinATRPct )
{ reason = " regime_low_atr " ; return false ; }
if ( RegimeMaxATRPct > 0.0 & & atr_pct > RegimeMaxATRPct )
{ reason = " regime_high_atr " ; return false ; }
return true ;
}
bool CircuitCooldownAllowed ( string & reason )
{
reason = " ok " ;
if ( CircuitCooldownSec < = 0 )
return true ;
if ( g_last_trade_placed < = 0 )
return true ;
datetime now = TimeCurrent ( ) ;
if ( ( now - g_last_trade_placed ) < CircuitCooldownSec )
{ reason = " circuit_cooldown " ; return false ; }
return true ;
}
bool P5_CorrPruneAllow ( const TradeOrder & order , string & reason )
{
reason = " ok " ;
// No open positions -> nothing to prune
int npos = PositionsTotal ( ) ;
if ( npos < = 0 )
return true ;
// Prefer PositionManager's portfolio correlation if available
double avg_abs_corr = 0.0 ;
bool corr_from_pm = ( CheckPointer ( g_position_manager ) ! = POINTER_INVALID ) ;
if ( corr_from_pm )
{
avg_abs_corr = ( * g_position_manager ) . GetPortfolioCorrelation ( _Symbol ) ;
}
else
{
// Manual weighted average absolute correlation to each open symbol
int ps = PeriodSeconds ( _Period ) ;
if ( ps < = 0 ) ps = 60 ;
int approx_bars = ( int ) MathMax ( 50 , MathMin ( 2000 , ( P5_CorrLookbackDays * 86400 ) / ps ) ) ;
double sum = 0.0 , wsum = 0.0 ;
for ( int i = 0 ; i < npos ; + + i )
{
ulong t = PositionGetTicket ( i ) ;
if ( t = = 0 ) continue ;
if ( ! PositionSelectByTicket ( t ) ) continue ;
string osym = PositionGetString ( POSITION_SYMBOL ) ;
if ( osym = = " " | | osym = = _Symbol ) continue ;
double vol = PositionGetDouble ( POSITION_VOLUME ) ;
// Compute Pearson correlation between closes of _Symbol and osym
double a [ ] ; double b [ ] ;
int ca = CopyClose ( _Symbol , _Period , 0 , approx_bars , a ) ;
int cb = CopyClose ( osym , _Period , 0 , approx_bars , b ) ;
int m = MathMin ( ca , cb ) ;
if ( m < 10 ) continue ;
double ma = 0.0 , mb = 0.0 ; for ( int k = 0 ; k < m ; + + k ) { ma + = a [ k ] ; mb + = b [ k ] ; }
ma / = m ; mb / = m ;
double cov = 0.0 , va = 0.0 , vb = 0.0 ;
for ( int k = 0 ; k < m ; + + k )
{
double da = a [ k ] - ma ; double db = b [ k ] - mb ;
cov + = da * db ; va + = da * da ; vb + = db * db ;
}
if ( va < = 0.0 | | vb < = 0.0 ) continue ;
double c = cov / MathSqrt ( va * vb ) ;
if ( c > 1.0 ) c = 1.0 ; if ( c < -1.0 ) c = -1.0 ;
sum + = MathAbs ( c ) * vol ;
wsum + = vol ;
}
avg_abs_corr = ( wsum > 0.0 ? sum / wsum : 0.0 ) ;
}
if ( avg_abs_corr > P5_CorrMax )
{
reason = StringFormat ( " corr=%.2f>max=%.2f " , avg_abs_corr , P5_CorrMax ) ;
return false ;
}
return true ;
}
// --- Insights/Policy helpers and loaders
// Trim helper
string TrimCopy ( string s ) { StringTrimLeft ( s ) ; StringTrimRight ( s ) ; return s ; }
// Normalize broker-specific suffixes to base symbol (e.g., "AUDNZD_otc" -> "AUDNZD")
string NormalizeSymbol ( string s )
{
string lowers = s ; StringToLower ( lowers ) ;
// Common suffixes/prefixes to strip
string suf [ ] = { " _otc " , " _pro " , " _ecn " , " _mini " , " _micro " , " .r " , " .i " , " .pro " , " .ecn " , " .m " } ;
for ( int i = 0 ; i < ArraySize ( suf ) ; + + i )
{
int p = StringFind ( lowers , suf [ i ] , StringLen ( lowers ) - StringLen ( suf [ i ] ) ) ;
if ( p > = 0 & & p = = StringLen ( lowers ) - StringLen ( suf [ i ] ) )
{
// Remove suffix of same length from original string
s = StringSubstr ( s , 0 , StringLen ( s ) - StringLen ( suf [ i ] ) ) ;
break ;
}
}
return s ;
}
// Load policy.json from Common Files and cache min_confidence and slice_probs
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 = " DualEA \\ policy.json " ;
int h = FileOpen ( path , FILE_READ | FILE_TXT | FILE_COMMON | FILE_ANSI ) ;
if ( h = = INVALID_HANDLE )
{
if ( ShouldLog ( LOG_WARN ) ) PrintFormat ( " Policy gating: cannot open %s (Common). Err=%d " , path , GetLastError ( ) ) ;
return false ;
}
string cur_s = " " , cur_y = " " ; int cur_tf = -1 ; double cur_p = -1.0 ;
double cur_sl = 1.0 , cur_tp = 1.0 , cur_tr = 1.0 ; // optional scales default to 1.0
while ( ! FileIsEnding ( h ) )
{
string line = FileReadString ( h ) ;
if ( line = = " " & & FileIsEnding ( h ) ) break ;
int p ;
// min_confidence
p = StringFind ( line , " \" min_confidence \" " , 0 ) ;
if ( p > = 0 )
{
int c = StringFind ( line , " : " , p ) ;
if ( c > = 0 ) { string num = TrimCopy ( StringSubstr ( line , c + 1 ) ) ; double v = StringToDouble ( num ) ; if ( v > 0 ) g_policy_min_conf = v ; }
}
// strategy
p = StringFind ( line , " \" strategy \" " , 0 ) ;
if ( p > = 0 )
{
int q = StringFind ( line , " , " , p + 1 ) ; string seg = ( q > p ? StringSubstr ( line , p , q - p ) : StringSubstr ( line , p ) ) ;
int c3 = StringFind ( seg , " \" " , 0 ) ; c3 = StringFind ( seg , " \" " , c3 + 1 ) ; int c4 = StringFind ( seg , " \" " , c3 + 1 ) ; int c5 = StringFind ( seg , " \" " , c4 + 1 ) ;
if ( c4 > 0 & & c5 > c4 ) cur_s = StringSubstr ( seg , c4 + 1 , c5 - c4 -1 ) ;
}
// symbol
p = StringFind ( line , " \" symbol \" " , 0 ) ;
if ( p > = 0 )
{
int q = StringFind ( line , " , " , p + 1 ) ; string seg = ( q > p ? StringSubstr ( line , p , q - p ) : StringSubstr ( line , p ) ) ;
int c3 = StringFind ( seg , " \" " , 0 ) ; c3 = StringFind ( seg , " \" " , c3 + 1 ) ; int c4 = StringFind ( seg , " \" " , c3 + 1 ) ; int c5 = StringFind ( seg , " \" " , c4 + 1 ) ;
if ( c4 > 0 & & c5 > c4 ) cur_y = StringSubstr ( seg , c4 + 1 , c5 - c4 -1 ) ;
}
// timeframe
p = StringFind ( line , " \" timeframe \" " , 0 ) ;
if ( p > = 0 )
{
int c = StringFind ( line , " : " , p ) ; if ( c > = 0 ) { string num = TrimCopy ( StringSubstr ( line , c + 1 ) ) ; cur_tf = ( int ) StringToInteger ( num ) ; }
}
// p_win
p = StringFind ( line , " \" p_win \" " , 0 ) ;
if ( p > = 0 )
{
int c = StringFind ( line , " : " , p ) ; if ( c > = 0 ) { string num = TrimCopy ( StringSubstr ( line , c + 1 ) ) ; cur_p = StringToDouble ( num ) ; }
}
// optional: sl_scale
p = StringFind ( line , " \" sl_scale \" " , 0 ) ;
if ( p > = 0 )
{
int c = StringFind ( line , " : " , p ) ; if ( c > = 0 ) { string num = TrimCopy ( StringSubstr ( line , c + 1 ) ) ; cur_sl = StringToDouble ( num ) ; }
}
// optional: tp_scale
p = StringFind ( line , " \" tp_scale \" " , 0 ) ;
if ( p > = 0 )
{
int c = StringFind ( line , " : " , p ) ; if ( c > = 0 ) { string num = TrimCopy ( StringSubstr ( line , c + 1 ) ) ; cur_tp = StringToDouble ( num ) ; }
}
// optional: trail_scale
p = StringFind ( line , " \" trail_scale \" " , 0 ) ;
if ( p > = 0 )
{
int c = StringFind ( line , " : " , p ) ; if ( c > = 0 ) { string num = TrimCopy ( StringSubstr ( line , c + 1 ) ) ; cur_tr = StringToDouble ( num ) ; }
}
// commit when we have strat+symbol (timeframe may be -1)
if ( cur_s ! = " " & & cur_y ! = " " & & cur_tf ! = -2 & & cur_p > = 0.0 )
{
int n = ArraySize ( g_pol_strat ) ;
ArrayResize ( g_pol_strat , n + 1 ) ; ArrayResize ( g_pol_sym , n + 1 ) ; ArrayResize ( g_pol_tf , n + 1 ) ;
ArrayResize ( g_pol_p , n + 1 ) ; ArrayResize ( g_pol_sl , n + 1 ) ; ArrayResize ( g_pol_tp , n + 1 ) ; ArrayResize ( g_pol_trail , n + 1 ) ;
g_pol_strat [ n ] = cur_s ; g_pol_sym [ n ] = cur_y ; g_pol_tf [ n ] = cur_tf ; g_pol_p [ n ] = cur_p ; g_pol_sl [ n ] = cur_sl ; g_pol_tp [ n ] = cur_tp ; g_pol_trail [ n ] = cur_tr ;
// reset slice accumulators for next object
cur_s = " " ; cur_y = " " ; cur_tf = -1 ; cur_p = -1.0 ; cur_sl = 1.0 ; cur_tp = 1.0 ; cur_tr = 1.0 ;
}
}
FileClose ( h ) ;
g_policy_loaded = ( ArraySize ( g_pol_strat ) > 0 ) ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Policy gating: min_conf=%.3f slices=%d " , g_policy_min_conf , ArraySize ( g_pol_strat ) ) ;
return g_policy_loaded ;
}
double GetPolicyProb ( const string strategy , const string symbol , const int timeframe )
{
string symN = NormalizeSymbol ( symbol ) ;
// 1) Exact slice: strat+symbol+timeframe
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = timeframe )
return g_pol_p [ i ] ;
// 2) Symbol aggregate across TFs: strat+symbol, tf=-1
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = -1 )
return g_pol_p [ i ] ;
// 3) Strategy aggregate across symbols/TFs: strat only, symbol="*", tf=-1
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = " * " & & g_pol_tf [ i ] = = -1 )
return g_pol_p [ i ] ;
return -1.0 ;
}
double GetPolicyScaleSL ( const string strategy , const string symbol , const int timeframe )
{
string symN = NormalizeSymbol ( symbol ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = timeframe )
return ( g_pol_sl [ i ] > 0 ? g_pol_sl [ i ] : 1.0 ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = -1 )
return ( g_pol_sl [ i ] > 0 ? g_pol_sl [ i ] : 1.0 ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = " * " & & g_pol_tf [ i ] = = -1 )
return ( g_pol_sl [ i ] > 0 ? g_pol_sl [ i ] : 1.0 ) ;
return 1.0 ;
}
double GetPolicyScaleTP ( const string strategy , const string symbol , const int timeframe )
{
string symN = NormalizeSymbol ( symbol ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = timeframe )
return ( g_pol_tp [ i ] > 0 ? g_pol_tp [ i ] : 1.0 ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = -1 )
return ( g_pol_tp [ i ] > 0 ? g_pol_tp [ i ] : 1.0 ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = " * " & & g_pol_tf [ i ] = = -1 )
return ( g_pol_tp [ i ] > 0 ? g_pol_tp [ i ] : 1.0 ) ;
return 1.0 ;
}
double GetPolicyScaleTrail ( const string strategy , const string symbol , const int timeframe )
{
string symN = NormalizeSymbol ( symbol ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = timeframe )
return ( g_pol_trail [ i ] > 0 ? g_pol_trail [ i ] : 1.0 ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = symN & & g_pol_tf [ i ] = = -1 )
return ( g_pol_trail [ i ] > 0 ? g_pol_trail [ i ] : 1.0 ) ;
for ( int i = 0 ; i < ArraySize ( g_pol_strat ) ; + + i )
if ( g_pol_strat [ i ] = = strategy & & g_pol_sym [ i ] = = " * " & & g_pol_tf [ i ] = = -1 )
return ( g_pol_trail [ i ] > 0 ? g_pol_trail [ i ] : 1.0 ) ;
return 1.0 ;
}
// Apply policy-driven scaling to SL/TP/trailing of an order. Entry price approximated.
void ApplyPolicyScaling ( TradeOrder & order , const string symbol , const int timeframe , const double ppol )
{
if ( ! UsePolicyGating | | ! g_policy_loaded ) return ;
// fetch scales
double sls = GetPolicyScaleSL ( order . strategy_name , symbol , timeframe ) ;
double tps = GetPolicyScaleTP ( order . strategy_name , symbol , timeframe ) ;
double trs = GetPolicyScaleTrail ( order . strategy_name , symbol , timeframe ) ;
// determine entry reference
double entry = 0.0 ;
if ( order . price > 0.0 )
entry = order . price ; // pending
else
entry = ( order . action = = ACTION_BUY ? SymbolInfoDouble ( symbol , SYMBOL_ASK ) : SymbolInfoDouble ( symbol , SYMBOL_BID ) ) ;
// Adjust SL
if ( order . stop_loss > 0.0 & & entry > 0.0 & & sls > 0.0 & & sls ! = 1.0 )
{
double d = MathAbs ( entry - order . stop_loss ) ;
double nd = d * sls ;
if ( order . action = = ACTION_BUY )
order . stop_loss = entry - nd ;
else if ( order . action = = ACTION_SELL )
order . stop_loss = entry + nd ;
}
// Adjust TP
if ( order . take_profit > 0.0 & & entry > 0.0 & & tps > 0.0 & & tps ! = 1.0 )
{
double d = MathAbs ( order . take_profit - entry ) ;
double nd = d * tps ;
if ( order . action = = ACTION_BUY )
order . take_profit = entry + nd ;
else if ( order . action = = ACTION_SELL )
order . take_profit = entry - nd ;
}
// Adjust trailing
if ( order . trailing_enabled & & trs > 0.0 & & trs ! = 1.0 )
{
if ( order . trailing_type = = TRAIL_ATR )
order . atr_multiplier * = trs ;
else if ( order . trailing_type = = TRAIL_FIXED_POINTS )
{
order . trail_distance_points * = trs ;
order . trail_step_points * = trs ;
}
}
if ( ShouldLog ( LOG_DEBUG ) )
PrintFormat ( " [POLICY] scales applied %s/%s tf=%d p=%.3f sl=%.2f tp=%.2f tr=%.2f " ,
order . strategy_name , symbol , timeframe , ppol , sls , tps , trs ) ;
}
// --- Exploration counters (weekly/daily) persistence
int WeekMondayId ( datetime t )
{
MqlDateTime dt ; TimeToStruct ( t , dt ) ;
// MT5: day_of_week 0=Sunday, 1=Monday, ... 6=Saturday
int dow = dt . day_of_week ;
int delta_days = ( dow = = 0 ? 6 : ( dow -1 ) ) ;
datetime monday = t - ( delta_days * 86400 ) ;
MqlDateTime md ; TimeToStruct ( monday , md ) ;
return ( md . year * 10000 + md . mon * 100 + md . day ) ;
}
2026-02-05 23:31:20 -05:00
// P0-3: Checksum calculation for exploration counters
uint CalculateExploreChecksum ( string key , int week , int count )
{
string data = key + " | " + IntegerToString ( week ) + " | " + IntegerToString ( count ) ;
uint checksum = 0 ;
for ( int i = 0 ; i < StringLen ( data ) ; i + + )
{
checksum = ( ( checksum < < 5 ) + checksum ) + ( uchar ) StringGetCharacter ( data , i ) ;
}
return checksum ;
}
2025-09-24 15:10:43 -04:00
string ExploreCountsPath ( )
{
return " DualEA \\ explore_counts.csv " ; // FILE_COMMON
}
void SaveExploreCounts ( )
{
string path = ExploreCountsPath ( ) ;
int h = FileOpen ( path , FILE_WRITE | FILE_CSV | FILE_ANSI | FILE_COMMON , ' , ' ) ;
if ( h = = INVALID_HANDLE ) { PrintFormat ( " Explore persist: cannot open %s for write. Err=%d " , path , GetLastError ( ) ) ; return ; }
2026-02-05 23:31:20 -05:00
// P0-3: Add checksum column to header
FileWrite ( h , " key " , " week_monday_yyyymmdd " , " count " , " checksum " ) ;
2025-09-24 15:10:43 -04:00
for ( int i = 0 ; i < ArraySize ( g_exp_keys ) ; + + i )
{
2026-02-05 23:31:20 -05:00
// P0-3: Calculate and write checksum
uint checksum = CalculateExploreChecksum ( g_exp_keys [ i ] , g_exp_weeks [ i ] , g_exp_counts [ i ] ) ;
FileWrite ( h , g_exp_keys [ i ] , IntegerToString ( g_exp_weeks [ i ] ) , IntegerToString ( g_exp_counts [ i ] ) , IntegerToString ( ( long ) checksum ) ) ;
2025-09-24 15:10:43 -04:00
}
FileClose ( h ) ;
2026-02-05 23:31:20 -05:00
// P0-3: Verify file was written correctly
if ( ! VerifyExploreCountsFile ( ) )
{
Print ( " [P0-3] WARNING: Explore counts file verification failed - data may be corrupted " ) ;
}
2025-09-24 15:10:43 -04:00
}
2026-02-05 23:31:20 -05:00
// P0-3: Verify the saved file by reading it back and checking consistency
bool VerifyExploreCountsFile ( )
{
string path = ExploreCountsPath ( ) ;
int h = FileOpen ( path , FILE_READ | FILE_CSV | FILE_ANSI | FILE_COMMON , ' , ' ) ;
if ( h = = INVALID_HANDLE ) return false ;
bool first = true ;
int row = 0 ;
bool valid = true ;
while ( ! FileIsEnding ( h ) & & valid )
{
string k = FileReadString ( h ) ;
if ( k = = " " & & FileIsEnding ( h ) ) break ;
string wk_s = FileReadString ( h ) ;
string cnt_s = FileReadString ( h ) ;
string checksum_s = FileReadString ( h ) ; // P0-3: Read checksum
if ( first ) { first = false ; continue ; } // Skip header
// P0-3: Verify checksum
if ( StringLen ( checksum_s ) > 0 )
{
uint expected = CalculateExploreChecksum ( k , ( int ) StringToInteger ( wk_s ) , ( int ) StringToInteger ( cnt_s ) ) ;
uint actual = ( uint ) StringToInteger ( checksum_s ) ;
if ( expected ! = actual )
{
PrintFormat ( " [P0-3] Checksum mismatch at row %d: key=%s " , row , k ) ;
valid = false ;
}
}
row + + ;
}
FileClose ( h ) ;
return valid ;
}
2025-09-24 15:10:43 -04:00
bool LoadExploreCounts ( )
{
ArrayResize ( g_exp_keys , 0 ) ; ArrayResize ( g_exp_weeks , 0 ) ; ArrayResize ( g_exp_counts , 0 ) ;
string path = ExploreCountsPath ( ) ;
int h = FileOpen ( path , FILE_READ | FILE_CSV | FILE_ANSI | FILE_COMMON , ' , ' ) ;
if ( h = = INVALID_HANDLE ) { if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Explore persist: no prior %s (ok) " , path ) ; return true ; }
bool first = true ;
2026-02-05 23:31:20 -05:00
int corrupted_rows = 0 ;
int total_rows = 0 ;
2025-09-24 15:10:43 -04:00
while ( ! FileIsEnding ( h ) )
{
string k = FileReadString ( h ) ; if ( k = = " " & & FileIsEnding ( h ) ) break ;
string wk_s = FileReadString ( h ) ;
string cnt_s = FileReadString ( h ) ;
2026-02-05 23:31:20 -05:00
string checksum_s = FileReadString ( h ) ; // P0-3: Read checksum column
2025-09-24 15:10:43 -04:00
// skip header if present
if ( first & & ( StringFind ( k , " key " , 0 ) = = 0 ) ) { first = false ; continue ; }
first = false ;
2026-02-05 23:31:20 -05:00
total_rows + + ;
// P0-3: Validate checksum before loading
bool valid = true ;
if ( StringLen ( checksum_s ) > 0 )
{
uint expected = CalculateExploreChecksum ( k , ( int ) StringToInteger ( wk_s ) , ( int ) StringToInteger ( cnt_s ) ) ;
uint actual = ( uint ) StringToInteger ( checksum_s ) ;
if ( expected ! = actual )
{
PrintFormat ( " [P0-3] Corrupted row detected and skipped: key=%s (checksum mismatch) " , k ) ;
corrupted_rows + + ;
valid = false ;
}
}
if ( valid )
{
int n = ArraySize ( g_exp_keys ) ;
ArrayResize ( g_exp_keys , n + 1 ) ; ArrayResize ( g_exp_weeks , n + 1 ) ; ArrayResize ( g_exp_counts , n + 1 ) ;
g_exp_keys [ n ] = k ; g_exp_weeks [ n ] = ( int ) StringToInteger ( wk_s ) ; g_exp_counts [ n ] = ( int ) StringToInteger ( cnt_s ) ;
}
2025-09-24 15:10:43 -04:00
}
FileClose ( h ) ;
2026-02-05 23:31:20 -05:00
// P0-3: Log corruption statistics
if ( corrupted_rows > 0 )
{
PrintFormat ( " [P0-3] LoadExploreCounts: Recovered from %d corrupted rows out of %d total " , corrupted_rows , total_rows ) ;
}
2025-09-24 15:10:43 -04:00
return true ;
}
int DayId ( datetime t )
{
MqlDateTime dt ; TimeToStruct ( t , dt ) ;
return ( dt . year * 10000 + dt . mon * 100 + dt . day ) ;
}
string ExploreDayCountsPath ( )
{
return " DualEA \\ explore_counts_day.csv " ;
}
void SaveExploreCountsDay ( )
{
string path = ExploreDayCountsPath ( ) ;
int h = FileOpen ( path , FILE_WRITE | FILE_CSV | FILE_ANSI | FILE_COMMON , ' , ' ) ;
if ( h = = INVALID_HANDLE ) { PrintFormat ( " Explore persist(day): cannot open %s for write. Err=%d " , path , GetLastError ( ) ) ; return ; }
FileWrite ( h , " key,day_yyyymmdd,count " ) ;
for ( int i = 0 ; i < ArraySize ( g_exp_day_keys ) ; + + i )
FileWrite ( h , g_exp_day_keys [ i ] , IntegerToString ( g_exp_day_days [ i ] ) , IntegerToString ( g_exp_day_counts [ i ] ) ) ;
FileClose ( h ) ;
}
bool LoadExploreCountsDay ( )
{
ArrayResize ( g_exp_day_keys , 0 ) ; ArrayResize ( g_exp_day_days , 0 ) ; ArrayResize ( g_exp_day_counts , 0 ) ;
string path = ExploreDayCountsPath ( ) ;
int h = FileOpen ( path , FILE_READ | FILE_CSV | FILE_ANSI | FILE_COMMON , ' , ' ) ;
if ( h = = INVALID_HANDLE ) { if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Explore persist(day): no prior %s (ok) " , path ) ; return true ; }
bool first = true ;
while ( ! FileIsEnding ( h ) )
{
string k = FileReadString ( h ) ; if ( k = = " " & & FileIsEnding ( h ) ) break ;
string d = FileReadString ( h ) ;
string c = FileReadString ( h ) ;
if ( first & & ( StringFind ( k , " key " , 0 ) = = 0 ) ) { first = false ; continue ; }
first = false ;
int n = ArraySize ( g_exp_day_keys ) ;
ArrayResize ( g_exp_day_keys , n + 1 ) ; ArrayResize ( g_exp_day_days , n + 1 ) ; ArrayResize ( g_exp_day_counts , n + 1 ) ;
g_exp_day_keys [ n ] = k ; g_exp_day_days [ n ] = ( int ) StringToInteger ( d ) ; g_exp_day_counts [ n ] = ( int ) StringToInteger ( c ) ;
}
FileClose ( h ) ;
return true ;
}
string SliceKey ( const string strategy , const string symbol , const int timeframe )
{
return strategy + " | " + symbol + " | " + IntegerToString ( timeframe ) ;
}
int GetExploreCount ( const string key )
{
int wk = WeekMondayId ( TimeCurrent ( ) ) ;
for ( int i = 0 ; i < ArraySize ( g_exp_keys ) ; + + i )
if ( g_exp_keys [ i ] = = key & & g_exp_weeks [ i ] = = wk ) return g_exp_counts [ i ] ;
return 0 ;
}
int GetExploreCountDay ( const string key )
{
int d = DayId ( TimeCurrent ( ) ) ;
for ( int i = 0 ; i < ArraySize ( g_exp_day_keys ) ; + + i )
if ( g_exp_day_keys [ i ] = = key & & g_exp_day_days [ i ] = = d ) return g_exp_day_counts [ i ] ;
return 0 ;
}
void IncExploreCount ( const string key )
{
int wk = WeekMondayId ( TimeCurrent ( ) ) ;
for ( int i = 0 ; i < ArraySize ( g_exp_keys ) ; + + i )
if ( g_exp_keys [ i ] = = key & & g_exp_weeks [ i ] = = wk )
{
g_exp_counts [ i ] = g_exp_counts [ i ] + 1 ;
SaveExploreCounts ( ) ;
// also bump daily
int dd = DayId ( TimeCurrent ( ) ) ; bool day_found = false ;
for ( int j = 0 ; j < ArraySize ( g_exp_day_keys ) ; + + j )
if ( g_exp_day_keys [ j ] = = key & & g_exp_day_days [ j ] = = dd ) { g_exp_day_counts [ j ] + = 1 ; day_found = true ; break ; }
if ( ! day_found )
{
int m = ArraySize ( g_exp_day_keys ) ;
ArrayResize ( g_exp_day_keys , m + 1 ) ; ArrayResize ( g_exp_day_days , m + 1 ) ; ArrayResize ( g_exp_day_counts , m + 1 ) ;
g_exp_day_keys [ m ] = key ; g_exp_day_days [ m ] = dd ; g_exp_day_counts [ m ] = 1 ;
}
SaveExploreCountsDay ( ) ;
return ;
}
int n = ArraySize ( g_exp_keys ) ;
ArrayResize ( g_exp_keys , n + 1 ) ;
ArrayResize ( g_exp_weeks , n + 1 ) ;
ArrayResize ( g_exp_counts , n + 1 ) ;
g_exp_keys [ n ] = key ;
g_exp_weeks [ n ] = wk ;
g_exp_counts [ n ] = 1 ;
SaveExploreCounts ( ) ;
// init daily row
int dd = DayId ( TimeCurrent ( ) ) ;
int m = ArraySize ( g_exp_day_keys ) ;
ArrayResize ( g_exp_day_keys , m + 1 ) ; ArrayResize ( g_exp_day_days , m + 1 ) ; ArrayResize ( g_exp_day_counts , m + 1 ) ;
g_exp_day_keys [ m ] = key ; g_exp_day_days [ m ] = dd ; g_exp_day_counts [ m ] = 1 ;
SaveExploreCountsDay ( ) ;
}
// Check if insights has an existing slice for exact strategy/symbol/timeframe
bool HasSlice ( const string strategy , const string symbol , const int timeframe )
{
for ( int i = 0 ; i < ArraySize ( g_gate_strat ) ; + + i )
if ( g_gate_strat [ i ] = = strategy & & g_gate_sym [ i ] = = symbol & & g_gate_tf [ i ] = = timeframe & & g_gate_cnt [ i ] > 0 )
return true ;
return false ;
}
bool Insights_Load ( )
{
int loaded = Insights_Load_Default (
g_gate_strat ,
g_gate_sym ,
g_gate_tf ,
g_gate_cnt ,
g_gate_wr ,
g_gate_avgR ,
g_gate_pf ,
g_gate_dd
) ;
if ( loaded < 0 )
return false ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Insights gating: loaded %d slices " , ArraySize ( g_gate_strat ) ) ;
return ArraySize ( g_gate_strat ) > 0 ;
}
bool Insights_Allow ( const string strategy , const string symbol , const int timeframe , string & reason , const bool shadow = false , const bool no_side_effects = false )
{
reason = " " ;
// Global bypass for data collection
if ( NoConstraintsMode & & ! shadow )
{
reason = " no_constraints " ;
return true ;
}
// Default policy fallback when no policy is loaded
if ( DefaultPolicyFallback & & UsePolicyGating & & FallbackWhenNoPolicy & & ! g_policy_loaded )
{
bool demo_ok0 = ( ! FallbackDemoOnly ) | | ( AccountInfoInteger ( ACCOUNT_TRADE_MODE ) = = ACCOUNT_TRADE_MODE_DEMO ) ;
if ( demo_ok0 )
{
reason = " fallback_no_policy " ;
return true ;
}
}
// Default policy fallback: allow neutral trading when policy slice is missing
if ( DefaultPolicyFallback & & UsePolicyGating & & g_policy_loaded )
{
double pchk = GetPolicyProb ( strategy , symbol , timeframe ) ;
if ( pchk < 0.0 )
{
bool demo_ok = ( ! FallbackDemoOnly ) | | ( AccountInfoInteger ( ACCOUNT_TRADE_MODE ) = = ACCOUNT_TRADE_MODE_DEMO ) ;
if ( demo_ok )
{
reason = " fallback_policy_miss " ;
return true ;
}
}
}
// Find matching slice
for ( int i = 0 ; i < ArraySize ( g_gate_strat ) ; + + i )
if ( g_gate_strat [ i ] = = strategy & & g_gate_sym [ i ] = = symbol & & g_gate_tf [ i ] = = timeframe )
{
if ( g_gate_cnt [ i ] < GateMinTrades )
{
reason = " min_trades " ; return false ;
}
if ( g_gate_wr [ i ] < GateMinWinRate )
{
reason = " win_rate " ; return false ;
}
if ( g_gate_avgR [ i ] < GateMinExpectancyR )
{
reason = " expectancy " ; return false ;
}
if ( g_gate_dd [ i ] > GateMaxDrawdownR )
{
reason = " drawdown " ; return false ;
}
if ( g_gate_pf [ i ] < GateMinProfitFactor )
{
reason = " profit_factor " ; return false ;
}
// Policy check for exact slice
if ( UsePolicyGating & & g_policy_loaded )
{
double ppol = GetPolicyProb ( strategy , symbol , timeframe ) ;
if ( ppol > = 0.0 & & ppol < g_policy_min_conf )
{ reason = " policy_min_conf " ; return false ; }
}
return true ;
}
// If no slices loaded at all, allow cold-start to bootstrap data
if ( ArraySize ( g_gate_strat ) = = 0 )
{ reason = " cold_start " ; return true ; }
// Fallback 1: aggregate by strategy+symbol across all timeframes
int total = 0 ; double wins = 0.0 ; double sumR = 0.0 ; double pf_sum = 0.0 ; int pf_cnt = 0 ; double worst_dd = 0.0 ;
for ( int j = 0 ; j < ArraySize ( g_gate_strat ) ; + + j )
if ( g_gate_strat [ j ] = = strategy & & g_gate_sym [ j ] = = symbol )
{
total + = g_gate_cnt [ j ] ;
wins + = g_gate_wr [ j ] * g_gate_cnt [ j ] ;
sumR + = g_gate_avgR [ j ] * g_gate_cnt [ j ] ;
pf_sum + = g_gate_pf [ j ] ; pf_cnt + + ;
if ( g_gate_dd [ j ] > worst_dd ) worst_dd = g_gate_dd [ j ] ;
}
if ( total > 0 )
{
double wr = ( total > 0 ? wins / total : 0.0 ) ;
double avgR = ( total > 0 ? sumR / total : 0.0 ) ;
double pf = ( pf_cnt > 0 ? pf_sum / pf_cnt : 0.0 ) ;
double dd = worst_dd ;
if ( total < GateMinTrades )
{
reason = " min_trades_agg_sym " ; return false ;
}
if ( wr < GateMinWinRate )
{
reason = " win_rate_agg_sym " ; return false ;
}
if ( avgR < GateMinExpectancyR )
{
reason = " expectancy_agg_sym " ; return false ;
}
if ( dd > GateMaxDrawdownR )
{
reason = " drawdown_agg_sym " ; return false ;
}
if ( pf < GateMinProfitFactor )
{
reason = " profit_factor_agg_sym " ; return false ;
}
// Policy check for aggregated slice (if available)
if ( UsePolicyGating & & g_policy_loaded )
{
double ppol = GetPolicyProb ( strategy , symbol , timeframe ) ;
if ( ppol > = 0.0 & & ppol < g_policy_min_conf )
{ reason = " policy_min_conf_agg_sym " ; return false ; }
}
reason = " agg_sym_ok " ; return true ;
}
// Fallback 2: aggregate by strategy across all symbols/timeframes
total = 0 ; wins = 0.0 ; sumR = 0.0 ; pf_sum = 0.0 ; pf_cnt = 0 ; worst_dd = 0.0 ;
for ( int j = 0 ; j < ArraySize ( g_gate_strat ) ; + + j )
if ( g_gate_strat [ j ] = = strategy )
{
total + = g_gate_cnt [ j ] ;
wins + = g_gate_wr [ j ] * g_gate_cnt [ j ] ;
sumR + = g_gate_avgR [ j ] * g_gate_cnt [ j ] ;
pf_sum + = g_gate_pf [ j ] ; pf_cnt + + ;
if ( g_gate_dd [ j ] > worst_dd ) worst_dd = g_gate_dd [ j ] ;
}
if ( total > 0 )
{
double wr = ( total > 0 ? wins / total : 0.0 ) ;
double avgR = ( total > 0 ? sumR / total : 0.0 ) ;
double pf = ( pf_cnt > 0 ? pf_sum / pf_cnt : 0.0 ) ;
double dd = worst_dd ;
if ( total < GateMinTrades )
{ reason = " min_trades_agg_strat " ; return false ; }
if ( wr < GateMinWinRate )
{ reason = " win_rate_agg_strat " ; return false ; }
if ( avgR < GateMinExpectancyR )
{ reason = " expectancy_agg_strat " ; return false ; }
if ( dd > GateMaxDrawdownR )
{ reason = " drawdown_agg_strat " ; return false ; }
if ( pf < GateMinProfitFactor )
{ reason = " profit_factor_agg_strat " ; return false ; }
// Policy check for aggregated strategy (if available)
if ( UsePolicyGating & & g_policy_loaded )
{
double ppol = GetPolicyProb ( strategy , symbol , timeframe ) ;
if ( ppol > = 0.0 & & ppol < g_policy_min_conf )
{ reason = " policy_min_conf_agg_strat " ; return false ; }
}
reason = " agg_strat_ok " ; return true ;
}
// If slice not found and gating disabled, allow
if ( ! UseInsightsGating ) return true ;
// Else, no exact slice exists. Exploration Mode: allow only when no slice, with day+week caps
if ( ExploreOnNoSlice )
{
// Strict policy: allow exploration ONLY when no slice exists
bool has_slice = HasSlice ( strategy , symbol , timeframe ) ;
if ( has_slice )
{
reason = " slice_exists " ;
return false ;
}
string key = SliceKey ( strategy , symbol , timeframe ) ;
int used_w = GetExploreCount ( key ) ;
int used_d = GetExploreCountDay ( key ) ;
if ( ExploreMaxPerSlicePerDay > 0 & & used_d > = ExploreMaxPerSlicePerDay )
{ reason = " explore_cap_day " ; return false ; }
if ( ExploreMaxPerSlice > 0 & & used_w > = ExploreMaxPerSlice )
{ reason = " explore_cap_week " ; return false ; }
if ( ! no_side_effects )
g_explore_pending_key = key ;
reason = " explore_allow " ;
return true ;
}
reason = " no_slice " ;
return false ;
}
// --- Evaluation and execution orchestrator
void EvaluateAndMaybeExecute ( const TradeOrder & order )
{
if ( g_eval_busy ) return ; // prevent reentry
g_eval_busy = true ;
ulong t0 = GetTickCount ( ) ;
EnsureSessionRollover ( ) ;
EnsureTelemetry ( ) ;
EnsurePM ( ) ;
EnsureSessionManager ( ) ;
EnsureCorrelationManager ( ) ;
EnsureVolatilitySizer ( ) ;
// Shadow gates (diagnostic only)
if ( NoConstraintsMode & & TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
{
int lat = 0 ;
string r ;
r = " " ; bool s_ok = SpreadAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " spread " , s_ok , ( s_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
r = " " ; bool ses_ok = SessionAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " session " , ses_ok , ( ses_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
r = " " ; bool r_ok = RiskAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " risk " , r_ok , ( r_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
r = " " ; bool news_ok = NewsAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " news " , news_ok , ( news_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
r = " " ; bool promo_ok = PromotionAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " promotion " , promo_ok , ( promo_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
r = " " ; bool regime_ok = RegimeAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " regime " , regime_ok , ( regime_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
r = " " ; bool cool_ok = CircuitCooldownAllowed ( r ) ; lat = ( int ) ( GetTickCount ( ) - t0 ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " circuit_cooldown " , cool_ok , ( cool_ok ? " ok " : r ) + " ;p6_latency= " + IntegerToString ( lat ) + " ms " , true ) ;
// Shadow insights gating (no side effects, ignore NoConstraints bypass)
if ( UseInsightsGating )
{
string rshadow = " " ; bool allow_shadow = Insights_Allow ( order . strategy_name , _Symbol , ( int ) _Period , rshadow , true , true ) ;
( * g_telemetry ) . LogGatingShadow ( order . strategy_name , _Symbol , ( int ) _Period , " insights " , allow_shadow , rshadow , true ) ;
}
}
// Early gates (fail-closed unless NoConstraintsMode)
if ( ! NoConstraintsMode )
{
// Spread gate
string r_spread_early = " " ; if ( ! SpreadAllowed ( r_spread_early ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (spread) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_spread_early ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " spread " , false , r_spread_early + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
// Session gate
string r_sess_early = " " ; if ( ! SessionAllowed ( r_sess_early ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (session) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_sess_early ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " session " , false , r_sess_early + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string rr = " " ; if ( ! RiskAllowed ( rr ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (eq=%.2f base=%.2f high=%.2f ml=%.2f) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , rr , AccountInfoDouble ( ACCOUNT_EQUITY ) , g_session_equity_start , g_equity_highwater , AccountInfoDouble ( ACCOUNT_MARGIN_LEVEL ) ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk " , false , rr + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_news_early = " " ; if ( ! NewsAllowed ( r_news_early ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (news) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_news_early ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " news " , false , r_news_early + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_promo_early = " " ; if ( ! PromotionAllowed ( r_promo_early ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (promotion) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_promo_early ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " promotion " , false , r_promo_early + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_regime_early = " " ; if ( ! RegimeAllowed ( r_regime_early ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (regime) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_regime_early ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " regime " , false , r_regime_early + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_cool_early = " " ; if ( ! CircuitCooldownAllowed ( r_cool_early ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " GATE: blocked %s on %s/%s reason=%s (circuit_cooldown) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_cool_early ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " circuit_cooldown " , false , r_cool_early + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
}
// Insights/Policy gate (fail-closed unless NoConstraintsMode)
if ( ! NoConstraintsMode )
{
2026-02-05 01:22:42 -05:00
// Check IncrementalInsightEngine for auto-promoted strategies
if ( CheckPointer ( g_insight_engine ) ! = POINTER_INVALID )
{
if ( ! g_insight_engine . IsStrategyApproved ( order . strategy_name , _Symbol , ( int ) _Period ) )
{
// Strategy not yet promoted - block trade
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [INSIGHTS] blocked %s on %s/%s reason=not_approved " ,
order . strategy_name , _Symbol , EnumToString ( _Period ) ) ;
g_eval_busy = false ; return ;
}
}
2025-09-24 15:10:43 -04:00
string gate_reason = " " ;
bool ins_ok = Insights_Allow ( order . strategy_name , _Symbol , ( int ) _Period , gate_reason ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " insights " , ins_ok , ( ins_ok ? " ok " : gate_reason ) + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
if ( ! ins_ok )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [INSIGHTS] blocked %s on %s/%s reason=%s " , order . strategy_name , _Symbol , EnumToString ( _Period ) , gate_reason ) ;
g_eval_busy = false ; return ;
}
}
// Correlation gate
string r_corr = " " ; bool corr_ok = P5_CorrPruneAllow ( order , r_corr ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " corr " , corr_ok , ( corr_ok ? " ok " : r_corr ) + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
if ( ! corr_ok ) { if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [CORR] blocked %s on %s/%s reason=%s " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_corr ) ; g_eval_busy = false ; return ; }
// Final RISK4 gates just before execution
string r_rk_exec = " " ;
if ( ! RiskAllowed ( r_rk_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_rk_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4 " , false , r_rk_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_spread_exec = " " ;
if ( ! SpreadAllowed ( r_spread_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s (spread) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_spread_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4_spread " , false , r_spread_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_sess_exec = " " ;
if ( ! SessionAllowed ( r_sess_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s (session) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_sess_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4_session " , false , r_sess_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_news_exec = " " ;
if ( ! NewsAllowed ( r_news_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s (news) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_news_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4_news " , false , r_news_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_promo_exec = " " ;
if ( ! PromotionAllowed ( r_promo_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s (promotion) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_promo_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4_promotion " , false , r_promo_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_regime_exec = " " ;
if ( ! RegimeAllowed ( r_regime_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s (regime) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_regime_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4_regime " , false , r_regime_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
string r_cool_exec = " " ;
if ( ! CircuitCooldownAllowed ( r_cool_exec ) )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [RISK4] blocked %s on %s/%s reason=%s (circuit_cooldown) " , order . strategy_name , _Symbol , EnumToString ( _Period ) , r_cool_exec ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogGating ( _Symbol , ( int ) _Period , order . strategy_name , " risk4_circuit_cooldown " , false , r_cool_exec + " ;p6_latency= " + IntegerToString ( ( int ) ( GetTickCount ( ) - t0 ) ) + " ms " ) ;
g_eval_busy = false ; return ;
}
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [READY] %s on %s/%s passed gates " , order . strategy_name , _Symbol , EnumToString ( _Period ) ) ;
// Apply policy-driven scaling (if enabled and available) using a local copy of order
TradeOrder req = order ;
double ppol_now = ( UsePolicyGating & & g_policy_loaded ? GetPolicyProb ( req . strategy_name , _Symbol , ( int ) _Period ) : -1.0 ) ;
ApplyPolicyScaling ( req , _Symbol , ( int ) _Period , ppol_now ) ;
// --- FR-06 Volatility Position Sizing (set req.lots) ---
if ( UseVolatilitySizer & & CheckPointer ( g_volatility_sizer ) ! = POINTER_INVALID )
2025-09-18 13:42:21 -04:00
{
2025-09-24 15:10:43 -04:00
double stop_pts = 0.0 ;
double bid = 0.0 , ask = 0.0 ; SymbolInfoDouble ( _Symbol , SYMBOL_BID , bid ) ; SymbolInfoDouble ( _Symbol , SYMBOL_ASK , ask ) ;
if ( req . stop_loss > 0.0 )
{
if ( req . order_type = = ORDER_TYPE_BUY | | req . order_type = = ORDER_TYPE_BUY_LIMIT | | req . order_type = = ORDER_TYPE_BUY_STOP )
{
double ref = ( req . price > 0.0 ? req . price : ask ) ;
stop_pts = MathMax ( 0.0 , ( ref - req . stop_loss ) / _Point ) ;
}
else
{
double ref = ( req . price > 0.0 ? req . price : bid ) ;
stop_pts = MathMax ( 0.0 , ( req . stop_loss - ref ) / _Point ) ;
}
}
double atr_pct = 0.0 ; string vs_r1 = " " ; double mult_hint = ( * g_volatility_sizer ) . CalculateSizeMultiplier ( atr_pct , vs_r1 ) ;
double mult_final = 1.0 ; string vs_r2 = " " ;
double sized = ( * g_volatility_sizer ) . CalculatePositionSize ( BaseLotSize , stop_pts , mult_final , vs_r2 ) ;
if ( sized > 0.0 ) req . lots = sized ;
if ( TelemetryEnabled & & CheckPointer ( g_tel_standard ) ! = POINTER_INVALID )
( * g_tel_standard ) . LogVolatilitySizingEvent ( _Symbol , ( int ) _Period , BaseLotSize , mult_final , ( sized > 0.0 ? sized : BaseLotSize ) , atr_pct ) ;
2025-09-18 13:42:21 -04:00
}
2025-09-24 15:10:43 -04:00
// Execute the trade using TradeManager
CTradeManager tm ( _Symbol , 0.0 /* default to symbol min lot if order.lots==0 */ , ( int ) MagicNumber ) ;
bool exec_ok = tm . ExecuteOrder ( req ) ;
uint retcode = tm . ResultRetcode ( ) ;
ulong deal_id = tm . ResultDeal ( ) ;
ulong order_id = tm . ResultOrder ( ) ;
if ( exec_ok )
{
g_last_trade_placed = TimeCurrent ( ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogTradeExecuted ( order . strategy_name , _Symbol , ( int ) _Period , ( int ) retcode , deal_id , order_id ) ;
// Exploration accounting: count only on successful execution
if ( g_explore_pending_key ! = " " )
{
IncExploreCount ( g_explore_pending_key ) ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Exploration used for %s -> %d/%d " , g_explore_pending_key , GetExploreCount ( g_explore_pending_key ) , ExploreMaxPerSlice ) ;
g_explore_pending_key = " " ;
}
}
else
{
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
( * g_telemetry ) . LogTradeFailed ( order . strategy_name , _Symbol , ( int ) _Period , ( int ) retcode ) ;
// Ensure exploration pending key does not leak across failed executions
if ( g_explore_pending_key ! = " " )
{
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Exploration not used due to failed execution for %s " , g_explore_pending_key ) ;
g_explore_pending_key = " " ;
}
}
g_eval_busy = false ;
}
// --- Terminal event handlers
2025-09-10 13:27:03 -04:00
int OnInit ( )
2025-09-24 15:10:43 -04:00
{
EnsureSessionRollover ( ) ;
EnsureTelemetry ( ) ;
EnsurePM ( ) ;
EnsureSessionManager ( ) ;
EnsureCorrelationManager ( ) ;
EnsureVolatilitySizer ( ) ;
2026-02-05 01:22:42 -05:00
// Initialize Incremental Insight Engine for real-time strategy approval
g_insight_engine = new CIncrementalInsightEngine ( ) ;
if ( CheckPointer ( g_insight_engine ) ! = POINTER_INVALID )
{
if ( ShouldLog ( LOG_INFO ) ) Print ( " [LiveEA] Incremental Insight Engine initialized " ) ;
}
2025-09-24 15:10:43 -04:00
// Load exploration counters and gating caches
LoadExploreCounts ( ) ;
LoadExploreCountsDay ( ) ;
bool gate_loaded = Insights_Load ( ) ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Insights gating cache load: %s " , ( gate_loaded ? " ok " : " fail " ) ) ;
if ( UsePolicyGating ) { bool pol_ok = Policy_Load ( ) ; if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Policy cache load: %s " , ( pol_ok ? " ok " : " fail " ) ) ; }
// Load news blackout windows
if ( UseNewsFilter & & NewsUseFile )
{
// Reset and load
ArrayResize ( g_news_key , 0 ) ; ArrayResize ( g_news_from , 0 ) ; ArrayResize ( g_news_to , 0 ) ; ArrayResize ( g_news_impact , 0 ) ;
int h = FileOpen ( NewsFileRelPath , FILE_READ | FILE_CSV | FILE_ANSI | FILE_COMMON , ' , ' ) ;
if ( h ! = INVALID_HANDLE )
{
bool first = true ;
while ( ! FileIsEnding ( h ) )
{
string k = FileReadString ( h ) ; if ( k = = " " & & FileIsEnding ( h ) ) break ;
string sfrom = FileReadString ( h ) ;
string sto = FileReadString ( h ) ;
string simp = FileReadString ( h ) ;
if ( first & & ( StringFind ( k , " key " , 0 ) = = 0 ) ) { first = false ; continue ; }
first = false ;
datetime tfrom = StringToTime ( sfrom ) ;
datetime tto = StringToTime ( sto ) ;
int impact = ( int ) StringToInteger ( simp ) ;
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 ) ;
StringToUpper ( k ) ;
g_news_key [ n ] = k ; g_news_from [ n ] = tfrom ; g_news_to [ n ] = tto ; g_news_impact [ n ] = impact ;
}
FileClose ( h ) ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " News blackout windows loaded: %d " , ArraySize ( g_news_key ) ) ;
}
else
{
if ( ShouldLog ( LOG_WARN ) ) PrintFormat ( " News filter: cannot open %s (Common). Err=%d " , NewsFileRelPath , GetLastError ( ) ) ;
}
}
// GATE summary
string gsum = StringFormat ( " insights=%s policy=%s(min=%.3f) explore=%s caps(slice=%d/day=%d) " ,
( UseInsightsGating ? " on " : " off " ) , ( UsePolicyGating ? " on " : " off " ) , g_policy_min_conf ,
( ExploreOnNoSlice ? " on " : " off " ) , ExploreMaxPerSlice , ExploreMaxPerSlicePerDay ) ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " [GATE] %s " , gsum ) ;
if ( ShouldLog ( LOG_INFO ) ) Print ( " LiveEA initialized " ) ;
// Start watcher timer for insights ready signal
if ( InsightsAutoReload & & InsightsReadyPollSec > 0 )
EventSetTimer ( InsightsReadyPollSec ) ;
// Bootstrap: ensure insights are fresh or request a rebuild immediately
MaybeEnsureInsights ( ) ;
return ( INIT_SUCCEEDED ) ;
}
2025-09-10 13:27:03 -04:00
2025-08-17 10:59:40 -04:00
void OnDeinit ( const int reason )
2025-09-24 15:10:43 -04:00
{
if ( CheckPointer ( g_position_manager ) ! = POINTER_INVALID ) { delete g_position_manager ; g_position_manager = NULL ; }
if ( CheckPointer ( g_telemetry ) ! = POINTER_INVALID ) { delete g_telemetry ; g_telemetry = NULL ; }
if ( CheckPointer ( g_tel_standard ) ! = POINTER_INVALID ) { delete g_tel_standard ; g_tel_standard = NULL ; }
if ( CheckPointer ( g_session_manager ) ! = POINTER_INVALID ) { delete g_session_manager ; g_session_manager = NULL ; }
if ( CheckPointer ( g_correlation_manager ) ! = POINTER_INVALID ) { delete g_correlation_manager ; g_correlation_manager = NULL ; }
if ( CheckPointer ( g_volatility_sizer ) ! = POINTER_INVALID ) { delete g_volatility_sizer ; g_volatility_sizer = NULL ; }
2026-02-05 01:22:42 -05:00
if ( CheckPointer ( g_insight_engine ) ! = POINTER_INVALID ) { delete g_insight_engine ; g_insight_engine = NULL ; }
2025-09-24 15:10:43 -04:00
if ( InsightsAutoReload & & InsightsReadyPollSec > 0 )
EventKillTimer ( ) ;
}
2025-09-10 13:27:03 -04:00
void OnTick ( )
2025-09-24 15:10:43 -04:00
{
// Strategy orchestration is external; gates are available via EvaluateAndMaybeExecute(order)
// Early gating before any selection/insights logic
if ( ! EvaluateAndMaybeExecute_PreSelector ( ) )
{
if ( ShouldLog ( LOG_INFO ) ) Print ( " [EARLY] gates blocked tick " ) ;
return ;
}
// If an external orchestrator performs selection and prepares orders, it should
// call EvaluateAndMaybeExecute_PreExecute() just before execution. We call it here
// as a placeholder to ensure telemetry is emitted even when no orders are placed.
EvaluateAndMaybeExecute_PreExecute ( ) ;
}
// --- Phase 6: Gate orchestration with shadow telemetry
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 ( ShouldLog ( LOG_INFO ) )
PrintFormat ( " [%s] %s latency_ms=%d " , tag , ( allowed ? " allow " : " block " ) , latency ) ;
if ( TelemetryEnabled & & CheckPointer ( g_telemetry ) ! = POINTER_INVALID )
{
string det = StringFormat ( " phase=%s p6_latency_ms=%d " , phase , latency ) ;
( * g_telemetry ) . LogEvent ( _Symbol , ( int ) _Period , " gate " , StringFormat ( " %s_%s " , tag , ( allowed ? " allow " : " block " ) ) , det ) ;
}
// Standardized telemetry
if ( TelemetryEnabled & & CheckPointer ( g_tel_standard ) ! = POINTER_INVALID )
{
( * g_tel_standard ) . LogGateEvent ( _Symbol , ( int ) _Period , tag , allowed , phase , latency , " n/a " ) ;
}
}
// Early gates before selector/insights
bool EarlyGatesAllow ( )
{
string reason ;
ulong t0 ;
// NEWS
t0 = NowMs ( ) ; bool news_ok = NewsAllowed ( reason ) ; LogGate ( " NEWS " , news_ok , " early " , t0 ) ;
if ( ! news_ok & & ! NoConstraintsMode ) return false ;
// PROMO
t0 = NowMs ( ) ; bool promo_ok = PromotionAllowed ( reason ) ; LogGate ( " PROMO " , promo_ok , " early " , t0 ) ;
if ( ! promo_ok & & ! NoConstraintsMode ) return false ;
// REGIME
t0 = NowMs ( ) ; bool reg_ok = RegimeAllowed ( reason ) ; LogGate ( " REGIME " , reg_ok , " early " , t0 ) ;
if ( ! reg_ok & & ! NoConstraintsMode ) return false ;
// CIRCUIT cooldown
t0 = NowMs ( ) ; bool circ_ok = CircuitCooldownAllowed ( reason ) ; LogGate ( " CIRCUIT " , circ_ok , " early " , t0 ) ;
// SESSION (diagnostic in early; block only if NoConstraintsMode is false)
EnsureSessionManager ( ) ;
if ( UseSessionManager & & CheckPointer ( g_session_manager ) ! = POINTER_INVALID )
{
t0 = NowMs ( ) ; bool sess_ok = g_session_manager . IsSessionAllowed ( reason ) ; LogGate ( " SESSION " , sess_ok , " early " , t0 ) ;
if ( ! sess_ok & & ! NoConstraintsMode ) return false ;
}
// CORRELATION (diagnostic/block depending on NoConstraintsMode)
EnsureCorrelationManager ( ) ;
if ( UseCorrelationManager & & CheckPointer ( g_correlation_manager ) ! = POINTER_INVALID )
{
double max_corr ;
t0 = NowMs ( ) ; bool corr_ok = g_correlation_manager . CheckCorrelationLimits ( reason , max_corr ) ; LogGate ( " CORR " , corr_ok , " early " , t0 ) ;
if ( ! corr_ok & & ! NoConstraintsMode ) return false ;
}
return true ;
}
// Final risk4 gates just before execution
bool Risk4GatesAllow ( )
{
string reason ;
ulong t0 ;
t0 = NowMs ( ) ; bool news_ok = NewsAllowed ( reason ) ; LogGate ( " NEWS " , news_ok , " risk4 " , t0 ) ; if ( ! news_ok ) return false ;
t0 = NowMs ( ) ; bool promo_ok = PromotionAllowed ( reason ) ; LogGate ( " PROMO " , promo_ok , " risk4 " , t0 ) ; if ( ! promo_ok ) return false ;
t0 = NowMs ( ) ; bool reg_ok = RegimeAllowed ( reason ) ; LogGate ( " REGIME " , reg_ok , " risk4 " , t0 ) ; if ( ! reg_ok ) return false ;
t0 = NowMs ( ) ; bool circ_ok = CircuitCooldownAllowed ( reason ) ; LogGate ( " CIRCUIT " , circ_ok , " risk4 " , t0 ) ; if ( ! circ_ok ) return false ;
// FR-02 Session gate
EnsureSessionManager ( ) ;
if ( UseSessionManager & & CheckPointer ( g_session_manager ) ! = POINTER_INVALID )
{
t0 = NowMs ( ) ; bool sess_ok = g_session_manager . IsSessionAllowed ( reason ) ; LogGate ( " SESSION " , sess_ok , " risk4 " , t0 ) ; if ( ! sess_ok ) return false ;
}
// FR-05 Correlation gate
EnsureCorrelationManager ( ) ;
if ( UseCorrelationManager & & CheckPointer ( g_correlation_manager ) ! = POINTER_INVALID )
{
double max_corr ;
t0 = NowMs ( ) ; bool corr_ok = g_correlation_manager . CheckCorrelationLimits ( reason , max_corr ) ; LogGate ( " CORR " , corr_ok , " risk4 " , t0 ) ; if ( ! corr_ok ) return false ;
}
// FR-04 Position Manager gate
EnsurePM ( ) ;
if ( UsePositionManager & & CheckPointer ( g_position_manager ) ! = POINTER_INVALID )
{
int open_positions = PositionsTotal ( ) ;
bool pm_ok = ( PMMaxOpenPositions < = 0 ? true : ( open_positions < PMMaxOpenPositions ) ) ;
t0 = NowMs ( ) ; LogGate ( " PM " , pm_ok , " risk4 " , t0 ) ;
if ( ! pm_ok ) return false ;
}
return true ;
}
// Public function to be called by orchestrator before/after selector/insights
bool EvaluateAndMaybeExecute_PreSelector ( )
{
return EarlyGatesAllow ( ) ;
}
bool EvaluateAndMaybeExecute_PreExecute ( )
{
return Risk4GatesAllow ( ) ;
}
// --- Insights ready/reload logic ---
bool IsInsightsFresh ( const int fresh_minutes )
{
string ip = " DualEA \\ insights.json " ;
long ex_i = FileGetInteger ( ip , FILE_EXISTS , true ) ;
if ( ex_i = = 0 ) return false ;
datetime ti = ( datetime ) FileGetInteger ( ip , FILE_MODIFY_DATE , true ) ;
if ( fresh_minutes < = 0 ) return true ; // any existing is fine
return ( ( TimeCurrent ( ) - ti ) < = ( fresh_minutes * 60 ) ) ;
}
void RequestInsightsReload ( )
{
string path = " DualEA \\ insights.reload " ;
if ( FileIsExist ( path , FILE_COMMON ) ) FileDelete ( path , FILE_COMMON ) ;
int h = FileOpen ( path , FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON ) ;
if ( h ! = INVALID_HANDLE )
{
FileWrite ( h , IntegerToString ( ( int ) TimeCurrent ( ) ) ) ;
FileClose ( h ) ;
if ( ShouldLog ( LOG_INFO ) ) Print ( " Insights reload requested " ) ;
}
else
{
if ( ShouldLog ( LOG_WARN ) ) PrintFormat ( " Insights reload: cannot create %s (err=%d) " , path , GetLastError ( ) ) ;
}
}
void CheckInsightsReady ( )
{
string rdy = " DualEA \\ insights.ready " ;
int h = FileOpen ( rdy , FILE_READ | FILE_TXT | FILE_ANSI | FILE_COMMON ) ;
if ( h = = INVALID_HANDLE ) return ;
// Ready present -> consume and reload
FileClose ( h ) ;
if ( ! FileDelete ( rdy , FILE_COMMON ) )
{
// still proceed, but log
if ( ShouldLog ( LOG_WARN ) ) PrintFormat ( " Insights ready: cannot delete %s (err=%d) " , rdy , GetLastError ( ) ) ;
}
bool ok = Insights_Load ( ) ;
if ( ShouldLog ( LOG_INFO ) ) PrintFormat ( " Insights ready detected: reload %s " , ( ok ? " ok " : " fail " ) ) ;
g_waiting_insights = false ;
}
void MaybeEnsureInsights ( )
{
if ( ! InsightsAutoReload ) return ;
if ( IsInsightsFresh ( InsightsLiveFreshMinutes ) ) return ;
if ( ! g_waiting_insights )
{
RequestInsightsReload ( ) ;
g_waiting_insights = true ;
}
}
2025-09-12 21:07:54 -04:00
void OnTimer ( )
2025-09-24 15:10:43 -04:00
{
if ( ! InsightsAutoReload ) return ;
MaybeEnsureInsights ( ) ;
CheckInsightsReady ( ) ;
}