#property indicator_separate_window
#property indicator_buffers    5
#property indicator_color1     clrRed
#property indicator_color2     clrRed
#property indicator_color3     clrLime
#property indicator_color4     clrLime
#property indicator_color5     clrDarkSlateGray
#property indicator_width2     1
#property indicator_width4     1
#property indicator_width5     2
#property strict

//
//
//

input int                inpRocPeriod  = 21;          // ROC period
input int                inpPeriod     = 20;          // Hull Period
input double             inpDivisor    = 2.0;         // Hull Divisor ("speed")
input ENUM_APPLIED_PRICE inpPrice      = PRICE_CLOSE; // Price 
input bool               inpAutoWidth  = true;        // Automatic width?
input int                inpHistoWidth = 2;           // Used if not using automatic width

double Upa[],Upb[],Dna[],Dnb[],val[],valc[];
struct sGlobalStruct { double _cma,_pma; int lim,width,scale; }; sGlobalStruct glo;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
{
   if (inpAutoWidth)
   {
      glo.scale = int(ChartGetInteger(0,CHART_SCALE));
      switch(glo.scale) 
	   {
	      case 0: glo.width =  1; break;
	      case 1: glo.width =  1; break;
		   case 2: glo.width =  2; break;
		   case 3: glo.width =  3; break;
		   case 4: glo.width =  6; break;
		   case 5: glo.width = 14; break;
	   }
	}
	else { glo.width = inpHistoWidth; }
	
   IndicatorBuffers(6);
   SetIndexBuffer(0, Dna,  INDICATOR_DATA);   SetIndexStyle(0,DRAW_HISTOGRAM,EMPTY,glo.width);
   SetIndexBuffer(1, Dnb,  INDICATOR_DATA);   SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(2, Upa,  INDICATOR_DATA);   SetIndexStyle(2,DRAW_HISTOGRAM,EMPTY,glo.width);
   SetIndexBuffer(3, Upb,  INDICATOR_DATA);   SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexBuffer(4, val,  INDICATOR_DATA);   SetIndexStyle(4,DRAW_LINE);  SetIndexLabel(4,"Hull Roc");
   SetIndexBuffer(5, valc, INDICATOR_CALCULATIONS);
   
   iHullc.init(inpPeriod,inpDivisor);
   iHullp.init(inpPeriod,inpDivisor);
   
   IndicatorSetString(INDICATOR_SHORTNAME," Hull Smoothed ROC ("+(string)inpRocPeriod+","+(string)inpPeriod+")");
return (INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{  if(ChartGetInteger(0,CHART_SCALE) != glo.width)  OnInit();
   glo.lim  = fmin(rates_total-prev_calculated+1,rates_total-1); 
   
   //
   //
   //
   
   for(int i=glo.lim, r=rates_total-glo.lim-1; i>=0; i--,r++) 
   {
      glo._pma = iHullp.calculate(iMA(NULL,0,1,0,MODE_SMA,inpPrice,(int)fmin(rates_total-1,i+1)),r,rates_total);
      glo._cma = iHullc.calculate(iMA(NULL,0,1,0,MODE_SMA,inpPrice,i)                           ,r,rates_total);
      val[i]   = (glo._pma!=0) ?  100*(glo._cma-glo._pma)/glo._pma : 0;
      valc[i]  =  (r>0) ? (val[i]>0) ? (val[i]>val[i+1]) ? 0 : 1 : (val[i]<val[i+1]) ? 2 : 3 : 0;
      Upa[i]   = (valc[i] == 0) ? val[i] : EMPTY_VALUE;
      Upb[i]   = (valc[i] == 1) ? val[i] : EMPTY_VALUE;
      Dna[i]   = (valc[i] == 2) ? val[i] : EMPTY_VALUE;
      Dnb[i]   = (valc[i] == 3) ? val[i] : EMPTY_VALUE; 
   }
return(rates_total);
}

//------------------------------------------------------------------
// Custom function(s)
//------------------------------------------------------------------

class CHull
{
   private :
      int    m_fullPeriod;
      int    m_halfPeriod;
      int    m_sqrtPeriod;
      int    m_arraySize;
      double m_weight1;
      double m_weight2;
      double m_weight3;
      struct sHullArrayStruct
         {
            double value;
            double value3;
            double wsum1;
            double wsum2;
            double wsum3;
            double lsum1;
            double lsum2;
            double lsum3;
         };
      sHullArrayStruct m_array[];
  
   public :
      CHull() : m_fullPeriod(1), m_halfPeriod(1), m_sqrtPeriod(1), m_arraySize(-1) {                     }
     ~CHull()                                                                      { ArrayFree(m_array); }
    
      ///
      ///
      ///
    
      bool init(int period, double divisor)
      {
            m_fullPeriod = (int)(period>1 ? period : 1);  
            m_halfPeriod = (int)(m_fullPeriod>1 ? m_fullPeriod/(divisor>1 ? divisor : 1) : 1);
            m_sqrtPeriod = (int) MathSqrt(m_fullPeriod);
            m_arraySize  = -1; m_weight1 = m_weight2 = m_weight3 = 1;
               return(true);
      }
      
      //
      //
      //
      
      double calculate( double value, int i, int bars)
      {
         if (m_arraySize<bars) { m_arraySize = ArrayResize(m_array,bars+500); if (m_arraySize<bars) return(0); }
            
            //
            //
            //
            
            m_array[i].value=value;
            if (i>m_fullPeriod)
            {
               m_array[i].wsum1 = m_array[i-1].wsum1+value*m_halfPeriod-m_array[i-1].lsum1;
               m_array[i].lsum1 = m_array[i-1].lsum1+value-m_array[i-m_halfPeriod].value;
               m_array[i].wsum2 = m_array[i-1].wsum2+value*m_fullPeriod-m_array[i-1].lsum2;
               m_array[i].lsum2 = m_array[i-1].lsum2+value-m_array[i-m_fullPeriod].value;
            }
            else
            {
               m_array[i].wsum1 = m_array[i].wsum2 =
               m_array[i].lsum1 = m_array[i].lsum2 = m_weight1 = m_weight2 = 0;
               for(int k=0, w1=m_halfPeriod, w2=m_fullPeriod; w2>0 && i>=k; k++, w1--, w2--)
               {
                  if (w1>0)
                  {
                     m_array[i].wsum1 += m_array[i-k].value*w1;
                     m_array[i].lsum1 += m_array[i-k].value;
                     m_weight1        += w1;
                  }                  
                  m_array[i].wsum2 += m_array[i-k].value*w2;
                  m_array[i].lsum2 += m_array[i-k].value;
                  m_weight2        += w2;
               }
            }
            m_array[i].value3=2.0*m_array[i].wsum1/m_weight1-m_array[i].wsum2/m_weight2;
        
            //
            //
            //
        
            if (i>m_sqrtPeriod)
            {
               m_array[i].wsum3 = m_array[i-1].wsum3+m_array[i].value3*m_sqrtPeriod-m_array[i-1].lsum3;
               m_array[i].lsum3 = m_array[i-1].lsum3+m_array[i].value3-m_array[i-m_sqrtPeriod].value3;
            }
            else
            {  
               m_array[i].wsum3 =
               m_array[i].lsum3 = m_weight3 = 0;
               for(int k=0, w3=m_sqrtPeriod; w3>0 && i>=k; k++, w3--)
               {
                  m_array[i].wsum3 += m_array[i-k].value3*w3;
                  m_array[i].lsum3 += m_array[i-k].value3;
                  m_weight3        += w3;
               }
            }        
         return(m_array[i].wsum3/m_weight3);
      }
};
CHull iHullc,iHullp;
