//------------------------------------------------------------------
#property copyright "mladen"
#property link      "mladenfx@gmail.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1  LimeGreen
#property indicator_color2  Orange
#property indicator_color3  Orange
#property indicator_width1  2
#property indicator_width2  2
#property indicator_width3  2

//
//
//
//
//

extern int    StochasticLength1  = 200;
extern double StochasticDivisor1 = 1;
extern double SmoothMA1          = 50;
extern int    StochasticLength2  = 100;
extern double StochasticDivisor2 = 2;
extern double SmoothMA2          = 25;
extern int    StochasticLength3  = 12;
extern double StochasticDivisor3 = 4;
extern double SmoothMA3          = 3;
extern int    StochasticLength4  = 0;
extern double StochasticDivisor4 = 1;
extern double SmoothMA4          = 3;
extern int    StochasticLength5  = 0;
extern double StochasticDivisor5 = 1;
extern double SmoothMA5          = 3;
extern int    StochasticLength6  = 0;
extern double StochasticDivisor6 = 1;
extern double SmoothMA6          = 3;
extern int    StochasticLength7  = 0;
extern double StochasticDivisor7 = 1;
extern double SmoothMA7          = 3;
extern int    StochasticLength8  = 0;
extern double StochasticDivisor8 = 1;
extern double SmoothMA8          = 3;
extern int    FinalStochasticLength = 32;
extern int    FinalSmooth           =  3;
extern ENUM_MA_METHOD SmoothMode = MODE_EMA;
double dssBuffer[];
double dssBufferda[];
double dssBufferdb[];
double slope[];

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(4);
   SetIndexBuffer(0,dssBuffer);
   SetIndexBuffer(1,dssBufferda);
   SetIndexBuffer(2,dssBufferdb);
   SetIndexBuffer(3,slope);
   IndicatorShortName("DSS Bressert - composite "+getAverageName(SmoothMode)+" smoothing");
   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);

   if (slope[limit]==-1) CleanPoint(limit,dssBufferda,dssBufferdb);
 	for(int i = limit; i>=0; i--)
 	{
 	    double dss = 0;
 	    if (StochasticLength1>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength1,SmoothMA1,SmoothMode,i,0)/StochasticDivisor1;
 	    if (StochasticLength2>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength2,SmoothMA2,SmoothMode,i,1)/StochasticDivisor2;
 	    if (StochasticLength3>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength3,SmoothMA3,SmoothMode,i,2)/StochasticDivisor3;
 	    if (StochasticLength4>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength4,SmoothMA4,SmoothMode,i,3)/StochasticDivisor4;
 	    if (StochasticLength5>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength5,SmoothMA5,SmoothMode,i,4)/StochasticDivisor5;
 	    if (StochasticLength6>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength6,SmoothMA6,SmoothMode,i,5)/StochasticDivisor6;
 	    if (StochasticLength7>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength7,SmoothMA7,SmoothMode,i,6)/StochasticDivisor7;
 	    if (StochasticLength8>1)     dss += iDss(Close[i],High[i],Low[i],StochasticLength8,SmoothMA8,SmoothMode,i,7)/StochasticDivisor8;
 	    if (FinalStochasticLength>1) dss  = iDss(dss     ,dss    ,dss   ,FinalStochasticLength,FinalSmooth,SmoothMode,i,8);
 	          dssBuffer[i]   = dss;
 	          dssBufferda[i] = EMPTY_VALUE;
 	          dssBufferdb[i] = EMPTY_VALUE;
 	          slope[i]       = slope[i+1];
 	          if (dssBuffer[i]>dssBuffer[i+1]) slope[i] =  1;
 	          if (dssBuffer[i]<dssBuffer[i+1]) slope[i] = -1;
 	          if (slope[i]==-1) PlotPoint(i,dssBufferda,dssBufferdb,dssBuffer);
   } 	    
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double workDss[][45];
#define _st1    0
#define _ss1    1
#define _pHigh  2
#define _pLow   3
#define _dss    4

double iDss(double close, double high, double low, int length, double smooth, int mode, int i, int instanceNo=0)
{
   if (ArrayRange(workDss,0)!=Bars) ArrayResize(workDss,Bars); int r=Bars-i-1; int maInstance = instanceNo*2; instanceNo*=5;
   
   //
   //
   //
   //
   //
   
         workDss[r][instanceNo+_pHigh]  = high;
         workDss[r][instanceNo+_pLow]   = low;
     
         double min = workDss[r][instanceNo+_pLow];
         double max = workDss[r][instanceNo+_pHigh];
         for (int k=1; k<length && (r-k)>=0; k++)
         {
            min = MathMin(min,workDss[r-k][instanceNo+_pLow]);
            max = MathMax(max,workDss[r-k][instanceNo+_pHigh]);
         }
      
         workDss[r][instanceNo+_st1] = 0;
               if (min!=max) workDss[r][instanceNo+_st1] = 100*(close-min)/(max-min);
         workDss[r][instanceNo+_ss1] = iCustomMa(mode,workDss[r][instanceNo+_st1],smooth,i,maInstance+0);

         //
         //
         //
         //
         //
         
         min = workDss[r][instanceNo+_ss1];
         max = workDss[r][instanceNo+_ss1];
         for (k=1; k<length && (r-k)>=0; k++)
         {
            min = MathMin(min,workDss[r-k][instanceNo+_ss1]);
            max = MathMax(max,workDss[r-k][instanceNo+_ss1]);
         }
         double stoch = 0; if (min!=max) stoch = 100*(workDss[r][instanceNo+_ss1]-min)/(max-min);
         
         //
         //
         //
         //
         //
         
          workDss[r][instanceNo+_dss] = iCustomMa(mode,stoch,smooth,i,maInstance+1);
                                       
   return(workDss[r][instanceNo+_dss]);
}


//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

string methodNames[] = {"SMA","EMA","Smoothed MA","Linear weighted MA"};
string getAverageName(int method)
{
   int max = ArraySize(methodNames)-1;
      method=MathMax(MathMin(method,max),0); return(methodNames[method]);
}

//
//
//
//
//

#define _maWorkBufferx1 18
#define _maWorkBufferx2 36
#define _maWorkBufferx3 54

double iCustomMa(int mode, double price, double length, int i, int instanceNo=0)
{
   int r = Bars-i-1;
   switch (mode)
   {
      case 0  : return(iSma(price,length,r,instanceNo));
      case 1  : return(iEma(price,length,r,instanceNo));
      case 2  : return(iSmma(price,length,r,instanceNo));
      case 3  : return(iLwma(price,length,r,instanceNo));
      default : return(0);
   }
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

double workSma[][_maWorkBufferx2];
double iSma(double price, int period, int r, int instanceNo=0)
{
   if (ArrayRange(workSma,0)!= Bars) ArrayResize(workSma,Bars); instanceNo *= 2;

   //
   //
   //
   //
   //
      
   workSma[r][instanceNo] = price;
   if (r>=period)
          workSma[r][instanceNo+1] = workSma[r-1][instanceNo+1]+(workSma[r][instanceNo]-workSma[r-period][instanceNo])/period;
   else { workSma[r][instanceNo+1] = 0; for(int k=0; k<period && (r-k)>=0; k++) workSma[r][instanceNo+1] += workSma[r-k][instanceNo];  
          workSma[r][instanceNo+1] /= k; }
   return(workSma[r][instanceNo+1]);
}

//
//
//
//
//

double workEma[][_maWorkBufferx1];
double iEma(double price, double period, int r, int instanceNo=0)
{
   if (ArrayRange(workEma,0)!= Bars) ArrayResize(workEma,Bars);

   //
   //
   //
   //
   //
      
   double alpha = 2.0 / (1.0+period);
          workEma[r][instanceNo] = workEma[r-1][instanceNo]+alpha*(price-workEma[r-1][instanceNo]);
   return(workEma[r][instanceNo]);
}

//
//
//
//
//

double workSmma[][_maWorkBufferx1];
double iSmma(double price, double period, int r, int instanceNo=0)
{
   if (ArrayRange(workSmma,0)!= Bars) ArrayResize(workSmma,Bars);

   //
   //
   //
   //
   //

   if (r<period)
         workSmma[r][instanceNo] = price;
   else  workSmma[r][instanceNo] = workSmma[r-1][instanceNo]+(price-workSmma[r-1][instanceNo])/period;
   return(workSmma[r][instanceNo]);
}

//
//
//
//
//

double workLwma[][_maWorkBufferx1];
double iLwma(double price, double period, int r, int instanceNo=0)
{
   if (ArrayRange(workLwma,0)!= Bars) ArrayResize(workLwma,Bars);
   
   //
   //
   //
   //
   //
   
   workLwma[r][instanceNo] = price;
      double sumw = period;
      double sum  = period*price;

      for(int k=1; k<period && (r-k)>=0; k++)
      {
         double weight = period-k;
                sumw  += weight;
                sum   += weight*workLwma[r-k][instanceNo];  
      }             
      return(sum/sumw);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

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; }
}