//+------------------------------------------------------------------+
//|                                                  CStatistics.mqh |
//|                                                      Sahil Bagdi |
//|                         https://www.mql5.com/en/users/sahilbagdi |
//+------------------------------------------------------------------+
#property copyright "Sahil Bagdi"
#property link      "https://www.mql5.com/en/users/sahilbagdi"

//+------------------------------------------------------------------+
//| Class for statistical calculations                               |
//+------------------------------------------------------------------+
class CStatistics
{
private:
    double      m_data[];           // Data array for calculations
    int         m_dataSize;         // Size of the data array
    bool        m_isSorted;         // Flag indicating if data is sorted
    double      m_sortedData[];     // Sorted copy of data for percentile calculations
    
public:
    // Constructor and destructor
    CStatistics();
    ~CStatistics();
    
    // Data management methods
    bool        SetData(const double &data[], int size);
    bool        AddData(double value);
    void        Clear();
    
    // Basic statistical methods
    double      Mean() const;
    double      StandardDeviation() const;
    double      Variance() const;
    
    // Range and extremes
    double      Min() const;
    double      Max() const;
    double      Range() const;
    
    // Time series specific methods
    double      Autocorrelation(int lag) const;
    double      TrendStrength() const;
    double      MeanReversionStrength() const;
    
    // Percentile calculations
    double      Percentile(double percentile);
    double      Median();
};

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CStatistics::CStatistics()
{
    m_dataSize = 0;
    m_isSorted = false;
    ArrayResize(m_data, 0);
    ArrayResize(m_sortedData, 0);
}

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CStatistics::~CStatistics()
{
    Clear();
}

//+------------------------------------------------------------------+
//| Set data array for calculations                                  |
//+------------------------------------------------------------------+
bool CStatistics::SetData(const double &data[], int size)
{
    if(size <= 0)
        return false;
        
    m_dataSize = size;
    ArrayResize(m_data, size);
    
    for(int i = 0; i < size; i++)
        m_data[i] = data[i];
        
    m_isSorted = false;
    return true;
}

//+------------------------------------------------------------------+
//| Add a single data point                                          |
//+------------------------------------------------------------------+
bool CStatistics::AddData(double value)
{
    m_dataSize++;
    ArrayResize(m_data, m_dataSize);
    m_data[m_dataSize - 1] = value;
    m_isSorted = false;
    return true;
}

//+------------------------------------------------------------------+
//| Clear all data                                                   |
//+------------------------------------------------------------------+
void CStatistics::Clear()
{
    m_dataSize = 0;
    ArrayResize(m_data, 0);
    ArrayResize(m_sortedData, 0);
    m_isSorted = false;
}

//+------------------------------------------------------------------+
//| Calculate mean of data                                           |
//+------------------------------------------------------------------+
double CStatistics::Mean() const
{
    if(m_dataSize <= 0)
        return 0.0;
        
    double sum = 0.0;
    for(int i = 0; i < m_dataSize; i++)
        sum += m_data[i];
        
    return sum / m_dataSize;
}

//+------------------------------------------------------------------+
//| Calculate standard deviation of data                             |
//+------------------------------------------------------------------+
double CStatistics::StandardDeviation() const
{
    if(m_dataSize <= 1)
        return 0.0;
        
    double mean = Mean();
    double sum = 0.0;
    
    for(int i = 0; i < m_dataSize; i++)
        sum += MathPow(m_data[i] - mean, 2);
        
    return MathSqrt(sum / (m_dataSize - 1));
}

//+------------------------------------------------------------------+
//| Calculate variance of data                                       |
//+------------------------------------------------------------------+
double CStatistics::Variance() const
{
    if(m_dataSize <= 1)
        return 0.0;
        
    double stdDev = StandardDeviation();
    return stdDev * stdDev;
}

//+------------------------------------------------------------------+
//| Find minimum value in data                                       |
//+------------------------------------------------------------------+
double CStatistics::Min() const
{
    if(m_dataSize <= 0)
        return 0.0;
        
    double min = m_data[0];
    
    for(int i = 1; i < m_dataSize; i++)
        if(m_data[i] < min)
            min = m_data[i];
            
    return min;
}

//+------------------------------------------------------------------+
//| Find maximum value in data                                       |
//+------------------------------------------------------------------+
double CStatistics::Max() const
{
    if(m_dataSize <= 0)
        return 0.0;
        
    double max = m_data[0];
    
    for(int i = 1; i < m_dataSize; i++)
        if(m_data[i] > max)
            max = m_data[i];
            
    return max;
}

//+------------------------------------------------------------------+
//| Calculate range (max - min) of data                              |
//+------------------------------------------------------------------+
double CStatistics::Range() const
{
    return Max() - Min();
}

//+------------------------------------------------------------------+
//| Calculate autocorrelation with specified lag                     |
//+------------------------------------------------------------------+
double CStatistics::Autocorrelation(int lag) const
{
    if(m_dataSize <= lag || lag <= 0)
        return 0.0;
        
    double mean = Mean();
    double numerator = 0.0;
    double denominator = 0.0;
    
    for(int i = 0; i < m_dataSize - lag; i++)
    {
        numerator += (m_data[i] - mean) * (m_data[i + lag] - mean);
    }
    
    for(int i = 0; i < m_dataSize; i++)
    {
        denominator += MathPow(m_data[i] - mean, 2);
    }
    
    if(denominator == 0.0)
        return 0.0;
        
    return numerator / denominator;
}

//+------------------------------------------------------------------+
//| Calculate trend strength based on autocorrelation                |
//+------------------------------------------------------------------+
double CStatistics::TrendStrength() const
{
    // Use lag-1 autocorrelation as a measure of trend strength
    double ac1 = Autocorrelation(1);
    
    // Positive autocorrelation indicates trending behavior
    return ac1;
}

//+------------------------------------------------------------------+
//| Calculate mean reversion strength based on autocorrelation       |
//+------------------------------------------------------------------+
double CStatistics::MeanReversionStrength() const
{
    // Negative autocorrelation indicates mean-reverting behavior
    double ac1 = Autocorrelation(1);
    
    // Return the negative of autocorrelation, so positive values
    // indicate stronger mean reversion
    return -ac1;
}

//+------------------------------------------------------------------+
//| Calculate percentile of data                                     |
//+------------------------------------------------------------------+
double CStatistics::Percentile(double percentile)
{
    if(m_dataSize <= 0 || percentile < 0.0 || percentile > 100.0)
        return 0.0;
        
    // Sort data if needed
    if(!m_isSorted)
    {
        ArrayResize(m_sortedData, m_dataSize);
        for(int i = 0; i < m_dataSize; i++)
            m_sortedData[i] = m_data[i];
            
        ArraySort(m_sortedData);
        m_isSorted = true;
    }
    
    // Calculate position
    double position = (percentile / 100.0) * (m_dataSize - 1);
    int lowerIndex = (int)MathFloor(position);
    int upperIndex = (int)MathCeil(position);
    
    // Handle edge cases
    if(lowerIndex == upperIndex)
        return m_sortedData[lowerIndex];
        
    // Interpolate
    double fraction = position - lowerIndex;
    return m_sortedData[lowerIndex] + fraction * (m_sortedData[upperIndex] - m_sortedData[lowerIndex]);
}

//+------------------------------------------------------------------+
//| Calculate median (50th percentile) of data                       |
//+------------------------------------------------------------------+
double CStatistics::Median()
{
    return Percentile(50.0);
}
