//+------------------------------------------------------------------+
//|                                       smTMMS Oscillator_vX
//+------------------------------------------------------------------+
#property copyright "Copyright 30.06.2019, SwingMan"
#property strict
#property indicator_separate_window

#property indicator_buffers    5
#property indicator_level1     20
#property indicator_level2     0
#property indicator_level3     -20
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT
#property indicator_levelwidth 1

#property indicator_maximum    50
#property indicator_minimum    -50
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

sinput string                 sRSI              = "RSI"; //=================================     
input int                     RSI_Period        = 14;
input ENUM_APPLIED_PRICE      RSI_AppliedPrice  = PRICE_CLOSE;
//
sinput string                 sStoch            = "STOCHASTIC"; //=================================    
input int                     Sto1PerK          = 8;
input int                     Sto1PerD          = 3;
input int                     Sto1Slow          = 3;
input ENUM_STO_PRICE          Sto1Price         = STO_LOWHIGH;
input ENUM_MA_METHOD          Sto1MaMethod      = MODE_SMMA;
input int                     Sto2PerK          = 14;
input int                     Sto2PerD          = 3;
input int                     Sto2Slow          = 3;
input ENUM_STO_PRICE          Sto2Price         = STO_LOWHIGH;
input ENUM_MA_METHOD          Sto2MaMethod      = MODE_SMMA;
//
sinput string                 sHullAverage      = "HULL Moving Average"; //=================================  
input int                     Hull_Period       = 12;
input double                  Hull_Divisor      = 2;
input ENUM_APPLIED_PRICE      Hull_AppliedPrice = PRICE_CLOSE;

sinput string                 Display           = "Display settings";         //=================================  
input bool                    AutoHisto         = true;                       // Automatically adjust histo width
input int                     HistWidth         = 3;                          // Histogram bars width
input color                   UpHistoColor      = clrGreen;                   // Bullish color
input color                   DnHistoColor      = clrRed;                     // Bearish color
input color                   NuHistoColor      = clrGray;                    // Neutral color
enum ENUM_ARROW_TYPE
     {
         Circle,                                                              // Circle arrow type
         Rectangle                                                            // Rectangle arrow type
     };
input ENUM_ARROW_TYPE         Arrow_Type         = Circle;                    // Arrow type
input color                   upColor            = clrLimeGreen;              // Bullish Hull slope color
input color                   dnColor            = clrOrange;                 // Bearish Hull slope color

double huu[],hdd[],hnn[],hmaUp[],hmaDn[];
struct sGlobalStruct
{
   int      hwidth;
};
sGlobalStruct glo;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
{
   if (AutoHisto)
   {
      int scale = int(ChartGetInteger(0,CHART_SCALE));
      switch(scale) 
	   {
	      case 0: glo.hwidth =  1; break;
	      case 1: glo.hwidth =  1; break;
		   case 2: glo.hwidth =  2; break;
		   case 3: glo.hwidth =  3; break;
		   case 4: glo.hwidth =  6; break;
		   case 5: glo.hwidth = 14; break;
	   }
	}
	else { glo.hwidth = HistWidth; }
	
   int iArrow = (Arrow_Type==Circle) ? 108 : 110;
   
   SetIndexBuffer(0,huu,  INDICATOR_DATA);  SetIndexStyle(0,DRAW_HISTOGRAM,EMPTY,glo.hwidth,UpHistoColor);
   SetIndexBuffer(1,hdd,  INDICATOR_DATA);  SetIndexStyle(1,DRAW_HISTOGRAM,EMPTY,glo.hwidth,DnHistoColor); 
   SetIndexBuffer(2,hnn,  INDICATOR_DATA);  SetIndexStyle(2,DRAW_HISTOGRAM,EMPTY,glo.hwidth,NuHistoColor);  
   SetIndexBuffer(3,hmaUp,INDICATOR_DATA);  SetIndexStyle(3,DRAW_ARROW,EMPTY,EMPTY,upColor); SetIndexArrow(3,iArrow); SetIndexLabel(3,"HMA_Trend UP");
   SetIndexBuffer(4,hmaDn,INDICATOR_DATA);  SetIndexStyle(4,DRAW_ARROW,EMPTY,EMPTY,dnColor); SetIndexArrow(4,iArrow); SetIndexLabel(4,"HMA_Trend DOWN"); 

   iHull.init(Hull_Period,Hull_Divisor);
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 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.hwidth) OnInit();
   int i,r,limit=fmin(rates_total-prev_calculated+1,rates_total-1);
   
   //
   //
   //
   
   struct sWorkStruct
   {
     double rsi1;
     double sto1;
     double sto2;
     double avg1;
     double trend;
   };
   static sWorkStruct wrk[];
   static int         wrkSize = -1;
                  if (wrkSize<rates_total) wrkSize = ArrayResize(wrk,rates_total+500);
   
   
   //
   //
   //
   
   for(i=limit, r=rates_total-limit-1; i>=0; i--,r++)
   {
      wrk[r].rsi1 = iRSI(_Symbol,_Period,RSI_Period,RSI_AppliedPrice,i)-50.0;
      wrk[r].sto1 = iStochastic(_Symbol,_Period,Sto1PerK,Sto1PerD,Sto1Slow,Sto1MaMethod,Sto1Price,MODE_MAIN,i)-50.0;
      wrk[r].sto2 = iStochastic(_Symbol,_Period,Sto2PerK,Sto2PerD,Sto2Slow,Sto2MaMethod,Sto2Price,MODE_MAIN,i)-50.0;
      wrk[r].avg1 = iHull.calculate(iMA(NULL,0,1,0,MODE_SMA,Hull_AppliedPrice,i),r,rates_total);
      
      huu[i] = (wrk[r].rsi1>0 && wrk[r].sto1>0 && wrk[r].sto2>0) ? wrk[r].sto2 : EMPTY_VALUE;
      hdd[i] = (wrk[r].rsi1<0 && wrk[r].sto1<0 && wrk[r].sto2<0) ? wrk[r].sto2 : EMPTY_VALUE;
      hnn[i] = (huu[i] == EMPTY_VALUE && hdd[i] == EMPTY_VALUE)  ? wrk[r].sto2 : EMPTY_VALUE;
  
      wrk[r].trend = (r>0) ? (wrk[r].avg1>wrk[r-1].avg1) ? 1 : (wrk[r].avg1<wrk[r-1].avg1) ? -1 : wrk[r-1].trend : 0;
      hmaUp[i] = (wrk[r].trend>0) ? 0 : EMPTY_VALUE;
      hmaDn[i] = (wrk[r].trend<0) ? 0 : 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 iHull;
