//+------------------------------------------------------------------+
//|                                              SpreadMonitor.mqh   |
//+------------------------------------------------------------------+
#ifndef SPREADMONITOR_MQH
#define SPREADMONITOR_MQH

#include <Spread_Monitor\SpreadHistogram.mqh>

//+------------------------------------------------------------------+
//| Spread status classification, file scope                         |
//+------------------------------------------------------------------+
enum ENUM_SPREAD_STATUS
  {
   STATUS_GREEN,
   STATUS_YELLOW,
   STATUS_RED,
   STATUS_WARMING
  };
//+------------------------------------------------------------------+
//| Class CSpreadMonitor                                             |
//| Coordinates per-symbol spread histograms and the order gate      |
//+------------------------------------------------------------------+
class CSpreadMonitor
  {
private:
   CSpreadHistogram  m_histograms[];
   string            m_symbols[];
   int               m_n_symbols;
   double            m_alert_pct;
   double            m_warn_pct;
public:
                     CSpreadMonitor(void);
                    ~CSpreadMonitor(void);
   bool              Init(const string &symbols[],int n,int num_bins,double min_pts,double max_pts,
                           int window_size,double alert_pct,double warn_pct);
   void              OnTick(void);
   ENUM_SPREAD_STATUS GetStatus(int symbol_idx);
   bool              IsOrderAllowed(int symbol_idx);
   double            GetCurrentSpread(int symbol_idx);
   double            GetMedianSpread(int symbol_idx);
   double            GetPercentileRank(int symbol_idx);
   double            GetThresholdSpread(int symbol_idx);
   bool              GetBinCounts(int symbol_idx,int &out_counts[]);
   int               GetNumBins(int symbol_idx);
   double            GetBinWidth(int symbol_idx);
   double            GetMinSpread(int symbol_idx);
   double            GetMaxSpread(int symbol_idx);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSpreadMonitor::CSpreadMonitor(void)
  {
   m_n_symbols=0;
   m_alert_pct=95.0;
   m_warn_pct=75.0;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpreadMonitor::~CSpreadMonitor(void)
  {
   ArrayFree(m_histograms);
   ArrayFree(m_symbols);
  }
//+------------------------------------------------------------------+
//| Init                                                             |
//+------------------------------------------------------------------+
bool CSpreadMonitor::Init(const string &symbols[],int n,int num_bins,double min_pts,double max_pts,
                           int window_size,double alert_pct,double warn_pct)
  {
   if(n<=0 || alert_pct<=warn_pct || alert_pct>100.0 || warn_pct<0.0)
      return(false);
   m_n_symbols=n;
   m_alert_pct=alert_pct;
   m_warn_pct=warn_pct;
   if(ArrayResize(m_histograms,m_n_symbols)!=m_n_symbols)
      return(false);
   if(ArrayResize(m_symbols,m_n_symbols)!=m_n_symbols)
      return(false);
   for(int i=0;i<m_n_symbols;i++)
     {
      m_symbols[i]=symbols[i];
      if(!m_histograms[i].Init(num_bins,min_pts,max_pts,window_size))
         return(false);
      if(!SymbolSelect(symbols[i],true))
         PrintFormat("CSpreadMonitor: SymbolSelect failed for %s",symbols[i]);
     }
   return(true);
  }
//+------------------------------------------------------------------+
//| OnTick                                                           |
//+------------------------------------------------------------------+
void CSpreadMonitor::OnTick(void)
  {
   for(int i=0;i<m_n_symbols;i++)
     {
      long spread_points=SymbolInfoInteger(m_symbols[i],SYMBOL_SPREAD);
      double spread_pts=(double)spread_points;
      m_histograms[i].Update(spread_pts);
     }
  }
//+------------------------------------------------------------------+
//| GetStatus                                                        |
//+------------------------------------------------------------------+
ENUM_SPREAD_STATUS CSpreadMonitor::GetStatus(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(STATUS_RED);
   if(!m_histograms[symbol_idx].IsReady())
      return(STATUS_WARMING);
   double rank=GetPercentileRank(symbol_idx);
   if(rank>m_alert_pct)
      return(STATUS_RED);
   if(rank>m_warn_pct)
      return(STATUS_YELLOW);
   return(STATUS_GREEN);
  }
//+------------------------------------------------------------------+
//| IsOrderAllowed                                                   |
//+------------------------------------------------------------------+
bool CSpreadMonitor::IsOrderAllowed(int symbol_idx)
  {
   ENUM_SPREAD_STATUS status=GetStatus(symbol_idx);
   return(status==STATUS_GREEN || status==STATUS_YELLOW);
  }
//+------------------------------------------------------------------+
//| GetCurrentSpread                                                 |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetCurrentSpread(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   return((double)SymbolInfoInteger(m_symbols[symbol_idx],SYMBOL_SPREAD));
  }
//+------------------------------------------------------------------+
//| GetMedianSpread                                                  |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetMedianSpread(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   return(m_histograms[symbol_idx].GetMedian());
  }
//+------------------------------------------------------------------+
//| GetPercentileRank                                                |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetPercentileRank(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   double current=GetCurrentSpread(symbol_idx);
   return(m_histograms[symbol_idx].GetPercentileRank(current));
  }
//+------------------------------------------------------------------+
//| GetThresholdSpread                                               |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetThresholdSpread(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   return(m_histograms[symbol_idx].GetPercentileValue(m_alert_pct));
  }
//+------------------------------------------------------------------+
//| GetBinCounts                                                     |
//+------------------------------------------------------------------+
bool CSpreadMonitor::GetBinCounts(int symbol_idx,int &out_counts[])
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(false);
   return(m_histograms[symbol_idx].GetBinCounts(out_counts));
  }
//+------------------------------------------------------------------+
//| GetNumBins                                                       |
//+------------------------------------------------------------------+
int CSpreadMonitor::GetNumBins(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0);
   return(m_histograms[symbol_idx].GetNumBins());
  }
//+------------------------------------------------------------------+
//| GetBinWidth                                                      |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetBinWidth(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   return(m_histograms[symbol_idx].GetBinWidth());
  }
//+------------------------------------------------------------------+
//| GetMinSpread                                                     |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetMinSpread(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   return(m_histograms[symbol_idx].GetMinSpread());
  }
//+------------------------------------------------------------------+
//| GetMaxSpread                                                     |
//+------------------------------------------------------------------+
double CSpreadMonitor::GetMaxSpread(int symbol_idx)
  {
   if(symbol_idx<0 || symbol_idx>=m_n_symbols)
      return(0.0);
   return(m_histograms[symbol_idx].GetMaxSpread());
  }
#endif
//+------------------------------------------------------------------+