263 righe
7,6 KiB
MQL5
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
|