//------------------------------------------------------------------
#property copyright "mladen"
#property link      "mladenfx@gmail.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 7
#property strict


//
//
//
//
//

extern int            DssPeriod       = 55;             // DSS period
extern int            DssEmaPeriod    = 15;             // Smoothing period
extern int            DssQuanPeriod   = 15;             // Quantile period (<=0 for same as dss period)
extern double         LevelUp         = 80;             // Level up
extern double         LevelDn         = 20;             // Level dowm
extern color          ColorNu         = clrSilver;      // Color for neutral
extern color          ColorUp         = clrLimeGreen;   // Color for up
extern color          ColorDown       = clrSandyBrown;  // Color for down
extern int            LineWidth       = 2;              // Main line width

double dss[],dssDa[],dssDb[],dssUa[],dssUb[],trend[],levelup[],leveldn[],high[],low[],close[];


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(11);
   SetIndexBuffer(0, dss);     SetIndexStyle(0,EMPTY,EMPTY,LineWidth,ColorNu);
   SetIndexBuffer(1, dssUa);   SetIndexStyle(1,EMPTY,EMPTY,LineWidth,ColorUp);
   SetIndexBuffer(2, dssUb);   SetIndexStyle(2,EMPTY,EMPTY,LineWidth,ColorUp);
   SetIndexBuffer(3, dssDa);   SetIndexStyle(3,EMPTY,EMPTY,LineWidth,ColorDown);
   SetIndexBuffer(4, dssDb);   SetIndexStyle(4,EMPTY,EMPTY,LineWidth,ColorDown);
   SetIndexBuffer(5, levelup); SetIndexStyle(5,EMPTY,STYLE_DOT);
   SetIndexBuffer(6, leveldn); SetIndexStyle(6,EMPTY,STYLE_DOT);
   SetIndexBuffer(7, trend);
   SetIndexBuffer(8, high);
   SetIndexBuffer(9, low);
   SetIndexBuffer(10,close);
      DssEmaPeriod = MathMax(DssEmaPeriod,1);
      IndicatorShortName("Double smoothed stochastic ("+(string)DssPeriod+")");
   return(0);
}
int deinit()
{
   return(0);
}

//
//
//
//
//

int start()
{
   int counted_bars = IndicatorCounted();
      if(counted_bars < 0) return(-1); 
      if(counted_bars > 0) counted_bars--;
           int limit = MathMin(Bars-counted_bars,Bars-1);

   //
   //
   //
   //
   //

   int quanPeriod = (DssQuanPeriod>0) ? DssQuanPeriod : DssPeriod;
   double workdss[]; ArrayResize(workdss,quanPeriod);
   if (trend[limit] ==-1) CleanPoint(limit,dssDa,dssDb);
   if (trend[limit] == 1) CleanPoint(limit,dssUa,dssUb);
   for (int i=limit; i>=0; i--)
   {  
      dss[i] = iDss(High[i],Low[i],Close[i],DssPeriod,DssEmaPeriod,i,Bars);
         ArrayCopy(workdss,dss,0,i,quanPeriod);
         levelup[i]  = iQuantile(quanPeriod,LevelUp,workdss);
         leveldn[i]  = iQuantile(quanPeriod,LevelDn,workdss);
         dssDa[i]   = EMPTY_VALUE;
         dssDb[i]   = EMPTY_VALUE;
         dssUa[i]   = EMPTY_VALUE;
         dssUb[i]   = EMPTY_VALUE;
         trend[i]    = (dss[i]>levelup[i]) ? 1 : (dss[i]<leveldn[i]) ? -1 : 0;
            if (trend[i] == 1) PlotPoint(i,dssUa,dssUb,dss);
            if (trend[i] ==-1) PlotPoint(i,dssDa,dssDb,dss);
   }         
   return(0);
}


//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

#define _dssInstances 2
#define _dssInstancesSize 5
double workDss[][_dssInstances*_dssInstancesSize];
double iDss(double _high, double _low, double _close, int _stoPeriod, double _emaPeriod, int i, int _bars, int instanceNo=0)
{
   if (ArrayRange(workDss,0)!=_bars) ArrayResize(workDss,_bars); instanceNo*=_dssInstancesSize; i =_bars-i-1;
   
   //
   //
   //
   //
   //
   
   workDss[i][instanceNo  ] = _high;
   workDss[i][instanceNo+1] = _low;
   workDss[i][instanceNo+2] = _close;
      double _alpha = 2.0/(1.0+_emaPeriod);
      double max    = _high;
      double min    = _low;
      for (int k=1; k<_stoPeriod && (i-k)>=0; k++)
         {
            max = MathMax(max,workDss[i-k][instanceNo  ]);
            min = MathMin(min,workDss[i-k][instanceNo+1]);
         }
         double sto = (max!=min) ? (_close-min)/(max-min) : 0;
         workDss[i][instanceNo+3] = (i>0) ? workDss[i-1][instanceNo+3] +_alpha*(sto-workDss[i-1][instanceNo+3]) : sto;
         
         //
         //
         //
         //
         //
         
         max = min = workDss[i][instanceNo+3];
         for (int k=1; k<_stoPeriod && (i-k)>=0; k++)
         {
            max = MathMax(max,workDss[i-k][instanceNo+3]);
            min = MathMin(min,workDss[i-k][instanceNo+3]);
         }
         sto = (max!=min) ? 100.0*(workDss[i][instanceNo+3]-min)/(max-min) : 0;
         workDss[i][instanceNo+4] = (i>0) ? workDss[i-1][instanceNo+4] +_alpha*(sto-workDss[i-1][instanceNo+4]) : sto;
         
      //
      //
      //
      //
      //
      
   return(workDss[i][instanceNo+4]);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double iQuantile(int period, double qp, double& quantileArray[])
{
   ArraySort(quantileArray);

   //
   //
   //
   //
   //
   
   double index = (period-1)*qp/100.00;
   int    ind   = (int)index;
   double delta = index - ind;
   if (ind == NormalizeDouble(index,5))
         return(            quantileArray[ind]);
   else  return((1.0-delta)*quantileArray[ind]+delta*quantileArray[ind+1]);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if (i>=Bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

void PlotPoint(int i,double& first[],double& second[],double& from[])
{
   if (i>=Bars-2) return;
   if (first[i+1] == EMPTY_VALUE)
      if (first[i+2] == EMPTY_VALUE) 
            { first[i]  = from[i]; first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
      else  { second[i] = from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else     { first[i]  = from[i];                          second[i] = EMPTY_VALUE; }
}