//+------------------------------------------------------------------+
//|                                                 ZScoreEngine.mqh |
//|                                  Copyright 2026, MetaQuotes Ltd. |
//+------------------------------------------------------------------+
#property copyright "Open Source"
#property version   "7.10"

//+------------------------------------------------------------------+
//| Class: CZScore                                                   |
//| Purpose: Calculates rolling Standard Deviation and Z-Score       |
//+------------------------------------------------------------------+
class CZScore
  {
private:
   int               m_period;            
   string            m_symbol;            
   ENUM_TIMEFRAMES   m_timeframe;         

   double            CalculateMean(const double &prices[]);
   double            CalculateStandardDeviation(const double &prices[], double mean);

public:
                     CZScore(string symbol, ENUM_TIMEFRAMES tf, int period);
                    ~CZScore(void);
                    
   double            GetZScore(int shift = 1); 
  };

//+------------------------------------------------------------------+
//| Constructor: Initializes the statistical environment             |
//+------------------------------------------------------------------+
CZScore::CZScore(string symbol, ENUM_TIMEFRAMES tf, int period)
  {
   m_symbol = (symbol == "") ? _Symbol : symbol;
   m_timeframe = tf;
   m_period = (period < 2) ? 20 : period; 
  }

//+------------------------------------------------------------------+
//| Destructor: Standard cleanup                                     |
//+------------------------------------------------------------------+
CZScore::~CZScore(void)
  {
  }

//+------------------------------------------------------------------+
//| Calculates the Arithmetic Mean (SMA) of the provided array       |
//+------------------------------------------------------------------+
double CZScore::CalculateMean(const double &prices[])
  {
   double sum = 0.0;
   int total = ArraySize(prices);
   
   for(int i = 0; i < total; i++)
     {
      sum += prices[i];
     }
     
   return (total > 0) ? (sum / total) : 0.0;
  }

//+------------------------------------------------------------------+
//| Calculates the Population Standard Deviation                     |
//+------------------------------------------------------------------+
double CZScore::CalculateStandardDeviation(const double &prices[], double mean)
  {
   double variance_sum = 0.0;
   int total = ArraySize(prices);
   
   for(int i = 0; i < total; i++)
     {
      double difference = prices[i] - mean;
      variance_sum += MathPow(difference, 2);
     }
     
   double variance = (total > 0) ? (variance_sum / total) : 0.0;
   return MathSqrt(variance);
  }

//+------------------------------------------------------------------+
//| Orchestrates data extraction and returns the current Z-Score     |
//+------------------------------------------------------------------+
double CZScore::GetZScore(int shift)
  {
   double prices[];
   ArraySetAsSeries(prices, true);
   
   if(CopyClose(m_symbol, m_timeframe, shift, m_period, prices) < m_period)
     {
      return 0.0; 
     }
     
   double current_close = iClose(m_symbol, m_timeframe, shift);
   double mean = CalculateMean(prices);
   double std_dev = CalculateStandardDeviation(prices, mean);
   
   if(std_dev == 0.0) return 0.0;
   
   return (current_close - mean) / std_dev;
  }