mql5/Experts/Advisors/DualEA/Include/CGateAudit.mqh
2026-02-24 12:47:37 -05:00

263 righe
7,6 KiB
MQL5

//+------------------------------------------------------------------+
//| CGateAudit.mqh - Gate System Audit Trail |
//| Tracks signal processing through gate pipeline |
//+------------------------------------------------------------------+
#ifndef CGATEAUDIT_MQH
#define CGATEAUDIT_MQH
//+------------------------------------------------------------------+
//| Gate Audit Log Entry |
//+------------------------------------------------------------------+
struct SGateAuditEntry
{
datetime timestamp;
string signal_id;
string strategy;
string gate_name;
bool passed;
double confidence;
double threshold;
string reason;
};
//+------------------------------------------------------------------+
//| Gate Audit Class |
//+------------------------------------------------------------------+
class CGateAudit
{
private:
SGateAuditEntry m_entries[];
int m_entry_count;
int m_max_entries;
bool m_enabled;
int m_min_signals_expected;
int m_min_uptime_minutes;
datetime m_start_time;
datetime m_last_alert_time;
int m_alert_cooldown_minutes;
string m_expected_strategies[];
int m_expected_strategy_count;
public:
CGateAudit()
{
m_entry_count = 0;
m_max_entries = 10000;
m_enabled = false;
m_min_signals_expected = 10;
m_min_uptime_minutes = 5;
m_start_time = TimeCurrent();
m_last_alert_time = 0;
m_alert_cooldown_minutes = 15;
m_expected_strategy_count = 0;
ArrayResize(m_entries, 0);
ArrayResize(m_expected_strategies, 0);
}
~CGateAudit()
{
ArrayResize(m_entries, 0);
ArrayResize(m_expected_strategies, 0);
}
bool Initialize(const string strategies_csv = "")
{
m_start_time = TimeCurrent();
if(strategies_csv != "")
SetExpectedStrategies(strategies_csv);
Print("[GateAudit] Initialized");
return true;
}
void Configure(int min_signals, int min_uptime_min, int alert_cooldown_min)
{
m_min_signals_expected = min_signals;
m_min_uptime_minutes = min_uptime_min;
m_alert_cooldown_minutes = alert_cooldown_min;
}
void SetEnabled(bool enabled) { m_enabled = enabled; }
bool IsEnabled() const { return m_enabled; }
void LogStrategyProcessed(const string strategy_name, const string signal_id,
bool passed, double confidence, const string gate_name)
{
if(!m_enabled) return;
int idx = m_entry_count;
if(idx >= m_max_entries)
{
// Shift entries to make room
for(int i = 0; i < m_max_entries - 1; i++)
m_entries[i] = m_entries[i + 1];
idx = m_max_entries - 1;
}
else
{
ArrayResize(m_entries, idx + 1);
m_entry_count++;
}
m_entries[idx].timestamp = TimeCurrent();
m_entries[idx].signal_id = signal_id;
m_entries[idx].strategy = strategy_name;
m_entries[idx].gate_name = gate_name;
m_entries[idx].passed = passed;
m_entries[idx].confidence = confidence;
m_entries[idx].threshold = 0.0;
m_entries[idx].reason = passed ? "PASS" : "BLOCK";
}
void LogGateDecision(const string gate_name, const string signal_id,
bool passed, double confidence, double threshold,
const string reason)
{
if(!m_enabled) return;
int idx = m_entry_count;
if(idx >= m_max_entries)
{
for(int i = 0; i < m_max_entries - 1; i++)
m_entries[i] = m_entries[i + 1];
idx = m_max_entries - 1;
}
else
{
ArrayResize(m_entries, idx + 1);
m_entry_count++;
}
m_entries[idx].timestamp = TimeCurrent();
m_entries[idx].signal_id = signal_id;
m_entries[idx].strategy = "";
m_entries[idx].gate_name = gate_name;
m_entries[idx].passed = passed;
m_entries[idx].confidence = confidence;
m_entries[idx].threshold = threshold;
m_entries[idx].reason = reason;
}
bool RunAudit(string &skipped_strategies, int &skipped_count)
{
if(!m_enabled) return false;
skipped_strategies = "";
skipped_count = 0;
datetime now = TimeCurrent();
int uptime_minutes = (int)((now - m_start_time) / 60);
// Check if enough signals have been processed
if(uptime_minutes >= m_min_uptime_minutes)
{
if(m_entry_count < m_min_signals_expected)
{
// Check cooldown
if(m_last_alert_time == 0 ||
(now - m_last_alert_time) / 60 >= m_alert_cooldown_minutes)
{
Print("[GateAudit] ALERT: Only " + IntegerToString(m_entry_count) +
" signals processed in " + IntegerToString(uptime_minutes) +
" minutes (expected: " + IntegerToString(m_min_signals_expected) + ")");
m_last_alert_time = now;
return false;
}
}
}
return true;
}
int GetProcessedSignalCount() const { return m_entry_count; }
int GetPassCount()
{
int count = 0;
for(int i = 0; i < m_entry_count; i++)
{
if(m_entries[i].passed)
count++;
}
return count;
}
int GetBlockCount()
{
int count = 0;
for(int i = 0; i < m_entry_count; i++)
{
if(!m_entries[i].passed)
count++;
}
return count;
}
void GetAuditReport(string &report)
{
report = "=== Gate Audit Report ===\n";
report += "Total entries: " + IntegerToString(m_entry_count) + "\n";
report += "Passed: " + IntegerToString(GetPassCount()) + "\n";
report += "Blocked: " + IntegerToString(GetBlockCount()) + "\n";
datetime now = TimeCurrent();
int uptime_minutes = (int)((now - m_start_time) / 60);
report += "Uptime: " + IntegerToString(uptime_minutes) + " minutes\n";
}
void Clear()
{
ArrayResize(m_entries, 0);
m_entry_count = 0;
}
void SetExpectedStrategies(const string &strategies_csv)
{
// Parse comma-separated strategy names
string temp = strategies_csv;
m_expected_strategy_count = 0;
ArrayResize(m_expected_strategies, 0);
int pos = 0;
while(StringFind(temp, ",", pos) != -1 || StringLen(temp) > 0)
{
int comma_pos = StringFind(temp, ",", pos);
string strategy;
if(comma_pos == -1)
{
strategy = StringSubstr(temp, pos);
temp = "";
}
else
{
strategy = StringSubstr(temp, pos, comma_pos - pos);
pos = comma_pos + 1;
}
StringTrimLeft(strategy);
StringTrimRight(strategy);
if(StringLen(strategy) > 0)
{
int idx = m_expected_strategy_count;
ArrayResize(m_expected_strategies, idx + 1);
m_expected_strategies[idx] = strategy;
m_expected_strategy_count++;
}
}
}
int GetExpectedStrategyCount() const { return m_expected_strategy_count; }
string GetExpectedStrategy(int index) const
{
if(index >= 0 && index < m_expected_strategy_count)
return m_expected_strategies[index];
return "";
}
};
// Global instance for PaperEA
CGateAudit g_gate_audit;
#endif // CGATEAUDIT_MQH