//+------------------------------------------------------------------+ //| 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