mql5/Experts/Advisors/DualEA/Include/CFeatureDriftDetector.mqh

190 lines
5.4 KiB
MQL5
Raw Permalink Normal View History

2026-02-24 12:47:37 -05:00
//+------------------------------------------------------------------+
//| CFeatureDriftDetector.mqh - Feature Drift Detection System |
//| P2-1: Concept drift auto-adjustment for gate system |
//+------------------------------------------------------------------+
#ifndef CFEATUREDRIFTDETECTOR_MQH
#define CFEATUREDRIFTDETECTOR_MQH
//+------------------------------------------------------------------+
//| Drift Statistics Structure |
//+------------------------------------------------------------------+
struct SDriftStats
{
string feature_name;
double mean;
double variance;
double min_val;
double max_val;
int sample_count;
datetime last_update;
bool drift_detected;
double drift_score;
};
//+------------------------------------------------------------------+
//| Feature Drift Detector Class |
//+------------------------------------------------------------------+
class CFeatureDriftDetector
{
private:
SDriftStats m_stats[];
int m_feature_count;
double m_drift_threshold;
int m_min_samples;
bool m_initialized;
int FindFeatureIndex(const string feature_name)
{
for(int i = 0; i < m_feature_count; i++)
{
if(m_stats[i].feature_name == feature_name)
return i;
}
return -1;
}
double CalculateZScore(double value, double mean, double variance)
{
if(variance < 1e-10) return 0.0;
return (value - mean) / MathSqrt(variance);
}
public:
CFeatureDriftDetector()
{
m_feature_count = 0;
m_drift_threshold = 3.0; // 3 sigma threshold
m_min_samples = 30;
m_initialized = false;
ArrayResize(m_stats, 0);
}
~CFeatureDriftDetector()
{
ArrayResize(m_stats, 0);
}
bool Initialize(double drift_threshold = 3.0, int min_samples = 30)
{
m_drift_threshold = drift_threshold;
m_min_samples = min_samples;
m_initialized = true;
Print("[FeatureDriftDetector] Initialized with threshold: " + DoubleToString(drift_threshold, 2));
return true;
}
void Shutdown()
{
ArrayResize(m_stats, 0);
m_feature_count = 0;
m_initialized = false;
}
void RecordSample(const string feature_name, double value)
{
int idx = FindFeatureIndex(feature_name);
if(idx < 0)
{
// Create new feature entry
idx = m_feature_count;
ArrayResize(m_stats, idx + 1);
m_stats[idx].feature_name = feature_name;
m_stats[idx].mean = value;
m_stats[idx].variance = 0.0;
m_stats[idx].min_val = value;
m_stats[idx].max_val = value;
m_stats[idx].sample_count = 1;
m_stats[idx].last_update = TimeCurrent();
m_stats[idx].drift_detected = false;
m_stats[idx].drift_score = 0.0;
m_feature_count++;
}
else
{
// Update existing statistics using Welford's online algorithm
SDriftStats &s = m_stats[idx];
s.sample_count++;
double delta = value - s.mean;
s.mean += delta / s.sample_count;
double delta2 = value - s.mean;
s.variance = ((s.sample_count - 1) * s.variance + delta * delta2) / s.sample_count;
s.min_val = MathMin(s.min_val, value);
s.max_val = MathMax(s.max_val, value);
s.last_update = TimeCurrent();
// Check for drift
if(s.sample_count >= m_min_samples)
{
double zscore = CalculateZScore(value, s.mean, s.variance);
s.drift_score = MathAbs(zscore);
s.drift_detected = (s.drift_score > m_drift_threshold);
}
}
}
bool IsDriftDetected(const string feature_name)
{
int idx = FindFeatureIndex(feature_name);
if(idx < 0) return false;
return m_stats[idx].drift_detected;
}
bool IsAnyDriftDetected()
{
for(int i = 0; i < m_feature_count; i++)
{
if(m_stats[i].drift_detected)
return true;
}
return false;
}
double GetDriftScore(const string feature_name)
{
int idx = FindFeatureIndex(feature_name);
if(idx < 0) return 0.0;
return m_stats[idx].drift_score;
}
void GetDriftReport(string &report)
{
report = "=== Feature Drift Report ===\n";
int drift_count = 0;
for(int i = 0; i < m_feature_count; i++)
{
if(m_stats[i].drift_detected)
{
drift_count++;
report += m_stats[i].feature_name + ": score=" + DoubleToString(m_stats[i].drift_score, 2) +
" (threshold=" + DoubleToString(m_drift_threshold, 2) + ")\n";
}
}
if(drift_count == 0)
report += "No drift detected in " + IntegerToString(m_feature_count) + " features\n";
}
void Reset()
{
for(int i = 0; i < m_feature_count; i++)
{
m_stats[i].drift_detected = false;
m_stats[i].drift_score = 0.0;
m_stats[i].sample_count = 0;
m_stats[i].mean = 0.0;
m_stats[i].variance = 0.0;
}
}
int GetFeatureCount() const { return m_feature_count; }
bool IsInitialized() const { return m_initialized; }
};
// Global instance for P0-P5 integration
CFeatureDriftDetector* g_feature_drift_detector = NULL;
#endif // CFEATUREDRIFTDETECTOR_MQH