607 lignes
20 Kio
MQL5
607 lignes
20 Kio
MQL5
//+------------------------------------------------------------------+
|
|
//| CInsightGateBridge.mqh - Real-time O(1) Gate-Insights Integration |
|
|
//| P1-2: Bridge IncrementalInsightEngine with GateManager |
|
|
//| Eliminates file I/O latency with in-memory O(1) lookups |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef CINSIGHTGATEBRIDGE_MQH
|
|
#define CINSIGHTGATEBRIDGE_MQH
|
|
|
|
#include "CSQLiteKnowledgeBase.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Real-time Strategy Metrics Structure |
|
|
//+------------------------------------------------------------------+
|
|
struct SStrategyMetrics
|
|
{
|
|
string strategy_name;
|
|
string symbol;
|
|
datetime last_updated;
|
|
|
|
// Core metrics
|
|
int total_trades;
|
|
int win_count;
|
|
int loss_count;
|
|
double win_rate;
|
|
double profit_factor;
|
|
double expectancy;
|
|
double avg_pnl;
|
|
double max_drawdown;
|
|
double r_multiple_avg;
|
|
|
|
// Recent performance (last N trades)
|
|
double recent_win_rate;
|
|
int recent_trades_count;
|
|
|
|
// Regime-specific performance
|
|
string best_regime;
|
|
string worst_regime;
|
|
double regime_performance[5]; // For 5 different regimes
|
|
|
|
void Reset()
|
|
{
|
|
strategy_name = "";
|
|
symbol = "";
|
|
last_updated = 0;
|
|
total_trades = 0;
|
|
win_count = 0;
|
|
loss_count = 0;
|
|
win_rate = 0.0;
|
|
profit_factor = 0.0;
|
|
expectancy = 0.0;
|
|
avg_pnl = 0.0;
|
|
max_drawdown = 0.0;
|
|
r_multiple_avg = 0.0;
|
|
recent_win_rate = 0.0;
|
|
recent_trades_count = 0;
|
|
best_regime = "";
|
|
worst_regime = "";
|
|
ArrayInitialize(regime_performance, 0.0);
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Insight-Gate Bridge Class |
|
|
//+------------------------------------------------------------------+
|
|
class CInsightGateBridge
|
|
{
|
|
private:
|
|
// O(1) lookup tables
|
|
SStrategyMetrics m_metrics_cache[];
|
|
string m_cache_keys[]; // Composite key: "strategy|symbol"
|
|
int m_cache_size;
|
|
int m_max_cache_size;
|
|
|
|
// Update tracking
|
|
datetime m_last_db_sync;
|
|
int m_sync_interval_seconds;
|
|
|
|
// SQLite KB reference
|
|
CSQLiteKnowledgeBase* m_sqlite_kb;
|
|
bool m_use_sqlite;
|
|
|
|
// Performance tracking
|
|
ulong m_lookup_count;
|
|
ulong m_cache_hit_count;
|
|
double m_avg_lookup_time_us;
|
|
|
|
public:
|
|
// Constructor
|
|
CInsightGateBridge(CSQLiteKnowledgeBase* kb = NULL)
|
|
{
|
|
m_cache_size = 0;
|
|
m_max_cache_size = 100; // Max strategies to cache
|
|
m_last_db_sync = 0;
|
|
m_sync_interval_seconds = 60; // Sync every minute
|
|
m_sqlite_kb = kb;
|
|
m_use_sqlite = (kb != NULL);
|
|
m_lookup_count = 0;
|
|
m_cache_hit_count = 0;
|
|
m_avg_lookup_time_us = 0.0;
|
|
|
|
ArrayResize(m_metrics_cache, 0);
|
|
ArrayResize(m_cache_keys, 0);
|
|
}
|
|
|
|
// Destructor
|
|
~CInsightGateBridge()
|
|
{
|
|
// Don't delete m_sqlite_kb - it's owned by caller
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize bridge |
|
|
//+------------------------------------------------------------------+
|
|
bool Initialize(CSQLiteKnowledgeBase* kb = NULL)
|
|
{
|
|
if(kb != NULL) m_sqlite_kb = kb;
|
|
m_use_sqlite = (m_sqlite_kb != NULL);
|
|
|
|
// Pre-load hot strategies
|
|
if(m_use_sqlite)
|
|
{
|
|
PreloadHotStrategies();
|
|
}
|
|
|
|
Print("[InsightGateBridge] Initialized - O(1) lookup ready");
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Build composite key |
|
|
//+------------------------------------------------------------------+
|
|
string BuildKey(string strategy, string symbol)
|
|
{
|
|
return strategy + "|" + symbol;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Find cache index (O(n) but n <= 100) |
|
|
//+------------------------------------------------------------------+
|
|
int FindCacheIndex(string key)
|
|
{
|
|
for(int i = 0; i < m_cache_size; i++)
|
|
{
|
|
if(m_cache_keys[i] == key) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| O(1) Get metrics with automatic cache refresh |
|
|
//+------------------------------------------------------------------+
|
|
bool GetStrategyMetrics(string strategy, string symbol, SStrategyMetrics &metrics)
|
|
{
|
|
ulong start_time = GetMicrosecondCount();
|
|
|
|
string key = BuildKey(strategy, symbol);
|
|
int idx = FindCacheIndex(key);
|
|
|
|
bool cache_hit = (idx >= 0);
|
|
bool needs_refresh = false;
|
|
|
|
if(cache_hit)
|
|
{
|
|
// Check if cache entry is stale
|
|
datetime age = TimeCurrent() - m_metrics_cache[idx].last_updated;
|
|
if(age > m_sync_interval_seconds)
|
|
{
|
|
needs_refresh = true;
|
|
}
|
|
else
|
|
{
|
|
// Cache hit - return cached data
|
|
metrics = m_metrics_cache[idx];
|
|
m_cache_hit_count++;
|
|
}
|
|
}
|
|
|
|
if(!cache_hit || needs_refresh)
|
|
{
|
|
// Cache miss or stale - load from database
|
|
if(!LoadMetricsFromDB(strategy, symbol, metrics))
|
|
{
|
|
// Failed to load - return empty metrics
|
|
metrics.Reset();
|
|
return false;
|
|
}
|
|
|
|
// Update cache
|
|
if(idx >= 0)
|
|
{
|
|
// Update existing entry
|
|
m_metrics_cache[idx] = metrics;
|
|
}
|
|
else
|
|
{
|
|
// Add new entry (LRU eviction if needed)
|
|
if(m_cache_size >= m_max_cache_size)
|
|
{
|
|
EvictLRUEntry();
|
|
}
|
|
|
|
idx = m_cache_size;
|
|
ArrayResize(m_metrics_cache, m_cache_size + 1);
|
|
ArrayResize(m_cache_keys, m_cache_size + 1);
|
|
m_cache_keys[idx] = key;
|
|
m_metrics_cache[idx] = metrics;
|
|
m_cache_size++;
|
|
}
|
|
}
|
|
|
|
// Update performance metrics
|
|
m_lookup_count++;
|
|
ulong lookup_time = GetMicrosecondCount() - start_time;
|
|
m_avg_lookup_time_us = (m_avg_lookup_time_us * (m_lookup_count - 1) + lookup_time) / m_lookup_count;
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Load metrics from SQLite database |
|
|
//+------------------------------------------------------------------+
|
|
bool LoadMetricsFromDB(string strategy, string symbol, SStrategyMetrics &metrics)
|
|
{
|
|
if(!m_use_sqlite || m_sqlite_kb == NULL) return false;
|
|
|
|
// Use SQLite KB to get stats
|
|
double win_rate, profit_factor, total_pnl;
|
|
int total_trades;
|
|
|
|
if(!m_sqlite_kb.GetStrategyStats(strategy, symbol, win_rate, profit_factor, total_pnl, total_trades))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Populate metrics
|
|
metrics.strategy_name = strategy;
|
|
metrics.symbol = symbol;
|
|
metrics.win_rate = win_rate;
|
|
metrics.profit_factor = profit_factor;
|
|
metrics.avg_pnl = (total_trades > 0) ? total_pnl / total_trades : 0.0;
|
|
metrics.total_trades = total_trades;
|
|
metrics.last_updated = TimeCurrent();
|
|
|
|
// Get recent trades for recent performance
|
|
STradeRecord recent_trades[];
|
|
int recent_count = m_sqlite_kb.GetRecentTrades(strategy, symbol, recent_trades, 20);
|
|
|
|
if(recent_count > 0)
|
|
{
|
|
int recent_wins = 0;
|
|
double recent_pnl = 0;
|
|
|
|
for(int i = 0; i < recent_count; i++)
|
|
{
|
|
if(recent_trades[i].is_winner) recent_wins++;
|
|
recent_pnl += recent_trades[i].pnl;
|
|
}
|
|
|
|
metrics.recent_win_rate = (double)recent_wins / recent_count;
|
|
metrics.recent_trades_count = recent_count;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Preload historical strategy data into cache |
|
|
//+------------------------------------------------------------------+
|
|
void PreloadHistoricalData()
|
|
{
|
|
if(!m_use_sqlite || m_sqlite_kb == NULL)
|
|
{
|
|
Print("[InsightGateBridge] No SQLite KB available for historical preload");
|
|
return;
|
|
}
|
|
|
|
Print("[InsightGateBridge] Preloading historical strategy data...");
|
|
|
|
// Get all strategies from knowledge base
|
|
string strategies[];
|
|
int strategy_count = m_sqlite_kb.GetUniqueStrategies(strategies);
|
|
|
|
if(strategy_count == 0)
|
|
{
|
|
Print("[InsightGateBridge] No historical strategies found in SQLite KB");
|
|
|
|
// CRITICAL: Try to import from CSV if SQLite is empty
|
|
Print("[InsightGateBridge] Attempting to import from CSV files...");
|
|
|
|
// Try trades CSV files first (actual format used by EA)
|
|
int imported = m_sqlite_kb.ImportFromTradesCSV();
|
|
|
|
if(imported == 0)
|
|
{
|
|
// Fallback to legacy knowledge_base.csv
|
|
string csv_path = TerminalInfoString(TERMINAL_COMMONDATA_PATH) + "\\Files\\DualEA\\knowledge_base.csv";
|
|
imported = m_sqlite_kb.ImportFromCSV(csv_path);
|
|
}
|
|
|
|
if(imported > 0)
|
|
{
|
|
Print(StringFormat("[InsightGateBridge] Successfully imported %d trades from CSV", imported));
|
|
|
|
// Try again to get strategies after import
|
|
strategy_count = m_sqlite_kb.GetUniqueStrategies(strategies);
|
|
if(strategy_count == 0)
|
|
{
|
|
Print("[InsightGateBridge] Still no strategies found after CSV import");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Print("[InsightGateBridge] CSV import failed or no CSV data available");
|
|
return;
|
|
}
|
|
}
|
|
|
|
Print(StringFormat("[InsightGateBridge] Found %d strategies in KB", strategy_count));
|
|
|
|
// Get all symbols from knowledge base
|
|
string symbols[];
|
|
int symbol_count = m_sqlite_kb.GetUniqueSymbols(symbols);
|
|
|
|
if(symbol_count == 0)
|
|
{
|
|
Print("[InsightGateBridge] No symbols found in KB");
|
|
return;
|
|
}
|
|
|
|
Print(StringFormat("[InsightGateBridge] Found %d symbols in KB", symbol_count));
|
|
|
|
// Preload each strategy/symbol combination
|
|
int loaded_count = 0;
|
|
for(int s = 0; s < strategy_count; s++)
|
|
{
|
|
for(int sym = 0; sym < symbol_count; sym++)
|
|
{
|
|
SStrategyMetrics metrics;
|
|
if(LoadMetricsFromDB(strategies[s], symbols[sym], metrics))
|
|
{
|
|
string key = BuildKey(strategies[s], symbols[sym]);
|
|
|
|
// Check if already in cache
|
|
int existing_idx = FindCacheIndex(key);
|
|
if(existing_idx >= 0)
|
|
{
|
|
// Update existing
|
|
m_metrics_cache[existing_idx] = metrics;
|
|
}
|
|
else
|
|
{
|
|
// Add new entry
|
|
if(m_cache_size >= m_max_cache_size)
|
|
{
|
|
EvictLRUEntry();
|
|
}
|
|
|
|
int idx = m_cache_size;
|
|
ArrayResize(m_metrics_cache, m_cache_size + 1);
|
|
ArrayResize(m_cache_keys, m_cache_size + 1);
|
|
m_cache_keys[idx] = key;
|
|
m_metrics_cache[idx] = metrics;
|
|
m_cache_size++;
|
|
}
|
|
loaded_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Print(StringFormat("[InsightGateBridge] Preloaded %d strategy/symbol combinations", loaded_count));
|
|
Print(StringFormat("[InsightGateBridge] Cache now contains %d entries", m_cache_size));
|
|
|
|
// Update lookup count to reflect preloaded data
|
|
m_lookup_count = loaded_count;
|
|
m_cache_hit_count = loaded_count; // All preloaded data counts as cache hits
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Preload frequently used strategies |
|
|
//+------------------------------------------------------------------+
|
|
void PreloadHotStrategies()
|
|
{
|
|
// Enhanced preload: Load all historical data first
|
|
PreloadHistoricalData();
|
|
|
|
// Additional hot strategy logic can be added here if needed
|
|
Print("[InsightGateBridge] Preloading hot strategies...");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Evict least recently used entry |
|
|
//+------------------------------------------------------------------+
|
|
void EvictLRUEntry()
|
|
{
|
|
if(m_cache_size == 0) return;
|
|
|
|
// Find oldest entry
|
|
int oldest_idx = 0;
|
|
datetime oldest_time = m_metrics_cache[0].last_updated;
|
|
|
|
for(int i = 1; i < m_cache_size; i++)
|
|
{
|
|
if(m_metrics_cache[i].last_updated < oldest_time)
|
|
{
|
|
oldest_time = m_metrics_cache[i].last_updated;
|
|
oldest_idx = i;
|
|
}
|
|
}
|
|
|
|
// Remove oldest entry by swapping with last and resizing
|
|
if(oldest_idx < m_cache_size - 1)
|
|
{
|
|
m_metrics_cache[oldest_idx] = m_metrics_cache[m_cache_size - 1];
|
|
m_cache_keys[oldest_idx] = m_cache_keys[m_cache_size - 1];
|
|
}
|
|
|
|
m_cache_size--;
|
|
ArrayResize(m_metrics_cache, m_cache_size);
|
|
ArrayResize(m_cache_keys, m_cache_size);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Fast gate decision using cached insights |
|
|
//+------------------------------------------------------------------+
|
|
bool FastGateDecision(string strategy, string symbol,
|
|
double min_win_rate, double min_profit_factor,
|
|
string &block_reason)
|
|
{
|
|
SStrategyMetrics metrics;
|
|
|
|
// O(1) lookup
|
|
if(!GetStrategyMetrics(strategy, symbol, metrics))
|
|
{
|
|
block_reason = "No metrics available";
|
|
return false; // Conservative: block if no data
|
|
}
|
|
|
|
// Check win rate threshold
|
|
if(metrics.win_rate < min_win_rate)
|
|
{
|
|
block_reason = StringFormat("Win rate %.1f%% < %.1f%%",
|
|
metrics.win_rate * 100, min_win_rate * 100);
|
|
return false;
|
|
}
|
|
|
|
// Check profit factor
|
|
if(metrics.profit_factor < min_profit_factor)
|
|
{
|
|
block_reason = StringFormat("PF %.2f < %.2f",
|
|
metrics.profit_factor, min_profit_factor);
|
|
return false;
|
|
}
|
|
|
|
// Check recent performance degradation
|
|
if(metrics.recent_trades_count >= 5 &&
|
|
metrics.recent_win_rate < min_win_rate * 0.8)
|
|
{
|
|
block_reason = StringFormat("Recent WR %.1f%% degraded",
|
|
metrics.recent_win_rate * 100);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get confidence score for signal |
|
|
//+------------------------------------------------------------------+
|
|
double GetSignalConfidence(string strategy, string symbol)
|
|
{
|
|
SStrategyMetrics metrics;
|
|
|
|
if(!GetStrategyMetrics(strategy, symbol, metrics))
|
|
{
|
|
return 0.5; // Neutral if no data
|
|
}
|
|
|
|
// Calculate composite confidence score
|
|
double confidence = 0.5;
|
|
|
|
// Weight by historical win rate
|
|
confidence += (metrics.win_rate - 0.5) * 0.3;
|
|
|
|
// Weight by profit factor
|
|
if(metrics.profit_factor > 1.5)
|
|
confidence += 0.1;
|
|
else if(metrics.profit_factor < 1.0)
|
|
confidence -= 0.1;
|
|
|
|
// Weight by recent performance
|
|
if(metrics.recent_trades_count >= 5)
|
|
{
|
|
confidence += (metrics.recent_win_rate - 0.5) * 0.2;
|
|
}
|
|
|
|
// Normalize
|
|
confidence = MathMax(0.0, MathMin(1.0, confidence));
|
|
|
|
return confidence;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update metrics after trade completion |
|
|
//+------------------------------------------------------------------+
|
|
void OnTradeCompleted(string strategy, string symbol, double pnl, double r_multiple)
|
|
{
|
|
string key = BuildKey(strategy, symbol);
|
|
int idx = FindCacheIndex(key);
|
|
|
|
if(idx >= 0)
|
|
{
|
|
// Update cached metrics incrementally
|
|
m_metrics_cache[idx].total_trades++;
|
|
if(pnl > 0)
|
|
{
|
|
m_metrics_cache[idx].win_count++;
|
|
m_metrics_cache[idx].recent_win_rate = (m_metrics_cache[idx].recent_win_rate * m_metrics_cache[idx].recent_trades_count + 1.0) / (m_metrics_cache[idx].recent_trades_count + 1);
|
|
}
|
|
else
|
|
{
|
|
m_metrics_cache[idx].loss_count++;
|
|
m_metrics_cache[idx].recent_win_rate = (m_metrics_cache[idx].recent_win_rate * m_metrics_cache[idx].recent_trades_count + 0.0) / (m_metrics_cache[idx].recent_trades_count + 1);
|
|
}
|
|
|
|
m_metrics_cache[idx].recent_trades_count = MathMin(20, m_metrics_cache[idx].recent_trades_count + 1);
|
|
m_metrics_cache[idx].win_rate = (double)m_metrics_cache[idx].win_count / m_metrics_cache[idx].total_trades;
|
|
m_metrics_cache[idx].last_updated = TimeCurrent();
|
|
|
|
// Recalculate profit factor periodically
|
|
if(m_metrics_cache[idx].total_trades % 10 == 0)
|
|
{
|
|
LoadMetricsFromDB(strategy, symbol, m_metrics_cache[idx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get bridge performance stats |
|
|
//+------------------------------------------------------------------+
|
|
void GetStats(double &hit_rate, double &avg_lookup_us, int &cache_size)
|
|
{
|
|
hit_rate = (m_lookup_count > 0) ? (double)m_cache_hit_count / m_lookup_count : 0.0;
|
|
avg_lookup_us = m_avg_lookup_time_us;
|
|
cache_size = m_cache_size;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Force full cache refresh |
|
|
//+------------------------------------------------------------------+
|
|
void RefreshCache()
|
|
{
|
|
for(int i = 0; i < m_cache_size; i++)
|
|
{
|
|
string key = m_cache_keys[i];
|
|
string parts[];
|
|
if(StringSplit(key, '|', parts) == 2)
|
|
{
|
|
LoadMetricsFromDB(parts[0], parts[1], m_metrics_cache[i]);
|
|
}
|
|
}
|
|
m_last_db_sync = TimeCurrent();
|
|
Print("[InsightGateBridge] Cache refreshed");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Sync with database if needed |
|
|
//+------------------------------------------------------------------+
|
|
void OnTickSync()
|
|
{
|
|
if(TimeCurrent() - m_last_db_sync > m_sync_interval_seconds)
|
|
{
|
|
RefreshCache();
|
|
}
|
|
}
|
|
};
|
|
|
|
// Global bridge instance
|
|
CInsightGateBridge* g_insight_gate_bridge = NULL;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize Insight-Gate Bridge |
|
|
//+------------------------------------------------------------------+
|
|
bool InitializeInsightGateBridge(CSQLiteKnowledgeBase* kb = NULL)
|
|
{
|
|
if(g_insight_gate_bridge != NULL)
|
|
{
|
|
delete g_insight_gate_bridge;
|
|
}
|
|
|
|
g_insight_gate_bridge = new CInsightGateBridge(kb);
|
|
return g_insight_gate_bridge.Initialize(kb);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Shutdown Insight-Gate Bridge |
|
|
//+------------------------------------------------------------------+
|
|
void ShutdownInsightGateBridge()
|
|
{
|
|
if(g_insight_gate_bridge != NULL)
|
|
{
|
|
delete g_insight_gate_bridge;
|
|
g_insight_gate_bridge = NULL;
|
|
}
|
|
}
|
|
|
|
#endif // CINSIGHTGATEBRIDGE_MQH
|