//+------------------------------------------------------------------+
//|                                             ApEnCalculator.mqh   |
//+------------------------------------------------------------------+
#ifndef APENCALCULATOR_MQH
#define APENCALCULATOR_MQH
//+------------------------------------------------------------------+
//| Class CApEnCalculator                                            |
//| Computes Approximate Entropy over a rolling window of returns    |
//+------------------------------------------------------------------+
class CApEnCalculator
  {
private:
   int               m_m;
   double            m_r_factor;
   double            m_window[];
   int               m_window_size;
   int               m_capacity;
   double            ComputePhi(const double &series[],int n,int m,double r);
   double            ComputeSD(const double &series[],int n);
public:
                     CApEnCalculator(void);
                    ~CApEnCalculator(void);
   bool              Init(int capacity,int m,double r_factor);
   void              Push(double value);
   double            Compute(void);
   bool              IsReady(void);
   void              Reset(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CApEnCalculator::CApEnCalculator(void)
  {
   m_m=2;
   m_r_factor=0.2;
   m_window_size=0;
   m_capacity=0;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CApEnCalculator::~CApEnCalculator(void)
  {
   ArrayFree(m_window);
  }
//+------------------------------------------------------------------+
//| Init                                                             |
//+------------------------------------------------------------------+
bool CApEnCalculator::Init(int capacity,int m,double r_factor)
  {
   if(capacity<=2 || m<1 || r_factor<=0.0)
      return(false);
   m_capacity=capacity;
   m_m=m;
   m_r_factor=r_factor;
   m_window_size=0;
   if(ArrayResize(m_window,m_capacity)!=m_capacity)
      return(false);
   ArrayInitialize(m_window,0.0);
   return(true);
  }
//+------------------------------------------------------------------+
//| Push                                                             |
//+------------------------------------------------------------------+
void CApEnCalculator::Push(double value)
  {
   if(m_capacity<=0)
      return;
   if(m_window_size<m_capacity)
     {
      m_window[m_window_size]=value;
      m_window_size++;
     }
   else
     {
      for(int i=0;i<m_capacity-1;i++)
         m_window[i]=m_window[i+1];
      m_window[m_capacity-1]=value;
     }
  }
//+------------------------------------------------------------------+
//| ComputeSD                                                        |
//+------------------------------------------------------------------+
double CApEnCalculator::ComputeSD(const double &series[],int n)
  {
   if(n<=1)
      return(0.0);
   double mean=0.0;
   for(int i=0;i<n;i++)
      mean+=series[i];
   mean/=n;
   double sum_sq=0.0;
   for(int i=0;i<n;i++)
      sum_sq+=(series[i]-mean)*(series[i]-mean);
   return(::MathSqrt(sum_sq/(n-1)));
  }
//+------------------------------------------------------------------+
//| ComputePhi                                                       |
//+------------------------------------------------------------------+
double CApEnCalculator::ComputePhi(const double &series[],int n,int m,double r)
  {
   int count=n-m+1;
   if(count<=0)
      return(0.0);
   double phi_sum=0.0;
   for(int i=0;i<count;i++)
     {
      int matches=0;
      for(int j=0;j<count;j++)
        {
         double dist=0.0;
         for(int k=0;k<m;k++)
           {
            double diff=::MathAbs(series[i+k]-series[j+k]);
            if(diff>dist)
               dist=diff;
           }
         if(dist<=r)
            matches++;
        }
      double c_i=(double)matches/(double)count;
      phi_sum+=::MathLog(c_i);
     }
   return(phi_sum/count);
  }
//+------------------------------------------------------------------+
//| Compute                                                          |
//+------------------------------------------------------------------+
double CApEnCalculator::Compute(void)
  {
   if(m_window_size<m_capacity)
      return(EMPTY_VALUE);
   if(m_capacity<m_m+2)
      return(EMPTY_VALUE);
   double sd=ComputeSD(m_window,m_window_size);
   if(sd<=0.0)
      return(EMPTY_VALUE);
   double r=m_r_factor*sd;
   double phi_m=ComputePhi(m_window,m_window_size,m_m,r);
   double phi_m1=ComputePhi(m_window,m_window_size,m_m+1,r);
   return(phi_m-phi_m1);
  }
//+------------------------------------------------------------------+
//| IsReady                                                          |
//+------------------------------------------------------------------+
bool CApEnCalculator::IsReady(void)
  {
   return(m_window_size>=m_capacity && m_capacity>0);
  }
//+------------------------------------------------------------------+
//| Reset                                                            |
//+------------------------------------------------------------------+
void CApEnCalculator::Reset(void)
  {
   m_window_size=0;
   ArrayInitialize(m_window,0.0);
  }
#endif
//+------------------------------------------------------------------+