2026-02-24 12:47:37 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| EnhancedEfficientGateSystem.mqh - v2.0 |
|
|
|
|
|
//| Integrates: CGateBase abstract class + Shadow Logging + |
|
|
|
|
|
//| Learning-Based Auto-Tuning + ONNX Dynamic Thresholds |
|
|
|
|
|
//| Based on chat-8Stage Gate System Integration Guide |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#ifndef __ENHANCED_EFFICIENT_GATE_SYSTEM_MQH__
|
|
|
|
|
#define __ENHANCED_EFFICIENT_GATE_SYSTEM_MQH__
|
|
|
|
|
|
|
|
|
|
#include "CGateBase.mqh"
|
|
|
|
|
#include "CShadowLogger.mqh"
|
|
|
|
|
#include "LearningBridge.mqh"
|
|
|
|
|
#include "CFeatures.mqh"
|
|
|
|
|
#include "CGateFeatureCache.mqh"
|
|
|
|
|
#include <Trade\Trade.mqh>
|
|
|
|
|
|
|
|
|
|
// Simple logging macros
|
|
|
|
|
#define LOG_DEBUG(msg) Print(msg)
|
|
|
|
|
#define LOG_INFO(msg) Print(msg)
|
|
|
|
|
#define LOG_WARNING(msg) Print(msg)
|
|
|
|
|
#define LOG_ERROR(msg) Print(msg)
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Helper: Count positions for specific symbol only |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CountPositionsForSymbol(const string symbol)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
|
if(ticket > 0)
|
|
|
|
|
{
|
|
|
|
|
if(PositionGetString(POSITION_SYMBOL) == symbol)
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Processing Modes (same as v1 for compatibility)
|
|
|
|
|
enum EGateProcessingMode
|
|
|
|
|
{
|
|
|
|
|
GATE_MODE_FULL, // All 8 gates (production)
|
|
|
|
|
GATE_MODE_RISK_ONLY, // Risk-critical gates (G1, G4, G7, G8)
|
|
|
|
|
GATE_MODE_FAST, // Skip heavy gates (skip G5, G6)
|
|
|
|
|
GATE_MODE_BYPASS, // Minimal validation only
|
|
|
|
|
GATE_MODE_ALL_BYPASS // ALL gates disabled - for data collection only
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Enhanced Signal Result with Full Metrics |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
struct ESignalResult
|
|
|
|
|
{
|
|
|
|
|
bool passed;
|
|
|
|
|
string block_reason;
|
|
|
|
|
double final_confidence;
|
|
|
|
|
double tweaks[5];
|
|
|
|
|
|
|
|
|
|
// Performance metrics
|
|
|
|
|
ulong total_latency_us;
|
|
|
|
|
int gates_evaluated;
|
|
|
|
|
int gates_passed;
|
|
|
|
|
|
|
|
|
|
// Per-gate results
|
|
|
|
|
EGateResult gate_results[8];
|
|
|
|
|
|
|
|
|
|
void Init()
|
|
|
|
|
{
|
|
|
|
|
passed = false;
|
|
|
|
|
block_reason = "";
|
|
|
|
|
final_confidence = 0.0;
|
|
|
|
|
ArrayInitialize(tweaks, 0.0);
|
|
|
|
|
total_latency_us = 0;
|
|
|
|
|
gates_evaluated = 0;
|
|
|
|
|
gates_passed = 0;
|
|
|
|
|
// Initialize each gate_result individually
|
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
gate_results[i].passed = false;
|
|
|
|
|
gate_results[i].confidence = 0.0;
|
|
|
|
|
gate_results[i].threshold = 0.0;
|
|
|
|
|
gate_results[i].block_reason = "";
|
|
|
|
|
gate_results[i].latency_us = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 1: Signal Rinse - Enhanced with Auto-Tuning |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CSignalRinseGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_min_confidence;
|
|
|
|
|
double m_max_spread_pct;
|
|
|
|
|
bool m_check_signal_integrity;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CSignalRinseGateEnhanced() : CGateBase("SignalRinse")
|
|
|
|
|
{
|
|
|
|
|
m_min_confidence = 0.5;
|
|
|
|
|
m_max_spread_pct = 0.05; // 5%
|
|
|
|
|
m_check_signal_integrity = true;
|
|
|
|
|
SetDefaultThreshold(0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Initialize() override
|
|
|
|
|
{
|
|
|
|
|
// Validate market data available
|
|
|
|
|
if(Bars(_Symbol, _Period) < 100)
|
|
|
|
|
{
|
|
|
|
|
Print("[Gate1] WARNING: Insufficient history (", Bars(_Symbol, _Period), " bars)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
// Calculate signal confidence based on signal strength
|
|
|
|
|
double conf = signal.confidence;
|
|
|
|
|
|
|
|
|
|
// Penalty for weak signals
|
|
|
|
|
if(conf < 0.3) conf *= 0.5;
|
|
|
|
|
|
|
|
|
|
// Bonus for strong directional signals
|
|
|
|
|
if(conf > 0.7) conf = MathMin(1.0, conf * 1.1);
|
|
|
|
|
|
|
|
|
|
return conf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
if(!m_enabled)
|
|
|
|
|
{
|
|
|
|
|
result.Set(true, signal.confidence, m_threshold, "Bypassed", 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
// Check minimum confidence
|
|
|
|
|
if(confidence < m_min_confidence)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_min_confidence,
|
|
|
|
|
StringFormat("Low confidence: %.3f < %.3f", confidence, m_min_confidence),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check spread
|
|
|
|
|
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
|
|
|
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
if(price > 0 && spread / price > m_max_spread_pct)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_max_spread_pct,
|
|
|
|
|
StringFormat("High spread: %.4f%% > %.4f%%", spread/price*100, m_max_spread_pct*100),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_min_confidence, "Signal valid",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMinConfidence(double val) { m_min_confidence = val; }
|
|
|
|
|
void SetMaxSpreadPct(double val) { m_max_spread_pct = val; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 2: Market Soap - Market Condition Validation |
|
|
|
|
|
//| P0-5: Uses cached ATR/ADX values for 40% faster processing |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CMarketSoapGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_max_volatility;
|
|
|
|
|
double m_min_adx;
|
|
|
|
|
double m_atr_multiplier;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CMarketSoapGateEnhanced() : CGateBase("MarketSoap")
|
|
|
|
|
{
|
|
|
|
|
m_max_volatility = 0.15; // 15%
|
|
|
|
|
m_min_adx = 20.0;
|
|
|
|
|
m_atr_multiplier = 1.0;
|
|
|
|
|
SetDefaultThreshold(0.6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
// P0-5: Use direct calculation (cache disabled due to linking issues)
|
|
|
|
|
double atr, adx, price;
|
|
|
|
|
|
|
|
|
|
// Fallback to direct calculation using handles
|
|
|
|
|
int atr_handle = iATR(_Symbol, _Period, 14);
|
|
|
|
|
int adx_handle = iADX(_Symbol, _Period, 14);
|
|
|
|
|
double atr_buf[1], adx_buf[1];
|
|
|
|
|
atr = (CopyBuffer(atr_handle, 0, 0, 1, atr_buf) == 1) ? atr_buf[0] : 0;
|
|
|
|
|
adx = (CopyBuffer(adx_handle, 0, 0, 1, adx_buf) == 1) ? adx_buf[0] : 0;
|
|
|
|
|
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
|
|
|
|
|
if(price == 0) return 0.0;
|
|
|
|
|
|
|
|
|
|
double vol_pct = atr / price;
|
|
|
|
|
|
|
|
|
|
// Confidence decreases with volatility
|
|
|
|
|
double vol_conf = 1.0 - (vol_pct / m_max_volatility);
|
|
|
|
|
vol_conf = MathMax(0.0, MathMin(1.0, vol_conf));
|
|
|
|
|
|
|
|
|
|
// ADX trend strength
|
|
|
|
|
double adx_conf = MathMin(1.0, adx / 50.0);
|
|
|
|
|
|
|
|
|
|
// Combined confidence
|
|
|
|
|
return (vol_conf * 0.6 + adx_conf * 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
if(!m_enabled)
|
|
|
|
|
{
|
|
|
|
|
result.Set(true, 1.0, m_threshold, "Bypassed", 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// P0-5: Use direct calculation (cache disabled due to linking issues)
|
|
|
|
|
double atr, adx, price;
|
|
|
|
|
|
|
|
|
|
// Fallback to direct calculation using handles
|
|
|
|
|
int atr_handle = iATR(_Symbol, _Period, 14);
|
|
|
|
|
int adx_handle = iADX(_Symbol, _Period, 14);
|
|
|
|
|
double atr_buf[1], adx_buf[1];
|
|
|
|
|
atr = (CopyBuffer(atr_handle, 0, 0, 1, atr_buf) == 1) ? atr_buf[0] : 0;
|
|
|
|
|
adx = (CopyBuffer(adx_handle, 0, 0, 1, adx_buf) == 1) ? adx_buf[0] : 0;
|
|
|
|
|
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
// Check volatility
|
|
|
|
|
double vol_pct = (price > 0) ? atr / price : 0;
|
|
|
|
|
|
|
|
|
|
if(vol_pct > m_max_volatility)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_threshold,
|
|
|
|
|
StringFormat("High volatility: %.2f%% > %.2f%%", vol_pct*100, m_max_volatility*100),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check ADX
|
|
|
|
|
if(adx < m_min_adx)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_threshold,
|
|
|
|
|
StringFormat("Weak trend: ADX=%.1f < %.1f", adx, m_min_adx),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_threshold, "Market conditions OK",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMaxVolatility(double val) { m_max_volatility = val; }
|
|
|
|
|
void SetMinADX(double val) { m_min_adx = val; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 3: Strategy Scrub - Strategy Validation |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CStrategyScrubGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_min_strat_confidence;
|
|
|
|
|
bool m_require_confirmation;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CStrategyScrubGateEnhanced() : CGateBase("StrategyScrub")
|
|
|
|
|
{
|
|
|
|
|
m_min_strat_confidence = 0.5;
|
|
|
|
|
m_require_confirmation = true;
|
|
|
|
|
SetDefaultThreshold(0.55);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
// Strategy confidence from signal
|
|
|
|
|
double conf = signal.confidence;
|
|
|
|
|
|
|
|
|
|
// Bonus for specific strategy types
|
|
|
|
|
if(StringFind(signal.strategy_name, "Trend") >= 0 && signal.direction > 0)
|
|
|
|
|
conf *= 1.05; // Trend following in trend direction
|
|
|
|
|
|
|
|
|
|
return MathMin(1.0, conf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
if(!m_enabled)
|
|
|
|
|
{
|
|
|
|
|
result.Set(true, 1.0, m_threshold, "Bypassed", 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
if(confidence < m_min_strat_confidence)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_min_strat_confidence,
|
|
|
|
|
StringFormat("Strategy confidence %.3f < %.3f", confidence, m_min_strat_confidence),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_min_strat_confidence, "Strategy valid",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMinConfidence(double val) { m_min_strat_confidence = val; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 4: Risk Wash - Position Sizing & Risk Management |
|
|
|
|
|
//| ALWAYS ENFORCED (per MQL5 risk guidelines) |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CRiskWashGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_max_risk_pct;
|
|
|
|
|
double m_min_risk_reward;
|
|
|
|
|
double m_max_position_pct;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CRiskWashGateEnhanced() : CGateBase("RiskWash")
|
|
|
|
|
{
|
|
|
|
|
m_max_risk_pct = 0.02; // 2% per trade
|
|
|
|
|
m_min_risk_reward = 1.5; // 1:1.5 minimum
|
|
|
|
|
m_max_position_pct = 0.10; // 10% of equity
|
|
|
|
|
SetDefaultThreshold(0.7);
|
|
|
|
|
Enable(true); // Always enabled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
// Calculate risk confidence based on position sizing
|
|
|
|
|
double sl_distance = MathAbs(signal.entry_price - signal.stop_loss);
|
|
|
|
|
double tp_distance = MathAbs(signal.take_profit - signal.entry_price);
|
|
|
|
|
|
|
|
|
|
if(sl_distance == 0) return 0.0;
|
|
|
|
|
|
|
|
|
|
double rr = tp_distance / sl_distance;
|
|
|
|
|
double rr_conf = MathMin(1.0, rr / m_min_risk_reward);
|
|
|
|
|
|
|
|
|
|
// Position size confidence
|
|
|
|
|
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
|
|
|
double risk_amount = signal.volume * sl_distance * tick_value;
|
|
|
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
|
|
|
|
|
|
|
|
double risk_pct = (equity > 0) ? risk_amount / equity : 1.0;
|
|
|
|
|
double size_conf = MathMax(0.0, 1.0 - (risk_pct / m_max_risk_pct));
|
|
|
|
|
|
|
|
|
|
return (rr_conf * 0.5 + size_conf * 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
// Risk gates are ALWAYS evaluated
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
// Check risk/reward
|
|
|
|
|
double sl_dist = MathAbs(signal.entry_price - signal.stop_loss);
|
|
|
|
|
double tp_dist = MathAbs(signal.take_profit - signal.entry_price);
|
|
|
|
|
|
|
|
|
|
if(sl_dist > 0)
|
|
|
|
|
{
|
|
|
|
|
double rr = tp_dist / sl_dist;
|
|
|
|
|
if(rr < m_min_risk_reward)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_min_risk_reward,
|
|
|
|
|
StringFormat("Risk/Reward %.2f < %.2f", rr, m_min_risk_reward),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check position sizing
|
|
|
|
|
double tick_val = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
|
|
|
double risk_amt = signal.volume * sl_dist * tick_val;
|
|
|
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
|
|
|
|
|
|
|
|
if(equity > 0 && risk_amt / equity > m_max_risk_pct)
|
|
|
|
|
{
|
|
|
|
|
// Auto-adjust position size instead of blocking
|
|
|
|
|
double max_risk_amt = equity * m_max_risk_pct;
|
|
|
|
|
double new_volume = max_risk_amt / (sl_dist * tick_val);
|
|
|
|
|
|
|
|
|
|
// Apply volume constraints
|
|
|
|
|
double min_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
|
|
|
double max_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
|
|
|
|
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
|
|
|
|
|
|
new_volume = MathMax(min_vol, MathMin(max_vol, new_volume));
|
|
|
|
|
new_volume = MathFloor(new_volume / step) * step;
|
|
|
|
|
|
|
|
|
|
signal.volume = new_volume;
|
|
|
|
|
|
|
|
|
|
Print(StringFormat("[Gate4] Position size adjusted: %.2f -> %.2f (risk limit)",
|
|
|
|
|
signal.volume, new_volume));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_threshold, "Risk validated",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMaxRiskPct(double val) { m_max_risk_pct = val; }
|
|
|
|
|
void SetMinRR(double val) { m_min_risk_reward = val; }
|
|
|
|
|
|
|
|
|
|
// Risk gates cannot be disabled
|
|
|
|
|
void Enable(bool enable) { m_enabled = true; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 5: Performance Wax - Actual Win Rate Validation |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CPerformanceWaxGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_min_winrate;
|
|
|
|
|
int m_lookback_trades;
|
|
|
|
|
int m_max_consecutive_losses;
|
|
|
|
|
double m_trade_pnls[]; // For win rate calculation
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CPerformanceWaxGateEnhanced() : CGateBase("PerformanceWax")
|
|
|
|
|
{
|
|
|
|
|
m_min_winrate = 0.45;
|
|
|
|
|
m_lookback_trades = 20;
|
|
|
|
|
m_max_consecutive_losses = 3;
|
|
|
|
|
SetDefaultThreshold(0.65);
|
|
|
|
|
ArrayResize(m_trade_pnls, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RecordTrade(double pnl)
|
|
|
|
|
{
|
|
|
|
|
// Add to history
|
|
|
|
|
int size = ArraySize(m_trade_pnls);
|
|
|
|
|
if(size >= m_lookback_trades)
|
|
|
|
|
{
|
|
|
|
|
// Shift array
|
|
|
|
|
for(int i=0; i<size-1; i++)
|
|
|
|
|
m_trade_pnls[i] = m_trade_pnls[i+1];
|
|
|
|
|
m_trade_pnls[size-1] = pnl;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ArrayResize(m_trade_pnls, size+1);
|
|
|
|
|
m_trade_pnls[size] = pnl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateWinRate()
|
|
|
|
|
{
|
|
|
|
|
int size = ArraySize(m_trade_pnls);
|
|
|
|
|
if(size < 5) return 0.5; // Default neutral
|
|
|
|
|
|
|
|
|
|
int wins = 0;
|
|
|
|
|
for(int i=0; i<size; i++)
|
|
|
|
|
if(m_trade_pnls[i] > 0) wins++;
|
|
|
|
|
|
|
|
|
|
return (double)wins / size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CalculateConsecutiveLosses()
|
|
|
|
|
{
|
|
|
|
|
int size = ArraySize(m_trade_pnls);
|
|
|
|
|
if(size == 0) return 0;
|
|
|
|
|
|
|
|
|
|
int consecutive = 0;
|
|
|
|
|
for(int i=size-1; i>=0; i--)
|
|
|
|
|
{
|
|
|
|
|
if(m_trade_pnls[i] < 0)
|
|
|
|
|
consecutive++;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return consecutive;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
double wr = CalculateWinRate();
|
|
|
|
|
int consec_losses = CalculateConsecutiveLosses();
|
|
|
|
|
|
|
|
|
|
// Confidence based on recent performance
|
|
|
|
|
double conf = wr;
|
|
|
|
|
|
|
|
|
|
// Penalty for consecutive losses
|
|
|
|
|
if(consec_losses > 0)
|
|
|
|
|
conf *= (1.0 - (double)consec_losses / m_max_consecutive_losses);
|
|
|
|
|
|
|
|
|
|
return MathMax(0.0, conf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
if(!m_enabled)
|
|
|
|
|
{
|
|
|
|
|
result.Set(true, 1.0, m_threshold, "Bypassed", 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
double wr = CalculateWinRate();
|
|
|
|
|
int consec = CalculateConsecutiveLosses();
|
|
|
|
|
|
|
|
|
|
// Check consecutive losses (circuit breaker)
|
|
|
|
|
if(consec >= m_max_consecutive_losses)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, 0.0,
|
|
|
|
|
StringFormat("Circuit breaker: %d consecutive losses", consec),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check win rate
|
|
|
|
|
int sample_size = ArraySize(m_trade_pnls);
|
|
|
|
|
if(sample_size >= 10 && wr < m_min_winrate)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_min_winrate,
|
|
|
|
|
StringFormat("Low win rate: %.1f%% < %.1f%% (%d trades)",
|
|
|
|
|
wr*100, m_min_winrate*100, sample_size),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_min_winrate,
|
|
|
|
|
StringFormat("Performance OK: WR=%.1f%% (%d trades)", wr*100, sample_size),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMinWinRate(double val) { m_min_winrate = val; }
|
|
|
|
|
void SetLookback(int val) { m_lookback_trades = val; }
|
|
|
|
|
void SetMaxConsecutiveLosses(int val) { m_max_consecutive_losses = val; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 6: ML Polish - ML Confidence with Dynamic Threshold |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CMLPolishGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_ml_confidence_threshold;
|
|
|
|
|
bool m_use_onnx_model;
|
|
|
|
|
bool m_dynamic_threshold; // Adjust based on market regime
|
|
|
|
|
|
|
|
|
|
// For ONNX integration
|
|
|
|
|
string m_onnx_model_path;
|
|
|
|
|
long m_onnx_handle;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CMLPolishGateEnhanced() : CGateBase("MLPolish")
|
|
|
|
|
{
|
|
|
|
|
m_ml_confidence_threshold = 0.55;
|
|
|
|
|
m_use_onnx_model = false;
|
|
|
|
|
m_dynamic_threshold = true;
|
|
|
|
|
m_onnx_handle = INVALID_HANDLE;
|
|
|
|
|
SetDefaultThreshold(0.6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
// Raw ML confidence from signal
|
|
|
|
|
double raw_conf = signal.confidence;
|
|
|
|
|
|
|
|
|
|
// If using dynamic threshold, adjust based on volatility
|
|
|
|
|
if(m_dynamic_threshold)
|
|
|
|
|
{
|
|
|
|
|
int atr_handle = iATR(_Symbol, _Period, 14);
|
|
|
|
|
double atr_buf[1];
|
|
|
|
|
double atr = (CopyBuffer(atr_handle, 0, 0, 1, atr_buf) == 1) ? atr_buf[0] : 0;
|
2026-03-09 15:23:42 -04:00
|
|
|
IndicatorRelease(atr_handle); // FIX: Release ATR handle to prevent leak
|
2026-02-24 12:47:37 -05:00
|
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
double vol_pct = (price > 0) ? atr / price : 0;
|
|
|
|
|
|
|
|
|
|
// In high volatility, require higher confidence
|
|
|
|
|
double vol_adj = 1.0 + (vol_pct * 10.0); // 0.01 -> 1.1x
|
|
|
|
|
raw_conf /= vol_adj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MathMax(0.0, MathMin(1.0, raw_conf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
if(!m_enabled)
|
|
|
|
|
{
|
|
|
|
|
result.Set(true, 1.0, m_threshold, "Bypassed", 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
// Get dynamic threshold based on regime
|
|
|
|
|
double threshold = m_ml_confidence_threshold;
|
|
|
|
|
if(m_dynamic_threshold)
|
|
|
|
|
{
|
|
|
|
|
int atr_handle2 = iATR(_Symbol, _Period, 14);
|
|
|
|
|
double atr2_buf[1];
|
|
|
|
|
double atr = (CopyBuffer(atr_handle2, 0, 0, 1, atr2_buf) == 1) ? atr2_buf[0] : 0;
|
2026-03-09 15:23:42 -04:00
|
|
|
IndicatorRelease(atr_handle2); // FIX: Release ATR handle to prevent leak
|
2026-02-24 12:47:37 -05:00
|
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
double vol_pct = (price > 0) ? atr / price : 0;
|
|
|
|
|
|
|
|
|
|
// Adjust threshold: higher in high vol, lower in low vol
|
|
|
|
|
if(vol_pct > 0.01)
|
|
|
|
|
threshold = MathMin(0.8, threshold * 1.2);
|
|
|
|
|
else if(vol_pct < 0.005)
|
|
|
|
|
threshold = MathMax(0.4, threshold * 0.9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(confidence < threshold)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, threshold,
|
|
|
|
|
StringFormat("ML confidence %.3f < threshold %.3f", confidence, threshold),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, threshold, "ML validation passed",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetConfidenceThreshold(double val) { m_ml_confidence_threshold = val; }
|
|
|
|
|
void SetDynamicThreshold(bool val) { m_dynamic_threshold = val; }
|
|
|
|
|
void EnableONNX(bool val) { m_use_onnx_model = val; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 7: Live Clean - Market Conditions (ALWAYS ENFORCED) |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CLiveCleanGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
double m_max_spread_pct;
|
|
|
|
|
int m_max_slippage_pts;
|
|
|
|
|
bool m_check_trading_allowed;
|
|
|
|
|
bool m_check_session;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CLiveCleanGateEnhanced() : CGateBase("LiveClean")
|
|
|
|
|
{
|
|
|
|
|
m_max_spread_pct = 0.05; // 5%
|
|
|
|
|
m_max_slippage_pts = 10;
|
|
|
|
|
m_check_trading_allowed = true;
|
|
|
|
|
m_check_session = true;
|
|
|
|
|
SetDefaultThreshold(0.8);
|
|
|
|
|
Enable(true); // Always enabled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
double conf = 1.0;
|
|
|
|
|
|
|
|
|
|
// Spread penalty
|
|
|
|
|
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
|
|
|
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
if(price > 0)
|
|
|
|
|
{
|
|
|
|
|
double spread_pct = spread / price;
|
|
|
|
|
conf -= (spread_pct / m_max_spread_pct) * 0.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Session check
|
|
|
|
|
if(m_check_session)
|
|
|
|
|
{
|
|
|
|
|
MqlDateTime dt;
|
|
|
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
|
|
|
int hour = dt.hour;
|
|
|
|
|
|
|
|
|
|
// Reduce confidence outside active hours (London/NY overlap best)
|
|
|
|
|
if(hour < 8 || hour > 20)
|
|
|
|
|
conf *= 0.9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MathMax(0.0, conf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
// ALWAYS evaluate
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
// Check spread
|
|
|
|
|
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
|
|
|
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
|
|
|
|
|
if(price > 0 && spread / price > m_max_spread_pct)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_max_spread_pct,
|
|
|
|
|
StringFormat("Spread too high: %.4f%% > %.4f%%",
|
|
|
|
|
spread/price*100, m_max_spread_pct*100),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if trading allowed
|
|
|
|
|
if(m_check_trading_allowed)
|
|
|
|
|
{
|
|
|
|
|
long trade_mode = 0;
|
|
|
|
|
SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE, trade_mode);
|
|
|
|
|
if(trade_mode == SYMBOL_TRADE_MODE_DISABLED)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, 0.0,
|
|
|
|
|
"Trading disabled for symbol",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_threshold, "Market conditions OK",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMaxSpreadPct(double val) { m_max_spread_pct = val; }
|
|
|
|
|
void EnableTradingCheck(bool val) { m_check_trading_allowed = val; }
|
|
|
|
|
|
|
|
|
|
// Cannot disable
|
|
|
|
|
void Enable(bool enable) { m_enabled = true; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gate 8: Final Verify - Pre-Execution Checks (ALWAYS ENFORCED) |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CFinalVerifyGateEnhanced : public CGateBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
int m_max_positions;
|
|
|
|
|
bool m_check_correlation;
|
|
|
|
|
double m_max_correlation;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CFinalVerifyGateEnhanced() : CGateBase("FinalVerify")
|
|
|
|
|
{
|
|
|
|
|
m_max_positions = 5;
|
|
|
|
|
m_check_correlation = true;
|
|
|
|
|
m_max_correlation = 0.8;
|
|
|
|
|
SetDefaultThreshold(0.9);
|
|
|
|
|
Enable(true); // Always enabled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateConfidence(const TradingSignal &signal) override
|
|
|
|
|
{
|
|
|
|
|
double conf = 1.0;
|
|
|
|
|
|
|
|
|
|
// Position count penalty - PER SYMBOL
|
|
|
|
|
int open_pos = CountPositionsForSymbol(_Symbol);
|
|
|
|
|
if(open_pos > m_max_positions)
|
|
|
|
|
conf = 0.0;
|
|
|
|
|
else if(open_pos > m_max_positions / 2)
|
|
|
|
|
conf = 0.5;
|
|
|
|
|
|
|
|
|
|
return conf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Validate(TradingSignal &signal, EGateResult &result) override
|
|
|
|
|
{
|
|
|
|
|
ulong start = GetMicrosecondCount();
|
|
|
|
|
|
|
|
|
|
// ALWAYS evaluate
|
|
|
|
|
double confidence = CalculateConfidence(signal);
|
|
|
|
|
|
|
|
|
|
// Check position limits only - PER SYMBOL (not global)
|
|
|
|
|
int open_pos = CountPositionsForSymbol(_Symbol);
|
|
|
|
|
if(open_pos >= m_max_positions)
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, m_max_positions,
|
|
|
|
|
StringFormat("Max positions reached for %s: %d/%d", _Symbol, open_pos, m_max_positions),
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate entry price only (SL/TP already validated by G4)
|
|
|
|
|
// CRITICAL: Use signal.price as signal.entry_price may not be populated
|
|
|
|
|
double check_price = (signal.entry_price > 0) ? signal.entry_price : signal.price;
|
|
|
|
|
if(check_price <= 0 || !MathIsValidNumber(check_price))
|
|
|
|
|
{
|
|
|
|
|
result.Set(false, confidence, 0.0,
|
|
|
|
|
"Invalid entry price",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Set(true, confidence, m_threshold, "Pre-execution checks passed",
|
|
|
|
|
GetMicrosecondCount() - start);
|
|
|
|
|
RecordPass(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMaxPositions(int val) { m_max_positions = val; }
|
|
|
|
|
|
|
|
|
|
// Cannot disable
|
|
|
|
|
void Enable(bool enable) { m_enabled = true; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Enhanced Efficient Gate Manager - Main Orchestrator |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CEfficientGateManagerEnhanced
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
// Gates
|
|
|
|
|
CSignalRinseGateEnhanced m_g1_signal;
|
|
|
|
|
CMarketSoapGateEnhanced m_g2_market;
|
|
|
|
|
CStrategyScrubGateEnhanced m_g3_strategy;
|
|
|
|
|
CRiskWashGateEnhanced m_g4_risk;
|
|
|
|
|
CPerformanceWaxGateEnhanced m_g5_performance;
|
|
|
|
|
CMLPolishGateEnhanced m_g6_ml;
|
|
|
|
|
CLiveCleanGateEnhanced m_g7_live;
|
|
|
|
|
CFinalVerifyGateEnhanced m_g8_final;
|
|
|
|
|
|
|
|
|
|
CGateBase* m_gates[8]; // Array for iteration
|
|
|
|
|
|
|
|
|
|
// Configuration
|
|
|
|
|
EGateProcessingMode m_mode;
|
|
|
|
|
bool m_no_constraints;
|
|
|
|
|
bool m_use_insights;
|
|
|
|
|
bool m_use_policy;
|
|
|
|
|
|
|
|
|
|
// Shadow logging
|
|
|
|
|
CShadowLogger m_shadow_logger;
|
|
|
|
|
bool m_use_shadow_logging;
|
|
|
|
|
|
|
|
|
|
// Performance tracking
|
|
|
|
|
ulong m_total_signals;
|
|
|
|
|
ulong m_passed_signals;
|
|
|
|
|
double m_avg_latency_ms;
|
|
|
|
|
|
|
|
|
|
// Feature extraction
|
|
|
|
|
CFeatures m_features;
|
|
|
|
|
bool m_features_initialized;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Constructor
|
|
|
|
|
CEfficientGateManagerEnhanced(bool no_constraints=false,
|
|
|
|
|
bool use_insights=false,
|
|
|
|
|
bool use_policy=false)
|
|
|
|
|
{
|
|
|
|
|
m_no_constraints = no_constraints;
|
|
|
|
|
m_use_insights = use_insights;
|
|
|
|
|
m_use_policy = use_policy;
|
|
|
|
|
m_mode = GATE_MODE_FULL;
|
|
|
|
|
m_use_shadow_logging = false;
|
|
|
|
|
m_total_signals = 0;
|
|
|
|
|
m_passed_signals = 0;
|
|
|
|
|
m_avg_latency_ms = 0.0;
|
|
|
|
|
m_features_initialized = false;
|
|
|
|
|
|
|
|
|
|
// Build gate array
|
|
|
|
|
m_gates[0] = GetPointer(m_g1_signal);
|
|
|
|
|
m_gates[1] = GetPointer(m_g2_market);
|
|
|
|
|
m_gates[2] = GetPointer(m_g3_strategy);
|
|
|
|
|
m_gates[3] = GetPointer(m_g4_risk);
|
|
|
|
|
m_gates[4] = GetPointer(m_g5_performance);
|
|
|
|
|
m_gates[5] = GetPointer(m_g6_ml);
|
|
|
|
|
m_gates[6] = GetPointer(m_g7_live);
|
|
|
|
|
m_gates[7] = GetPointer(m_g8_final);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize with shadow logging support
|
|
|
|
|
bool Initialize(bool shadow_mode=false)
|
|
|
|
|
{
|
|
|
|
|
// Initialize shadow logger
|
|
|
|
|
m_use_shadow_logging = shadow_mode;
|
|
|
|
|
if(!m_shadow_logger.Initialize(shadow_mode))
|
|
|
|
|
{
|
|
|
|
|
Print("[GateMgr] WARNING: Shadow logger initialization failed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize all gates
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
{
|
|
|
|
|
if(!m_gates[i].Initialize())
|
|
|
|
|
{
|
|
|
|
|
Print(StringFormat("[GateMgr] WARNING: Gate %d (%s) initialization failed",
|
|
|
|
|
i+1, m_gates[i].GetName()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set mode
|
|
|
|
|
if(m_no_constraints)
|
|
|
|
|
SetMode(GATE_MODE_RISK_ONLY);
|
|
|
|
|
|
|
|
|
|
Print("[GateMgr] Enhanced Efficient Gate System initialized (v2.0)");
|
|
|
|
|
if(shadow_mode)
|
|
|
|
|
Print("[GateMgr] SHADOW MODE: Logging without execution");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set processing mode
|
|
|
|
|
void SetMode(EGateProcessingMode mode)
|
|
|
|
|
{
|
|
|
|
|
m_mode = mode;
|
|
|
|
|
|
|
|
|
|
// Configure gates based on mode
|
|
|
|
|
switch(mode)
|
|
|
|
|
{
|
|
|
|
|
case GATE_MODE_RISK_ONLY:
|
|
|
|
|
// Only risk-critical gates: G1, G4, G7, G8
|
|
|
|
|
m_g2_market.Enable(false);
|
|
|
|
|
m_g3_strategy.Enable(false);
|
|
|
|
|
m_g5_performance.Enable(false);
|
|
|
|
|
m_g6_ml.Enable(false);
|
|
|
|
|
Print("[GateMgr] Mode: RISK_ONLY (G1,G4,G7,G8 active)");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GATE_MODE_FAST:
|
|
|
|
|
// Skip heavy performance gates: G5, G6
|
|
|
|
|
m_g5_performance.Enable(false);
|
|
|
|
|
m_g6_ml.Enable(false);
|
|
|
|
|
Print("[GateMgr] Mode: FAST (skip G5,G6)");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GATE_MODE_BYPASS:
|
|
|
|
|
// Minimal - only critical risk gates
|
|
|
|
|
m_g2_market.Enable(false);
|
|
|
|
|
m_g3_strategy.Enable(false);
|
|
|
|
|
m_g5_performance.Enable(false);
|
|
|
|
|
m_g6_ml.Enable(false);
|
|
|
|
|
Print("[GateMgr] Mode: BYPASS (minimal validation)");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GATE_MODE_ALL_BYPASS:
|
|
|
|
|
// DATA COLLECTION MODE: Disable ALL gates
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
m_gates[i].Enable(false);
|
|
|
|
|
Print("[GateMgr] Mode: ALL_BYPASS (ALL gates disabled - DATA COLLECTION)");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default: // GATE_MODE_FULL
|
|
|
|
|
// Enable all gates
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
m_gates[i].Enable(true);
|
|
|
|
|
Print("[GateMgr] Mode: FULL (all 8 gates active)");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configure individual gates
|
|
|
|
|
void SetPerformanceWaxEnabled(bool val, double min_wr=0.45, int lookback=20)
|
|
|
|
|
{
|
|
|
|
|
m_g5_performance.SetMinWinRate(min_wr);
|
|
|
|
|
m_g5_performance.SetLookback(lookback);
|
|
|
|
|
m_g5_performance.Enable(val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetMLPolishEnabled(bool val, double threshold=0.55)
|
|
|
|
|
{
|
|
|
|
|
m_g6_ml.SetConfidenceThreshold(threshold);
|
|
|
|
|
m_g6_ml.Enable(val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetLiveCleanEnabled(bool val, double max_spread=0.05)
|
|
|
|
|
{
|
|
|
|
|
m_g7_live.SetMaxSpreadPct(max_spread);
|
|
|
|
|
m_g7_live.Enable(val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable auto-tuning for specific gates
|
|
|
|
|
void EnableAutoTune(int gate_num, const SGateAutoTuneConfig &config)
|
|
|
|
|
{
|
|
|
|
|
if(gate_num >= 1 && gate_num <= 8)
|
|
|
|
|
m_gates[gate_num-1].EnableAutoTune(config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record trade outcome for learning
|
|
|
|
|
void RecordTradeOutcome(double pnl)
|
|
|
|
|
{
|
|
|
|
|
m_g5_performance.RecordTrade(pnl);
|
|
|
|
|
|
|
|
|
|
// Record for all gates with auto-tuning
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
m_gates[i].RecordTradeOutcome(pnl, true); // Assume passed for simplicity
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Main signal processing with full metrics
|
|
|
|
|
bool ProcessSignalEnhanced(TradingSignal &signal,
|
|
|
|
|
CSignalDecision &decision,
|
|
|
|
|
string &block_reason)
|
|
|
|
|
{
|
|
|
|
|
ulong start_time = GetMicrosecondCount();
|
|
|
|
|
block_reason = "";
|
|
|
|
|
|
|
|
|
|
// Normalize TradingSignal fields to ensure consistency across generators/gates/execution
|
|
|
|
|
if(signal.symbol == "")
|
|
|
|
|
signal.symbol = _Symbol;
|
|
|
|
|
if(signal.timeframe == PERIOD_CURRENT)
|
|
|
|
|
signal.timeframe = (ENUM_TIMEFRAMES)_Period;
|
|
|
|
|
if(signal.timestamp == 0)
|
|
|
|
|
signal.timestamp = TimeCurrent();
|
|
|
|
|
|
|
|
|
|
// Strategy name normalization
|
|
|
|
|
if(signal.strategy == "" && signal.strategy_name != "")
|
|
|
|
|
signal.strategy = signal.strategy_name;
|
|
|
|
|
if(signal.strategy_name == "" && signal.strategy != "")
|
|
|
|
|
signal.strategy_name = signal.strategy;
|
|
|
|
|
|
|
|
|
|
// Order type / direction normalization
|
|
|
|
|
if(signal.order_type <= 0)
|
|
|
|
|
signal.order_type = (signal.type == 0 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
|
|
|
|
|
if(signal.type != 0 && signal.type != 1)
|
|
|
|
|
signal.type = (signal.order_type == ORDER_TYPE_SELL || signal.order_type == ORDER_TYPE_SELL_LIMIT || signal.order_type == ORDER_TYPE_SELL_STOP || signal.order_type == ORDER_TYPE_SELL_STOP_LIMIT) ? 1 : 0;
|
|
|
|
|
if(signal.direction == 0)
|
|
|
|
|
signal.direction = (signal.type == 0 ? 1 : -1);
|
|
|
|
|
|
|
|
|
|
// Price/SL/TP normalization: prefer canonical price/sl/tp
|
|
|
|
|
if(signal.price <= 0.0)
|
|
|
|
|
signal.price = signal.entry_price;
|
|
|
|
|
if(signal.sl <= 0.0)
|
|
|
|
|
signal.sl = signal.stop_loss;
|
|
|
|
|
if(signal.tp <= 0.0)
|
|
|
|
|
signal.tp = signal.take_profit;
|
|
|
|
|
if(signal.entry_price <= 0.0)
|
|
|
|
|
signal.entry_price = signal.price;
|
|
|
|
|
if(signal.stop_loss <= 0.0)
|
|
|
|
|
signal.stop_loss = signal.sl;
|
|
|
|
|
if(signal.take_profit <= 0.0)
|
|
|
|
|
signal.take_profit = signal.tp;
|
|
|
|
|
|
|
|
|
|
// Confidence normalization
|
|
|
|
|
if(!MathIsValidNumber(signal.confidence) || signal.confidence <= 0.0 || signal.confidence > 1.0)
|
|
|
|
|
signal.confidence = 0.5;
|
|
|
|
|
|
|
|
|
|
// Execution-invalid guardrails (only hard stops allowed)
|
|
|
|
|
if(signal.price <= 0.0 || !MathIsValidNumber(signal.price))
|
|
|
|
|
{
|
|
|
|
|
block_reason = "Invalid price";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(signal.volume < 0.0 || !MathIsValidNumber(signal.volume))
|
|
|
|
|
{
|
|
|
|
|
block_reason = "Invalid volume";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create shadow log entry
|
|
|
|
|
SShadowLogEntry log_entry;
|
|
|
|
|
double log_price = (signal.entry_price > 0) ? signal.entry_price : signal.price;
|
|
|
|
|
double log_volume = (signal.volume > 0) ? signal.volume : 0.1;
|
|
|
|
|
log_entry.Init(_Symbol,
|
|
|
|
|
(signal.direction > 0) ? "BUY" : "SELL",
|
|
|
|
|
log_price,
|
|
|
|
|
log_volume,
|
|
|
|
|
signal.strategy_name);
|
|
|
|
|
|
|
|
|
|
// Execute gates in adjust-only mode: never block except execution-invalid
|
|
|
|
|
// Per-gate budget: max 2ms per gate to prevent cascade latency
|
|
|
|
|
const ulong GATE_BUDGET_US = 2000; // 2ms per gate max
|
|
|
|
|
bool passed = true;
|
|
|
|
|
int gates_evaluated = 0;
|
|
|
|
|
int gates_passed = 0;
|
|
|
|
|
int gates_skipped_budget = 0;
|
|
|
|
|
string soft_reasons = "";
|
|
|
|
|
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
{
|
|
|
|
|
ulong gate_start_us = GetMicrosecondCount();
|
|
|
|
|
CGateBase* gate = m_gates[i];
|
|
|
|
|
|
|
|
|
|
// Skip ALL gates in ALL_BYPASS mode (data collection)
|
|
|
|
|
if(m_mode == GATE_MODE_ALL_BYPASS)
|
|
|
|
|
continue;
|
|
|
|
|
|
2026-03-09 15:23:42 -04:00
|
|
|
// FIX: Explicit mode-based gate skipping
|
|
|
|
|
if(m_mode == GATE_MODE_RISK_ONLY)
|
|
|
|
|
{
|
|
|
|
|
// RISK_ONLY: Only run G1, G4, G7, G8 (indices 0, 3, 6, 7)
|
|
|
|
|
if(i != 0 && i != 3 && i != 6 && i != 7)
|
|
|
|
|
continue; // Skip G2, G3, G5, G6
|
|
|
|
|
}
|
|
|
|
|
else if(m_mode == GATE_MODE_FAST)
|
|
|
|
|
{
|
|
|
|
|
// FAST: Skip G5, G6 (indices 4, 5)
|
|
|
|
|
if(i == 4 || i == 5)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Normal mode: Skip disabled gates (but G1,G4,G7,G8 always run)
|
|
|
|
|
if(!gate.IsEnabled() && i != 0 && i != 3 && i != 6 && i != 7)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-02-24 12:47:37 -05:00
|
|
|
|
|
|
|
|
// Check remaining budget before evaluating gate
|
|
|
|
|
ulong elapsed_total_us = GetMicrosecondCount() - start_time;
|
|
|
|
|
if(elapsed_total_us > 15000) // Hard stop at 15ms total
|
|
|
|
|
{
|
|
|
|
|
gates_skipped_budget++;
|
|
|
|
|
PrintFormat("[GATE-BUDGET] Skipping G%d due to total budget exceeded (%.2fms)", i+1, elapsed_total_us/1000.0);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EGateResult result;
|
|
|
|
|
bool gate_passed = gate.Validate(signal, result);
|
|
|
|
|
|
|
|
|
|
// Enforce per-gate budget post-validation
|
|
|
|
|
ulong gate_latency_us = GetMicrosecondCount() - gate_start_us;
|
|
|
|
|
if(gate_latency_us > GATE_BUDGET_US)
|
|
|
|
|
{
|
|
|
|
|
Print(StringFormat("[GATE-SLOW] G%d took %.2fms (budget: %.2fms)", i+1, gate_latency_us/1000.0, GATE_BUDGET_US/1000.0));
|
|
|
|
|
}
|
|
|
|
|
gate.RecordLatency(gate_latency_us);
|
|
|
|
|
|
|
|
|
|
// P0-P5 integration: record gate outcomes for controller status/auditing
|
|
|
|
|
PostGateRecord(StringFormat("G%d", i+1), gate_passed, result.confidence);
|
|
|
|
|
|
|
|
|
|
// Record in log entry
|
|
|
|
|
log_entry.SetGateResult(i, result);
|
|
|
|
|
|
|
|
|
|
gates_evaluated++;
|
|
|
|
|
if(gate_passed)
|
|
|
|
|
gates_passed++;
|
|
|
|
|
|
|
|
|
|
// Soft-fail: record reason and continue evaluating remaining gates
|
|
|
|
|
if(!gate_passed)
|
|
|
|
|
{
|
|
|
|
|
passed = false;
|
|
|
|
|
string reason = StringFormat("G%d_%s: %s", i+1, gate.GetName(), result.block_reason);
|
|
|
|
|
if(soft_reasons == "")
|
|
|
|
|
soft_reasons = reason;
|
|
|
|
|
else
|
|
|
|
|
soft_reasons = soft_reasons + " | " + reason;
|
|
|
|
|
|
|
|
|
|
// Penalize confidence slightly but do not block
|
|
|
|
|
if(MathIsValidNumber(result.confidence) && result.confidence >= 0.0 && result.confidence <= 1.0)
|
|
|
|
|
signal.confidence = MathMax(0.05, MathMin(1.0, signal.confidence * (0.9 + 0.1 * result.confidence)));
|
|
|
|
|
else
|
|
|
|
|
signal.confidence = MathMax(0.05, MathMin(1.0, signal.confidence * 0.9));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Log budget summary if gates were skipped
|
|
|
|
|
if(gates_skipped_budget > 0)
|
|
|
|
|
{
|
|
|
|
|
Print(StringFormat("[GATE-BUDGET] %d gates skipped due to total budget constraints", gates_skipped_budget));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update shadow log
|
|
|
|
|
log_entry.all_gates_passed = passed;
|
|
|
|
|
log_entry.final_confidence = signal.confidence;
|
|
|
|
|
log_entry.execution_status = (m_use_shadow_logging ? "SHADOW" : "EXECUTED");
|
|
|
|
|
|
|
|
|
|
if(m_shadow_logger.IsInitialized())
|
|
|
|
|
m_shadow_logger.LogGateDecision(log_entry);
|
|
|
|
|
|
|
|
|
|
// Soft gating: we still execute/build the decision, but expose soft reasons for auditing
|
|
|
|
|
block_reason = soft_reasons;
|
|
|
|
|
decision.signal_id = signal.strategy_name;
|
|
|
|
|
decision.strategy = (signal.strategy != "") ? signal.strategy : signal.strategy_name; // CRITICAL: Set strategy name
|
|
|
|
|
decision.symbol = (signal.symbol != "") ? signal.symbol : _Symbol;
|
|
|
|
|
decision.order_type = (signal.type == 0) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
|
|
|
|
|
// FIX: Use price/sl/tp fields which are set by signal generators
|
|
|
|
|
// entry_price/stop_loss/take_profit are alternate names for same data
|
|
|
|
|
decision.final_price = (signal.entry_price > 0) ? signal.entry_price : signal.price;
|
|
|
|
|
decision.final_sl = (signal.stop_loss > 0) ? signal.stop_loss : signal.sl;
|
|
|
|
|
decision.final_tp = (signal.take_profit > 0) ? signal.take_profit : signal.tp;
|
|
|
|
|
decision.final_volume = (signal.volume > 0) ? signal.volume : 0.1; // Use signal volume or default
|
|
|
|
|
decision.final_confidence = signal.confidence;
|
|
|
|
|
decision.is_adjusted = (soft_reasons != "");
|
|
|
|
|
decision.block_reason = soft_reasons;
|
|
|
|
|
|
|
|
|
|
// Performance tracking
|
|
|
|
|
ulong total_latency = GetMicrosecondCount() - start_time;
|
|
|
|
|
m_total_signals++;
|
|
|
|
|
m_passed_signals++; // In adjust-only mode, all execution-valid signals are considered passed
|
|
|
|
|
|
|
|
|
|
// Update average latency (EMA)
|
|
|
|
|
if(m_avg_latency_ms == 0)
|
|
|
|
|
m_avg_latency_ms = (double)total_latency / 1000.0;
|
|
|
|
|
else
|
|
|
|
|
m_avg_latency_ms = m_avg_latency_ms * 0.9 + (double)total_latency / 1000.0 * 0.1;
|
|
|
|
|
|
|
|
|
|
// Log performance warning if slow
|
|
|
|
|
if(total_latency > 10000) // 10ms
|
|
|
|
|
Print(StringFormat("[GateMgr] WARNING: Slow processing: %.2fms", (double)total_latency/1000.0));
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Statistics
|
|
|
|
|
void PrintStats()
|
|
|
|
|
{
|
|
|
|
|
Print("=== Enhanced Gate System Statistics ===");
|
|
|
|
|
Print(StringFormat("Total Signals: %I64u | Passed: %I64u (%.1f%%)",
|
|
|
|
|
m_total_signals, m_passed_signals,
|
|
|
|
|
m_total_signals > 0 ? (double)m_passed_signals / m_total_signals * 100 : 0.0));
|
|
|
|
|
Print(StringFormat("Avg Latency: %.2fms", m_avg_latency_ms));
|
|
|
|
|
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
m_gates[i].PrintStats();
|
|
|
|
|
|
|
|
|
|
m_shadow_logger.PrintStats();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getters
|
|
|
|
|
bool IsShadowMode() const { return m_use_shadow_logging; }
|
|
|
|
|
CShadowLogger* GetShadowLogger() { return GetPointer(m_shadow_logger); }
|
|
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
|
void Shutdown()
|
|
|
|
|
{
|
|
|
|
|
Print("[GateMgr] Shutdown complete");
|
|
|
|
|
m_shadow_logger.PrintStats();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Update thresholds from learning system |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateFromLearning()
|
|
|
|
|
{
|
|
|
|
|
// Update gate thresholds based on learning/performance data
|
|
|
|
|
for(int i=0; i<8; i++)
|
|
|
|
|
{
|
|
|
|
|
if(m_gates[i].IsAutoTuneEnabled())
|
|
|
|
|
{
|
|
|
|
|
m_gates[i].UpdateThresholdFromLearning();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Print("[GateMgr] Updated thresholds from learning");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Individual Gate Processing Methods |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool ProcessGate1(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g1_signal.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate2(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g2_market.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate3(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g3_strategy.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate4(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g4_risk.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate5(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g5_performance.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate6(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g6_ml.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate7(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g7_live.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProcessGate8(TradingSignal &signal)
|
|
|
|
|
{
|
|
|
|
|
EGateResult result;
|
|
|
|
|
return m_g8_final.Validate(signal, result);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Compatibility macro for easy migration
|
|
|
|
|
#define CEfficientGateManager CEfficientGateManagerEnhanced
|
|
|
|
|
|
|
|
|
|
#endif // __ENHANCED_EFFICIENT_GATE_SYSTEM_MQH__
|