//+------------------------------------------------------------------+
//|                                                 Accumulation.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "http://www.mql4.com"
#property description "Accumulation/Distribution"
#property strict

//--- indicator settings
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_color1  clrLightSeaGreen
#property indicator_color2  clrDodgerBlue
#property indicator_color3  clrSandyBrown
#property indicator_color4  clrDimGray
#property indicator_color5  clrLimeGreen
#property indicator_color6  clrLimeGreen
#property indicator_color7  clrOrangeRed
#property indicator_color8  clrOrangeRed
#property indicator_width1  2
#property indicator_style4  STYLE_DOT


enum enLevelType
{
   lev_float, // Floating levels
   lev_quan   // Quantile levels
};
enum enMaTypes
{
   ma_sma,    // Simple moving average
   ma_ema,    // Exponential moving average
   ma_smma,   // Smoothed MA
   ma_lwma    // Linear weighted MA
};

extern enLevelType    FlType                       = lev_quan;     // Levels type
extern int            FlPeriod                     = 35;           // Levels period
extern double         FlUp                         = 80;           // Upper level %
extern double         FlDown                       = 20;           // Lower level %
extern int            PriceSmoothing               = 0;            // Price smothing (<=1 no smoothing)
extern enMaTypes      PriceSmoothingMethod         = ma_ema;       // Price smothing method
extern bool           divergenceVisible            = false;        // Should the divergence be visible
extern bool           divergenceOnValuesVisible    = true;         // Divergence lines on RSI visible?
extern bool           divergenceOnChartVisible     = true;         // Divergence lines on main chart visible?
extern int            DivergearrowSize             = 1;            // Divergence arrows size
extern double         DivergencearrowsUpperGap     = 4;            // Upper Gap between divergence arrows and indicator line
extern double         DivergencearrowsLowerGap     = 4;            // Lower Gap between divergence arrows and indicator line
extern bool           ShowClassicalDivergence      = true;         // Classical divergence visible
extern bool           ShowHiddenDivergence         = true;         // Hidden divergence visible
extern color          divergenceBullishColor       = clrLimeGreen; // Bullish divergences color
extern color          divergenceBearishColor       = clrOrangeRed; // Bearish divergences color
extern int            ClassicDivergenceUpArrowCode = 233;          // classical divergence up arrow code
extern int            ClassicDivergenceDnArrowCode = 234;          // classical divergence dn arrow code
extern int            HiddenDivergenceUpArrowCode  = 159;          // hidden divergence up arrow code
extern int            HiddenDivergenceDnArrowCode  = 159;          // hidden divergence dn arrow code
extern string         divergenceUniqueID           = "ad div";     // Unique ID for divergence lines



//--- buffers
double ac[], buffer1[],buffer2[],buffer3[];
double cbulld[];
double hbulld[];
double cbeard[];
double hbeard[];
string shortName;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit(void)
{
   IndicatorBuffers(8);
   SetIndexBuffer(0, ac);
   SetIndexBuffer(1, buffer1);
   SetIndexBuffer(2, buffer2);
   SetIndexBuffer(3, buffer3);
   SetIndexBuffer(4, cbulld);  SetIndexStyle(4,DRAW_ARROW,0,DivergearrowSize); SetIndexArrow(4,ClassicDivergenceUpArrowCode);
   SetIndexBuffer(5, hbulld);  SetIndexStyle(5,DRAW_ARROW,0,DivergearrowSize); SetIndexArrow(5,HiddenDivergenceUpArrowCode);
   SetIndexBuffer(6, cbeard);  SetIndexStyle(6,DRAW_ARROW,0,DivergearrowSize); SetIndexArrow(6,ClassicDivergenceDnArrowCode);
   SetIndexBuffer(7, hbeard);  SetIndexStyle(7,DRAW_ARROW,0,DivergearrowSize); SetIndexArrow(7,HiddenDivergenceDnArrowCode);
   shortName = divergenceUniqueID+": A/D ("+(string)FlPeriod+","+(string)PriceSmoothing+" "+averageName(PriceSmoothingMethod)+")";
   IndicatorShortName(shortName);
}
void OnDeinit(const int reason) 
{ 
    int lookForLength = StringLen(divergenceUniqueID);
   
      for (int i=ObjectsTotal()-1; i>=0; i--) 
      {
         string name = ObjectName(i);  if (StringSubstr(name,0,lookForLength) == divergenceUniqueID) ObjectDelete(name);
      }
}
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[])
{
   int counted_bars = prev_calculated;
      if(counted_bars < 0) return(-1);
      if(counted_bars > 0) counted_bars--;
           int limit=fmin(rates_total-counted_bars,rates_total-1);
   
   //
   //
   //
   //
   //

   for (int i=limit; i>=0 && !_StopFlag; i--)
   {
      double prices[3];
             prices[0] = iCustomMa(PriceSmoothingMethod,close[i],PriceSmoothing,i,0);
             prices[1] = iCustomMa(PriceSmoothingMethod,low[i]  ,PriceSmoothing,i,1);
             prices[2] = iCustomMa(PriceSmoothingMethod,high[i] ,PriceSmoothing,i,2);
             ArraySort(prices);
               double diff=prices[2]-prices[0];
               ac[i]= (diff!=0) ? (double)tick_volume[i]*((prices[1]-prices[0])-(prices[2]-prices[1]))/diff : 0;
               if(i<rates_total-1)
                 ac[i]+=ac[i+1];
         
            if (FlType==lev_float)
            {
               double min   = ac[ArrayMinimum(ac,FlPeriod,i)];
               double max   = ac[ArrayMaximum(ac,FlPeriod,i)];
               double range = max-min;
                  buffer1[i] = min+FlUp  *range/100.0;
                  buffer2[i] = min+FlDown*range/100.0;
                  buffer3[i] = min+0.5*range;
            }
            else
            {
                  buffer1[i] = iQuantile(ac[i],FlPeriod, FlUp            ,i,Bars);
                  buffer2[i] = iQuantile(ac[i],FlPeriod, FlDown          ,i,Bars);
                  buffer3[i] = iQuantile(ac[i],FlPeriod,(FlUp+FlDown)/2.0,i,Bars);
            }  
            if (divergenceVisible)
            {
              CatchBullishDivergence(ac,i);
              CatchBearishDivergence(ac,i);
            } 
     }
   return(rates_total);
}
  
//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

#define _maInstances 3
#define _maWorkBufferx1 1*_maInstances
#define _maWorkBufferx2 2*_maInstances

string averageName(int mode)
{
   switch (mode)
   {
      case ma_sma   : return("SMA");
      case ma_ema   : return("EMA");
      case ma_smma  : return("SMMA");
      case ma_lwma  : return("LWMA");
   }
   return("");
}
double iCustomMa(int mode, double price, double length, int r, int instanceNo=0)
{
   r = Bars-r-1;
   switch (mode)
   {
      case ma_sma   : return(iSma(price,(int)length,r,instanceNo));
      case ma_ema   : return(iEma(price,length,r,instanceNo));
      case ma_smma  : return(iSmma(price,length,r,instanceNo));
      case ma_lwma  : return(iLwma(price,(int)length,r,instanceNo));
      default       : return(price);
   }
}

//
//
//
//
//

double workSma[][_maWorkBufferx2];
double iSma(double price, int period, int r, int instanceNo=0)
{
   if (period<=1) return(price);
   if (ArrayRange(workSma,0)!= Bars) ArrayResize(workSma,Bars); instanceNo *= 2; int k;

   //
   //
   //
   //
   //
      
   workSma[r][instanceNo+0] = price;
   workSma[r][instanceNo+1] = price; for(k=1; k<period && (r-k)>=0; k++) workSma[r][instanceNo+1] += workSma[r-k][instanceNo+0];  
   workSma[r][instanceNo+1] /= 1.0*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);

   //
   //
   //
   //
   //
      
   workEma[r][instanceNo] = price;
   if (r>0 && period>1)
          workEma[r][instanceNo] = workEma[r-1][instanceNo]+(2.0 / (1.0+period))*(price-workEma[r-1][instanceNo]);
   return(workEma[r][instanceNo]);
}

//
//
//
//
//

double workSmma[][_maWorkBufferx1];
double iSmma(double price, double period, int r, int instanceNo=0)
{
   if (period<=1) return(price);
   if (ArrayRange(workSmma,0)!= Bars) ArrayResize(workSmma,Bars);

   //
   //
   //
   //
   //

   workSmma[r][instanceNo] = price;
   if (r>0 && period>1)
          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);
   
   //
   //
   //
   //
   //
   
   period = fmax(period,1);
   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);
}
  
//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

#define _quantileInstances 1
double _sortQuant[];
double _workQuant[][_quantileInstances];

double iQuantile(double value, int period, double qp, int i, int bars, int instanceNo=0)
{
   if (period<1) return(value);
   if (ArrayRange(_workQuant,0)!=bars) ArrayResize(_workQuant,bars); 
   if (ArraySize(_sortQuant)!=period)  ArrayResize(_sortQuant,period); 
            i=bars-i-1; _workQuant[i][instanceNo]=value;
            int k=0; for (; k<period && (i-k)>=0; k++) _sortQuant[k] = _workQuant[i-k][instanceNo];
                     for (; k<period            ; k++) _sortQuant[k] = 0;
                     ArraySort(_sortQuant);

   //
   //
   //
   //
   //
   
   double index = (period-1.0)*qp/100.00;
   int    ind   = (int)index;
   double delta = index - ind;
   if (ind == NormalizeDouble(index,5))
         return(            _sortQuant[ind]);
   else  return((1.0-delta)*_sortQuant[ind]+delta*_sortQuant[ind+1]);
}   

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//


void CatchBullishDivergence(double& values[], int i)
{
   i++;
            cbulld[(int)fmin(i,Bars-1)] = EMPTY_VALUE;
            hbulld[(int)fmin(i,Bars-1)] = EMPTY_VALUE;
            ObjectDelete(divergenceUniqueID+"l"+ DoubleToStr(Time[(int)fmin(i,Bars-1)],0));
            ObjectDelete(divergenceUniqueID+"l"+"os" + DoubleToStr(Time[(int)fmin(i,Bars-1)],0));            
   if (!IsIndicatorLow(values,(int)fmin(i,Bars-1))) return;  

   //
   //
   //
   //
   //

   int currentLow = i;
   int lastLow    = GetIndicatorLastLow(values,i+1);
      if (currentLow>=0 && currentLow<Bars && lastLow>=0) 
      if (values[currentLow] > values[lastLow] && Low[currentLow] < Low[lastLow])
      {
        if (ShowClassicalDivergence)
        {
          cbulld[currentLow] = values[currentLow] - DivergencearrowsLowerGap;
          if(divergenceOnChartVisible)  DrawPriceTrendLine("l",Time[currentLow],Time[lastLow],Low[currentLow],Low[lastLow],          divergenceBullishColor,STYLE_SOLID);
          if(divergenceOnValuesVisible) DrawIndicatorTrendLine("l",Time[currentLow],Time[lastLow],values[currentLow],values[lastLow],divergenceBullishColor,STYLE_SOLID);
        }
      }
      
      if (currentLow>=0 && currentLow<Bars && lastLow>=0) 
      if (values[currentLow] < values[lastLow] && Low[currentLow] > Low[lastLow])
      {
        if (ShowHiddenDivergence)
        {
          hbulld[currentLow] = values[currentLow] - DivergencearrowsLowerGap;
          if(divergenceOnChartVisible)  DrawPriceTrendLine("l",Time[currentLow],Time[lastLow],Low[currentLow],Low[lastLow],           divergenceBullishColor, STYLE_DOT);
          if(divergenceOnValuesVisible) DrawIndicatorTrendLine("l",Time[currentLow],Time[lastLow],values[currentLow],values[lastLow], divergenceBullishColor, STYLE_DOT);
        }
      }
}

//
//
//
//
//

void CatchBearishDivergence(double& values[], int i)
{
   i++; 
            cbeard[(int)fmin(i,Bars-1)] = EMPTY_VALUE;
            hbeard[(int)fmin(i,Bars-1)] = EMPTY_VALUE;
            ObjectDelete(divergenceUniqueID+"h"+DoubleToStr(Time[(int)fmin(i,Bars-1)],0));
            ObjectDelete(divergenceUniqueID+"h"+"os" + DoubleToStr(Time[(int)fmin(i,Bars-1)],0));            
   if (IsIndicatorPeak(values,(int)fmin(i,Bars-1)) == false) return;

   //
   //
   //
   //
   //
      
   int currentPeak = i;
   int lastPeak = GetIndicatorLastPeak(values,i+1);
      if (currentPeak>=0 && currentPeak<Bars && lastPeak>=0) 
      if (values[currentPeak] < values[lastPeak] && High[currentPeak]>High[lastPeak])
      {
        if (ShowClassicalDivergence)
        {
          cbeard[currentPeak] = values[currentPeak] + DivergencearrowsUpperGap;
          if (divergenceOnChartVisible)  DrawPriceTrendLine("h",Time[currentPeak],Time[lastPeak],High[currentPeak],High[lastPeak],        divergenceBearishColor,STYLE_SOLID);
          if (divergenceOnValuesVisible) DrawIndicatorTrendLine("h",Time[currentPeak],Time[lastPeak],values[currentPeak],values[lastPeak],divergenceBearishColor,STYLE_SOLID);
        }
      }
      if (currentPeak>=0 && currentPeak<Bars && lastPeak>=0) 
      if (values[currentPeak] > values[lastPeak] && High[currentPeak] < High[lastPeak])
      {
        if (ShowHiddenDivergence)
        {
          hbeard[currentPeak] = values[currentPeak] + DivergencearrowsUpperGap;
          if (divergenceOnChartVisible)  DrawPriceTrendLine("h",Time[currentPeak],Time[lastPeak],High[currentPeak],High[lastPeak],         divergenceBearishColor, STYLE_DOT);
          if (divergenceOnValuesVisible) DrawIndicatorTrendLine("h",Time[currentPeak],Time[lastPeak],values[currentPeak],values[lastPeak], divergenceBearishColor, STYLE_DOT);
        }
      }
}

//
//
//
//
//

bool IsIndicatorPeak(double& values[], int i) { return(values[i] >= values[(int)fmin(i+1,Bars-1)] && values[i] > values[(int)fmin(i+2,Bars-1)] && values[i] > values[(int)fmax(i-1,0)]); }
bool IsIndicatorLow( double& values[], int i) { return(values[i] <= values[(int)fmin(i+1,Bars-1)] && values[i] < values[(int)fmin(i+2,Bars-1)] && values[i] < values[(int)fmax(i-1,0)]); }

int GetIndicatorLastPeak(double& values[], int shift)
{
   for(int i = shift+5; i<Bars && (i+2)<(Bars-1) && (i-2)>=0; i++)
         if (values[i] >= values[i+1] && values[i] > values[i+2] && values[i] >= values[i-1] && values[i] > values[i-2]) return(i);
   return(-1);
}
int GetIndicatorLastLow(double& values[], int shift)
{
   for(int i = shift+5; i<Bars && (i+2)<(Bars-1) && (i-2)>=0; i++)
         if (values[i] <= values[i+1] && values[i] < values[i+2] && values[i] <= values[i-1] && values[i] < values[i-2]) return(i);
   return(-1);
}

//+------------------------------------------------------------------
//|                                                                  
//+------------------------------------------------------------------
//
//
//
//
//

void DrawPriceTrendLine(string first,datetime t1, datetime t2, double p1, double p2, color lineColor, double style)
{
   string   label = divergenceUniqueID+first+"os"+DoubleToStr(t1,0);
   ObjectDelete(label);
      ObjectCreate(label, OBJ_TREND, 0, t1+Period()*60-1, p1, t2, p2, 0, 0);
         ObjectSet(label, OBJPROP_RAY, false);
         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 = divergenceUniqueID+first+DoubleToStr(t1,0);
   ObjectDelete(label);
      ObjectCreate(label, OBJ_TREND, indicatorWindow, t1+Period()*60-1, p1, t2, p2, 0, 0);
         ObjectSet(label, OBJPROP_RAY, false);
         ObjectSet(label, OBJPROP_COLOR, lineColor);
         ObjectSet(label, OBJPROP_STYLE, style);
}

