282 lines
9.8 KiB
MQL5
282 lines
9.8 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| CGateBase.mqh - Abstract Gate Interface |
| |||
//| Based on chat-8Stage Gate System Integration Guide |
| |||
//| Enhances modularity with shadow logging & auto-tuning support |
| |||
//+------------------------------------------------------------------+
| |||
#ifndef CGATEBASE_MQH
| |||
#define CGATEBASE_MQH
| |||
| |||
#include <Arrays/ArrayLong.mqh>
| |||
#include "IStrategy.mqh"
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Gate Result Structure |
| |||
//+------------------------------------------------------------------+
| |||
struct EGateResult
| |||
{
| |||
bool passed; // Gate passed?
| |||
double confidence; // Confidence score (0.0-1.0)
| |||
double threshold; // Applied threshold
| |||
string block_reason; // Why blocked (if failed)
| |||
ulong latency_us; // Execution time in microseconds
| |||
| |||
void Set(bool p, double c, double t, string r="", ulong l=0)
| |||
{
| |||
passed = p;
| |||
confidence = c;
| |||
threshold = t;
| |||
block_reason = r;
| |||
latency_us = l;
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Auto-Tuning Configuration |
| |||
//+------------------------------------------------------------------+
| |||
struct SGateAutoTuneConfig
| |||
{
| |||
bool enabled; // Auto-tuning active?
| |||
double min_threshold; // Minimum allowed threshold
| |||
double max_threshold; // Maximum allowed threshold
| |||
int lookback_trades; // Trades to analyze
| |||
double target_winrate; // Target win rate for optimization
| |||
double learning_rate; // Adjustment speed (0.01-0.1)
| |||
| |||
void Init(double min_t=0.3, double max_t=0.9, int lookback=20,
| |||
double target_wr=0.55, double lr=0.05)
| |||
{
| |||
enabled = true;
| |||
min_threshold = min_t;
| |||
max_threshold = max_t;
| |||
lookback_trades = lookback;
| |||
target_winrate = target_wr;
| |||
learning_rate = lr;
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Abstract Gate Base Class |
| |||
//+------------------------------------------------------------------+
| |||
class CGateBase
| |||
{
| |||
protected:
| |||
string m_name; // Gate name
| |||
bool m_enabled; // Gate enabled?
| |||
bool m_shadow_mode; // Shadow logging only?
| |||
double m_threshold; // Current threshold
| |||
double m_default_threshold; // Default/fallback threshold
| |||
| |||
// Auto-tuning
| |||
SGateAutoTuneConfig m_autotune; // Auto-tuning config
| |||
double m_trade_history[]; // Trade PnL history for learning
| |||
bool m_pass_history[]; // Gate pass/fail history
| |||
| |||
// Performance tracking
| |||
ulong m_total_evaluations; // Total validations
| |||
ulong m_passed_count; // Passed count
| |||
double m_avg_latency_us; // Average latency
| |||
| |||
public:
| |||
// Constructor
| |||
CGateBase(string name)
| |||
: m_name(name),
| |||
m_enabled(true),
| |||
m_shadow_mode(false),
| |||
m_threshold(0.5),
| |||
m_default_threshold(0.5),
| |||
m_total_evaluations(0),
| |||
m_passed_count(0),
| |||
m_avg_latency_us(0.0)
| |||
{
| |||
ArrayResize(m_trade_history, 0);
| |||
ArrayResize(m_pass_history, 0);
| |||
}
| |||
| |||
// Virtual destructor
| |||
virtual ~CGateBase() {}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Core Interface (MUST OVERRIDE) |
| |||
//+------------------------------------------------------------------+
| |||
virtual bool Initialize() { return true; }
| |||
virtual bool Validate(TradingSignal &signal, EGateResult &result) = 0;
| |||
virtual double CalculateConfidence(const TradingSignal &signal) = 0;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Configuration Interface |
| |||
//+------------------------------------------------------------------+
| |||
void Enable(bool enable) { m_enabled = enable; }
| |||
void SetShadowMode(bool shadow) { m_shadow_mode = shadow; }
| |||
void SetThreshold(double threshold)
| |||
{
| |||
m_threshold = MathMax(0.0, MathMin(1.0, threshold));
| |||
}
| |||
void SetDefaultThreshold(double threshold)
| |||
{
| |||
m_default_threshold = MathMax(0.0, MathMin(1.0, threshold));
| |||
if(m_threshold == 0.0) m_threshold = m_default_threshold;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Auto-Tuning Interface |
| |||
//+------------------------------------------------------------------+
| |||
void EnableAutoTune(const SGateAutoTuneConfig &config)
| |||
{
| |||
m_autotune = config;
| |||
}
| |||
| |||
void DisableAutoTune() { m_autotune.enabled = false; }
| |||
| |||
void RecordTradeOutcome(double pnl, bool gate_passed)
| |||
{
| |||
// Add to history ring buffer
| |||
int size = ArraySize(m_trade_history);
| |||
if(size >= m_autotune.lookback_trades)
| |||
{
| |||
// Shift array (ring buffer)
| |||
for(int i=0; i<size-1; i++)
| |||
{
| |||
m_trade_history[i] = m_trade_history[i+1];
| |||
m_pass_history[i] = m_pass_history[i+1];
| |||
}
| |||
m_trade_history[size-1] = pnl;
| |||
m_pass_history[size-1] = gate_passed;
| |||
}
| |||
else
| |||
{
| |||
ArrayResize(m_trade_history, size+1);
| |||
ArrayResize(m_pass_history, size+1);
| |||
m_trade_history[size] = pnl;
| |||
m_pass_history[size] = gate_passed;
| |||
}
| |||
| |||
// Trigger auto-tuning if enough data
| |||
if(m_autotune.enabled && size >= m_autotune.lookback_trades/2)
| |||
{
| |||
AutoTuneThreshold();
| |||
}
| |||
}
| |||
| |||
void AutoTuneThreshold()
| |||
{
| |||
if(!m_autotune.enabled) return;
| |||
| |||
int size = ArraySize(m_trade_history);
| |||
if(size < 5) return; // Need minimum data
| |||
| |||
// Calculate win rate for trades that passed this gate
| |||
int wins = 0, total_passed = 0;
| |||
double total_pnl = 0;
| |||
| |||
for(int i=0; i<size; i++)
| |||
{
| |||
if(m_pass_history[i]) // Gate was passed
| |||
{
| |||
total_passed++;
| |||
if(m_trade_history[i] > 0) wins++;
| |||
total_pnl += m_trade_history[i];
| |||
}
| |||
}
| |||
| |||
if(total_passed == 0) return;
| |||
| |||
double winrate = (double)wins / total_passed;
| |||
double profit_factor = total_pnl > 0 ? total_pnl / MathAbs(total_pnl) : 0;
| |||
| |||
// Adjust threshold based on performance
| |||
double adjustment = 0.0;
| |||
if(winrate < m_autotune.target_winrate * 0.9)
| |||
{
| |||
// Win rate too low - make threshold stricter
| |||
adjustment = m_autotune.learning_rate;
| |||
}
| |||
else if(winrate > m_autotune.target_winrate * 1.1 && profit_factor > 1.2)
| |||
{
| |||
// Win rate high and profitable - can relax threshold
| |||
adjustment = -m_autotune.learning_rate;
| |||
}
| |||
| |||
// Apply adjustment with bounds
| |||
double new_threshold = m_threshold + adjustment;
| |||
new_threshold = MathMax(m_autotune.min_threshold,
| |||
MathMin(m_autotune.max_threshold, new_threshold));
| |||
| |||
if(MathAbs(new_threshold - m_threshold) > 0.001)
| |||
{
| |||
Print(StringFormat("[Gate:%s] Auto-tuned: %.3f -> %.3f (WR=%.2f%% PF=%.2f)",
| |||
m_name, m_threshold, new_threshold, winrate*100, profit_factor));
| |||
m_threshold = new_threshold;
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Performance Tracking |
| |||
//+------------------------------------------------------------------+
| |||
void RecordLatency(ulong latency_us)
| |||
{
| |||
// Exponential moving average
| |||
if(m_avg_latency_us == 0)
| |||
m_avg_latency_us = (double)latency_us;
| |||
else
| |||
m_avg_latency_us = m_avg_latency_us * 0.9 + (double)latency_us * 0.1;
| |||
| |||
m_total_evaluations++;
| |||
}
| |||
| |||
void RecordPass(bool passed)
| |||
{
| |||
if(passed) m_passed_count++;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Getters |
| |||
//+------------------------------------------------------------------+
| |||
string GetName() const { return m_name; }
| |||
bool IsEnabled() const { return m_enabled; }
| |||
bool IsShadowMode() const { return m_shadow_mode; }
| |||
double GetThreshold() const { return m_threshold; }
| |||
double GetDefaultThreshold() const { return m_default_threshold; }
| |||
double GetAvgLatency() const { return m_avg_latency_us; }
| |||
double GetPassRate() const
| |||
{
| |||
return m_total_evaluations > 0 ? (double)m_passed_count / m_total_evaluations : 0.0;
| |||
}
| |||
bool IsAutoTuneEnabled() const { return m_autotune.enabled; }
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Learning Interface |
| |||
//+------------------------------------------------------------------+
| |||
void UpdateThresholdFromLearning()
| |||
{
| |||
// Update threshold based on learning data
| |||
// This is called by the gate manager to refresh thresholds
| |||
if(m_autotune.enabled)
| |||
{
| |||
AutoTuneThreshold();
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Statistics |
| |||
//+------------------------------------------------------------------+
| |||
void PrintStats()
| |||
{
| |||
Print(StringFormat("[Gate:%s] Enabled=%s Evals=%I64u Passed=%I64u (%.1f%%) Latency=%.1fus Threshold=%.3f",
| |||
m_name,
| |||
m_enabled ? "YES" : "NO",
| |||
m_total_evaluations,
| |||
m_passed_count,
| |||
GetPassRate() * 100,
| |||
m_avg_latency_us,
| |||
m_threshold));
| |||
}
| |||
| |||
void ResetStats()
| |||
{
| |||
m_total_evaluations = 0;
| |||
m_passed_count = 0;
| |||
m_avg_latency_us = 0.0;
| |||
}
| |||
};
| |||
| |||
#endif // CGATEBASE_MQH
|