//+------------------------------------------------------------------+
//|                                                    kase peak.mq4 |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_separate_window
#property indicator_buffers  7
#property indicator_color1   DimGray
#property indicator_color2   DimGray
#property indicator_color3   Magenta
#property indicator_color4   DeepSkyBlue
#property indicator_color5   Magenta
#property indicator_color6   LimeGreen
#property indicator_color7   Red
#property indicator_width3   2
#property indicator_width4   2
#property indicator_width5   2
#property indicator_level1   0
#property indicator_levelcolor DimGray 

//
//
//
//
//

extern string TimeFrame               = "Current time frame";
extern double kcdDeviations           = 2.0;
extern int    kcdShortCycle           = 8;
extern int    kcdLongCycle            = 65;
extern double kcdSensitivity          = 40;
extern bool   allPeaksMode            = false;
extern bool   drawDivergences         = true;
extern bool   drawIndicatorTrendLines = true;
extern bool   drawPriceTrendLines     = true;
extern string drawLinesIdentificator  = "kaseCD1";
extern bool   Interpolate             = true; 

//
//
//
//
//

double kchBuffer[];
double kcdBuffer[];
double kdeBuffer[];
double kcmBuffer[];
double kcpBuffer[];
double bullishDivergence[];
double bearishDivergence[];
double wrkBuffer[][6];
string shortName;
string labelNames;

//
//
//
//
//

string indicatorFileName;
bool   calculateValue;
bool   returnBars;
int    timeFrame;
double pipMultiplier = 1;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   SetIndexBuffer(0,kchBuffer); SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(1,kcdBuffer); SetIndexLabel(1,NULL);
   SetIndexBuffer(2,kdeBuffer);
   SetIndexBuffer(3,kcmBuffer);
   SetIndexBuffer(4,kcpBuffer); SetIndexStyle(4,DRAW_HISTOGRAM); SetIndexLabel(4,"kpp");
   SetIndexBuffer(5,bullishDivergence); SetIndexStyle(5, DRAW_ARROW); SetIndexArrow(5, 233);
   SetIndexBuffer(6,bearishDivergence); SetIndexStyle(6, DRAW_ARROW); SetIndexArrow(6, 234);
   
   //
   //
   //
   //
   //
   
   timeFrame  = stringToTimeFrame(TimeFrame);   
   labelNames = "Kase CD oscillator_DivergenceLine "+drawLinesIdentificator+":";
   shortName  = timeFrameToString(timeFrame)+"  Kase CD oscillator div ("+DoubleToStr(kcdDeviations,2)+","+kcdShortCycle+","+kcdLongCycle+","+DoubleToStr(kcdSensitivity,2)+")";
   IndicatorShortName(shortName);
   
   //
   //
   //
   //
   //
   
   indicatorFileName = WindowExpertName();
   calculateValue    = (TimeFrame=="calculateValue"); if (calculateValue) return(0);
   returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
      
return(0);
}

//
//
//
//
//

int deinit()
{
   int length=StringLen(labelNames);
   for(int i=ObjectsTotal()-1; i>=0; i--)
   {
       string name = ObjectName(i);
       if(StringSubstr(name,0,length) == labelNames)  ObjectDelete(name);   
   }
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define ccLog 0
#define ccDev 1
#define x1    2
#define xs    3
#define xp    4
#define xpAbs 5

//
//
//
//
//

int start()
{
   int counted_bars=IndicatorCounted();
   int i,r,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = MathMin(Bars-counted_bars,Bars-kcdLongCycle);
         if (returnBars) { kchBuffer[0] = limit+1; return(0); }
         if (Digits==3 || Digits==5) 
                 pipMultiplier = 10;
           else  pipMultiplier = 1; 
               
   //
   //
   //
   //
   //
   
   if (calculateValue || timeFrame==Period())
   {
     if (ArrayRange(wrkBuffer,0) != Bars) ArrayResize(wrkBuffer,Bars);
     for(i=limit,r=Bars-i-1; i>=0; i--,r++)
     {
        wrkBuffer[r][x1]    = wrkBuffer[r-1][x1];
        wrkBuffer[r][xs]    = wrkBuffer[r-1][xs];
        wrkBuffer[r][ccLog] = MathLog(Close[i]/Close[i+1]);
        wrkBuffer[r][ccDev] = iDeviation(ccLog,9,r);

        //
        //
        //
        //
        //
      
        double avg = iSma(ccDev,30,r);
           if (avg>0)
           {
              double max1 = 0;
              double maxs = 0;
                 for (int k=kcdShortCycle; k<kcdLongCycle; k++)
                 {
                    max1 = MathMax(MathLog(High[i]/Low[i+k])/MathSqrt(k),max1);
                    maxs = MathMax(MathLog(High[i+k]/Low[i])/MathSqrt(k),maxs);
                 }                  
              wrkBuffer[r][x1] = max1/avg;
              wrkBuffer[r][xs] = maxs/avg;
           }
           wrkBuffer[r][xp]    = kcdSensitivity*(iSma(x1,3,r)-iSma(xs,3,r));
           wrkBuffer[r][xpAbs] = MathAbs(wrkBuffer[r][xp]);

           //
           //
           //
           //
           //

           kcpBuffer[i+1] = EMPTY_VALUE;      
           kcdBuffer[i]   = wrkBuffer[r][xp]-iSma(xp,9,r);
           kchBuffer[i]   = kcdBuffer[i];
              double tmpVal = iSma(xpAbs,50,r)+kcdDeviations*(iDeviation(xpAbs,50,r));
              double maxVal = MathMax(90.0,tmpVal);
              double minVal = MathMin(90.0,tmpVal);

           if (kcdBuffer[i] > 0) { kdeBuffer[i] =  maxVal; kcmBuffer[i] =  minVal; }
           else                  { kdeBuffer[i] = -maxVal; kcmBuffer[i] = -minVal; }
      
      //
      //
      //
      //
      //

      if (!allPeaksMode)
      {         
         if (kcdBuffer[i+1]>0 && kcdBuffer[i+1]>kcdBuffer[i] && kcdBuffer[i+1]>=kcdBuffer[i+2] && kcdBuffer[i+1]>= maxVal) kcpBuffer[i+1] = kcdBuffer[i+1];
         if (kcdBuffer[i+1]<0 && kcdBuffer[i+1]<kcdBuffer[i] && kcdBuffer[i+1]<=kcdBuffer[i+2] && kcdBuffer[i+1]<=-maxVal) kcpBuffer[i+1] = kcdBuffer[i+1];
      }
      else
      {
         if (kcdBuffer[i+1]>0 && kcdBuffer[i+1]>kcdBuffer[i] && kcdBuffer[i+1]>=kcdBuffer[i+2]) kcpBuffer[i+1] = kcdBuffer[i+1];
         if (kcdBuffer[i+1]<0 && kcdBuffer[i+1]<kcdBuffer[i] && kcdBuffer[i+1]<=kcdBuffer[i+2]) kcpBuffer[i+1] = kcdBuffer[i+1];
      }         
      if (drawDivergences)
      {
         CatchBullishDivergence(i);
         CatchBearishDivergence(i);
      }         
   }
   return(0);
   }
   
   //
   //
   //
   //
   //
    
   limit = MathMax(limit,MathMin(Bars-1,iCustom(NULL,timeFrame,indicatorFileName,"returnBars",0,0)*timeFrame/Period()));
   for (i=limit;i>=0; i--)
   {
      int y = iBarShift(NULL,timeFrame,Time[i]);
      int x = iBarShift(NULL,timeFrame,Time[i+1]);
         kchBuffer[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,0,y);
         kcdBuffer[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,1,y);
         kdeBuffer[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,2,y);
         kcmBuffer[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,3,y);
         kcpBuffer[i]         = EMPTY_VALUE;
         bullishDivergence[i] = EMPTY_VALUE;
         bearishDivergence[i] = EMPTY_VALUE;
         
         if (x!=y)
            kcpBuffer[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,4,y);
            
         int firstBar = iBarShift(NULL,0,iTime(NULL,timeFrame,y));
         if (i==firstBar)
         {
            bullishDivergence[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,5,y);
            bearishDivergence[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",kcdDeviations,kcdShortCycle,kcdLongCycle,kcdSensitivity,allPeaksMode,drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,6,y);
         }
          
         //
         //
         //
         //
         //
            
         if (!Interpolate || y==iBarShift(NULL,timeFrame,Time[i-1])) continue;

         //
         //
         //
         //
         //

         datetime time = iTime(NULL,timeFrame,y);
           for(int n = 1; i+n < Bars && Time[i+n] >= time; n++) continue;	
           for(int s = 1; s < n; s++)
           {
  	           kchBuffer[i+s] = kchBuffer[i] + (kchBuffer[i+n] - kchBuffer[i]) * s/n;
  	           kcdBuffer[i+s] = kcdBuffer[i] + (kcdBuffer[i+n] - kcdBuffer[i]) * s/n;
  	           kdeBuffer[i+s] = kdeBuffer[i] + (kdeBuffer[i+n] - kdeBuffer[i]) * s/n;
  	           kcmBuffer[i+s] = kcmBuffer[i] + (kcmBuffer[i+n] - kcmBuffer[i]) * s/n;
           }     
    }
return(0);
}
        
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iDeviation(int forDim, int period, int pos)
{
   double dMA  = iSma(forDim,period,pos);
   double dSum = 0;
      for(int i=0; i<period; i++,pos--) dSum += (wrkBuffer[pos][forDim]-dMA)*(wrkBuffer[pos][forDim]-dMA);
   return(MathSqrt(dSum/period));
}

//
//
//
//
//

double iSma(int forDim, int period, int pos)
{
   double sum = wrkBuffer[pos][forDim];
      for(int i=1; i<period; i++) sum += wrkBuffer[pos-i][forDim];
   return(sum/period);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void CatchBullishDivergence(int shift)
{
   shift++;
         bullishDivergence[shift] = EMPTY_VALUE;
            ObjectDelete(labelNames+"l"+DoubleToStr(Time[shift],0));
            ObjectDelete(labelNames+"l"+"os" + DoubleToStr(Time[shift],0));            
   if(!IsIndicatorLow(shift)) return;  

   //
   //
   //
   //
   //
      
   int currentLow = shift;
   int lastLow = GetIndicatorLastLow(shift+1);

   //
   //
   //
   //
   //
    
   if(kcdBuffer[currentLow] > kcdBuffer[lastLow] && Low[currentLow] < Low[lastLow])
   {
      bullishDivergence[currentLow] = kcdBuffer[currentLow] - Point*pipMultiplier;
      if(drawPriceTrendLines == true)
                 DrawPriceTrendLine("l",Time[currentLow],Time[lastLow],Low[currentLow],Low[lastLow],Green,STYLE_SOLID);
      if(drawIndicatorTrendLines == true)
                 DrawIndicatorTrendLine("l",Time[currentLow],Time[lastLow],kcdBuffer[currentLow],kcdBuffer[lastLow],Green,STYLE_SOLID);
   }
     
   //
   //
   //
   //
   //
        
   if(kcdBuffer[currentLow] < kcdBuffer[lastLow] && Low[currentLow] > Low[lastLow])
   {
      bullishDivergence[currentLow] = kcdBuffer[currentLow] - Point*pipMultiplier;
      if(drawPriceTrendLines == true)
                 DrawPriceTrendLine("l",Time[currentLow],Time[lastLow],Low[currentLow],Low[lastLow], Green, STYLE_DOT);
      if(drawIndicatorTrendLines == true)                            
                 DrawIndicatorTrendLine("l",Time[currentLow],Time[lastLow],kcdBuffer[currentLow], kcdBuffer[lastLow], Green, STYLE_DOT);
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void CatchBearishDivergence(int shift)
{
   shift++; 
         bearishDivergence[shift] = EMPTY_VALUE;
            ObjectDelete(labelNames+"h"+DoubleToStr(Time[shift],0));
            ObjectDelete(labelNames+"h"+"os" + DoubleToStr(Time[shift],0));            
   if(IsIndicatorPeak(shift) == false) return;
   int currentPeak = shift;
   int lastPeak = GetIndicatorLastPeak(shift+1);

   //
   //
   //
   //
   //
      
   if(kcdBuffer[currentPeak] < kcdBuffer[lastPeak] && High[currentPeak]>High[lastPeak])
   {
       bearishDivergence[currentPeak] = kcdBuffer[currentPeak] + Point*pipMultiplier;
       if(drawPriceTrendLines == true)
               DrawPriceTrendLine("h",Time[currentPeak],Time[lastPeak],High[currentPeak],High[lastPeak],Red,STYLE_SOLID);
       if(drawIndicatorTrendLines == true)
               DrawIndicatorTrendLine("h",Time[currentPeak],Time[lastPeak],kcdBuffer[currentPeak],kcdBuffer[lastPeak],Red,STYLE_SOLID);
   }
   
   //
   //
   //
   //
   //

   if(kcdBuffer[currentPeak] > kcdBuffer[lastPeak] && High[currentPeak] < High[lastPeak])
   {
      bearishDivergence[currentPeak] = kcdBuffer[currentPeak] + Point*pipMultiplier;
      if (drawPriceTrendLines == true)
               DrawPriceTrendLine("h",Time[currentPeak],Time[lastPeak],High[currentPeak],High[lastPeak], Red, STYLE_DOT);
      if (drawIndicatorTrendLines == true)
               DrawIndicatorTrendLine("h",Time[currentPeak],Time[lastPeak],kcdBuffer[currentPeak],kcdBuffer[lastPeak], Red, STYLE_DOT);
   }   
}

//
//
//

bool IsIndicatorPeak(int shift)
{
   if(kcpBuffer[shift] != EMPTY_VALUE && kcpBuffer[shift]>0)
        return(true);
   else return(false);
}
bool IsIndicatorLow(int shift)
{
   if(kcpBuffer[shift] != EMPTY_VALUE && kcpBuffer[shift]<0)
        return(true);
   else return(false);
}
int GetIndicatorLastPeak(int shift)
{
   for(int j=shift; j<Bars; j++) if(kcpBuffer[j] != EMPTY_VALUE && kcpBuffer[j]>0) return(j);
   return(-1);
}
int GetIndicatorLastLow(int shift)
{
   for(int j=shift; j<Bars; j++) if(kcpBuffer[j] != EMPTY_VALUE && kcpBuffer[j]<0) return(j);
   return(-1);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void DrawPriceTrendLine(string first,datetime t1, datetime t2, double p1, double p2, color lineColor, double style)
{
   string label = labelNames+first+"os"+DoubleToStr(t1,0);
   ObjectDelete(label);
      ObjectCreate(label, OBJ_TREND, 0, t1, p1, t2, p2, 0, 0);
         ObjectSet(label, OBJPROP_RAY, 0);
         ObjectSet(label, OBJPROP_COLOR, lineColor);
         ObjectSet(label, OBJPROP_STYLE, style);
}
void DrawIndicatorTrendLine(string first,datetime t1, datetime t2, double p1, double p2, color lineColor, double style)
{
   int indicatorWindow = WindowFind(shortName);
   if (indicatorWindow < 0) return;
   
   string label = labelNames+first+DoubleToStr(t1,0);
   ObjectDelete(label);
      ObjectCreate(label, OBJ_TREND, indicatorWindow, t1, p1, t2, p2, 0, 0);
         ObjectSet(label, OBJPROP_RAY, 0);
         ObjectSet(label, OBJPROP_COLOR, lineColor);
         ObjectSet(label, OBJPROP_STYLE, style);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

//
//
//
//
//

int stringToTimeFrame(string tfs)
{
   tfs = StringUpperCase(tfs);
   for (int i=ArraySize(iTfTable)-1; i>=0; i--)
         if (tfs==sTfTable[i] || tfs==""+iTfTable[i]) return(MathMax(iTfTable[i],Period()));
                                                      return(Period());
}

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//
//
//
//
//

string StringUpperCase(string str)
{
   string   s = str;
   for (int length=StringLen(str)-1; length>=0; length--)
   {
      int tchar = StringGetChar(s, length);
         if((tchar > 96 && tchar < 123) || (tchar > 223 && tchar < 256))
                     s = StringSetChar(s, length, tchar - 32);
         else if(tchar > -33 && tchar < 0)
                     s = StringSetChar(s, length, tchar + 224);
   }
   return(s);
}