//+------------------------------------------------------------------+
//|                                       CorrelationMonitorDemo.mq5 |
//+------------------------------------------------------------------+

#property strict

#include <EWCovariance/EWCovariance.mqh>
#include <EWCovariance/HeatmapRenderer.mqh>

//--- Input parameters
input string InpSymbol1 = "EURUSD";
input string InpSymbol2 = "GBPUSD";
input string InpSymbol3 = "USDJPY";
input string InpSymbol4 = "XAUUSD";
input string InpSymbol5 = "USDCHF";
input double InpLambda  = 0.94;
input int    InpMinObs  = 30;

//--- Module-scope state
#define N_SYMBOLS 5

string            g_symbols[N_SYMBOLS];
double            g_prev_close[N_SYMBOLS];
datetime          g_last_bar_time[N_SYMBOLS];
CEWCovariance     g_cov;
CHeatmapRenderer  g_renderer;

//+------------------------------------------------------------------+
//| Prints the structured correlation table to the Experts tab       |
//+------------------------------------------------------------------+
void PrintCorrelationTable(void)
  {
   Print("--- Correlation table (obs=", g_cov.GetObsCount(),
         ", valid=", (g_cov.IsValid() ? "true" : "false"), ") ---");
   for(int i = 0; i < N_SYMBOLS; i++)
     {
      for(int j = i + 1; j < N_SYMBOLS; j++)
        {
         double corr = g_cov.GetCorrelation(i, j);
         Print(g_symbols[i], "-", g_symbols[j], ": ", DoubleToString(corr, 4));
        }
     }
  }

//+------------------------------------------------------------------+
//| Computes log returns for all five symbols on new bar close       |
//| and feeds them through the covariance update                     |
//+------------------------------------------------------------------+
void ProcessNewBars(void)
  {
   double returns[N_SYMBOLS];
   bool   have_new_bar = false;

   for(int i = 0; i < N_SYMBOLS; i++)
     {
      datetime bar_time = iTime(g_symbols[i], PERIOD_CURRENT, 0);
      if(bar_time != g_last_bar_time[i])
         have_new_bar = true;
     }

   if(!have_new_bar)
      return;

   for(int i = 0; i < N_SYMBOLS; i++)
     {
      double close_now = iClose(g_symbols[i], PERIOD_CURRENT, 1);
      datetime bar_time = iTime(g_symbols[i], PERIOD_CURRENT, 0);

      if(g_prev_close[i] > 0.0 && close_now > 0.0)
         returns[i] = MathLog(close_now / g_prev_close[i]);
      else
         returns[i] = 0.0;

      g_prev_close[i]    = close_now;
      g_last_bar_time[i] = bar_time;
     }

   g_cov.Update(returns);
   g_renderer.Render(g_cov, g_cov.GetObsCount(), g_cov.IsValid());
   PrintCorrelationTable();
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   g_symbols[0] = InpSymbol1;
   g_symbols[1] = InpSymbol2;
   g_symbols[2] = InpSymbol3;
   g_symbols[3] = InpSymbol4;
   g_symbols[4] = InpSymbol5;

   for(int i = 0; i < N_SYMBOLS; i++)
     {
      if(!SymbolSelect(g_symbols[i], true))
         Print("OnInit - warning: could not select symbol ", g_symbols[i]);

      g_last_bar_time[i] = iTime(g_symbols[i], PERIOD_CURRENT, 0);
      g_prev_close[i]    = iClose(g_symbols[i], PERIOD_CURRENT, 1);
     }

   if(!g_cov.Init(N_SYMBOLS, InpLambda, InpMinObs))
     {
      Print("OnInit - CEWCovariance::Init failed");
      return(INIT_FAILED);
     }

   if(!g_renderer.Init(N_SYMBOLS, g_symbols, 10, 10, 60))
     {
      Print("OnInit - CHeatmapRenderer::Init failed");
      return(INIT_FAILED);
     }

   g_renderer.Render(g_cov, g_cov.GetObsCount(), g_cov.IsValid());
   EventSetTimer(10);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
   g_renderer.Destroy();
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   ProcessNewBars();
  }

//+------------------------------------------------------------------+
//| Timer function - refreshes the heatmap without a new bar         |
//+------------------------------------------------------------------+
void OnTimer()
  {
   g_renderer.Render(g_cov, g_cov.GetObsCount(), g_cov.IsValid());
  }
//+------------------------------------------------------------------+