//------------------------------------------------------------------
//
//------------------------------------------------------------------
#property  copyright "www.forex-tsd.com"
#property  link      "www.forex-tsd.com"

#property indicator_separate_window
#property indicator_buffers    3
#property indicator_color1     LimeGreen
#property indicator_color2     Red
#property indicator_color3     DodgerBlue
#property indicator_width3     2
#property indicator_levelcolor DarkGoldenrod

//
//
//
//
//

extern string TimeFrame               = "Current time frame";
extern int    MomPeriod               = 14;
extern int    MomPrice                = 0;
extern int    arrowSize               = 1; 

extern string separator2              = "Indicator Settings";
extern bool   drawDivergences         = true;
extern bool   drawIndicatorTrendLines = true;
extern bool   drawPriceTrendLines     = true;
extern string drawLinesIdentificator  = "momdiverge1";
extern bool   displayAlert            = true;
extern bool   alertsMessage           = true;
extern bool   alertsSound             = true;
extern bool   alertsEmail             = false;
extern bool   alertsPushnotification  = FALSE;
extern string NameSound               = "alert1.wav";

//
//
//
//
//

double mom[];
double bullishDivergence[];
double bearishDivergence[];
double price[];

//
//
//
//
//

static datetime lastAlertTime;
string indicatorName;
string labelNames;

string indicatorFileName;
bool   calculateValue;
bool   returnBars;
int    timeFrame;
double pipMultiplier = 1;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

int init()
{
   IndicatorBuffers(4); 
   SetIndexBuffer(0,bullishDivergence); SetIndexStyle(0,DRAW_ARROW,0,arrowSize); SetIndexArrow(0,233);
   SetIndexBuffer(1,bearishDivergence); SetIndexStyle(1,DRAW_ARROW,0,arrowSize); SetIndexArrow(1,234);  
   SetIndexBuffer(2,mom);
   SetIndexBuffer(3,price);
   SetLevelValue(0,0);
   
      //
      //
      //
      //
      //
      labelNames    = "Momentum_DivergenceLine "+drawLinesIdentificator+":";
      indicatorName = "Momentum Divergence";
      IndicatorShortName(indicatorName);
      
      //
      //
      //
      //
      //
      
      indicatorFileName = WindowExpertName();
      calculateValue    = (TimeFrame=="calculateValue"); if (calculateValue) return(0);
      returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
      timeFrame         = stringToTimeFrame(TimeFrame);
   
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);
}

//+------------------------------------------------------------------+
//| Momentum                                                         |
//+------------------------------------------------------------------+
//
//

int start()
{
   int counted_bars=IndicatorCounted();
   int i,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
           limit=MathMin(Bars-1,Bars-counted_bars);
           if (returnBars) { bullishDivergence[0] = limit+1; return(0); }
           if (Digits==3 || Digits==5) 
                 pipMultiplier = 10;
           else  pipMultiplier = 1; 


   //
   //
   //
   //
   //
   
   if (calculateValue || timeFrame == Period())
   {

     for (i=limit; i>=0; i--)
     {
        
       price[i] = iMA(NULL,0,1,0,MODE_SMA,MomPrice,i);
       mom[i]   = price[i] - price[i+MomPeriod];
       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]);
           mom[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",MomPeriod,MomPrice,arrowSize,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,alertsMessage,alertsSound,alertsEmail,NameSound,2,y); 
           bullishDivergence[i] = EMPTY_VALUE;
           bearishDivergence[i] = EMPTY_VALUE;

           int firstBar = iBarShift(NULL,0,iTime(NULL,timeFrame,y));
           if (i==firstBar)
           {
             bullishDivergence[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",MomPeriod,MomPrice,arrowSize,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,alertsMessage,alertsSound,alertsEmail,NameSound,0,y);
             bearishDivergence[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",MomPeriod,MomPrice,arrowSize,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,alertsMessage,alertsSound,alertsEmail,NameSound,1,y); 
           }
      }               
return(0);
}

//
//
//
//
//

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(mom[currentLow] > mom[lastLow] && Low[currentLow] < Low[lastLow])
   {
      bullishDivergence[currentLow] = mom[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],mom[currentLow],mom[lastLow],Green,STYLE_SOLID);
      if(displayAlert == true)
                 DisplayAlert("Classical bullish divergence on: ",currentLow);  
                        
   }
     
   //
   //
   //
   //
   //
        
   if(mom[currentLow] < mom[lastLow] && Low[currentLow] > Low[lastLow])
   {
      bullishDivergence[currentLow] = mom[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],mom[currentLow], mom[lastLow], Green, STYLE_DOT);
      if(displayAlert == true)
                 DisplayAlert("Reverse bullish divergence on: ",currentLow); 
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

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(mom[currentPeak] < mom[lastPeak] && High[currentPeak]>High[lastPeak])
   {
       bearishDivergence[currentPeak] = mom[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],mom[currentPeak],mom[lastPeak],Red,STYLE_SOLID);
       if(displayAlert == true)
               DisplayAlert("Classical bearish divergence on: ",currentPeak); 
                        
   }
   
   //
   //
   //
   //
   //

   if(mom[currentPeak] > mom[lastPeak] && High[currentPeak] < High[lastPeak])
   {
      bearishDivergence[currentPeak] = mom[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],mom[currentPeak],mom[lastPeak], Red, STYLE_DOT);
      if(displayAlert == true)
               DisplayAlert("Reverse bearish divergence on: ",currentPeak);
                         
   }   
}

//
//
//
//
//

bool IsIndicatorPeak(int shift)
{
   if(mom[shift] >= mom[shift+1] && mom[shift] > mom[shift+2] && mom[shift] > mom[shift-1])
       return(true);
   else 
       return(false);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

bool IsIndicatorLow(int shift)
{
   if(mom[shift] <= mom[shift+1] && mom[shift] < mom[shift+2] && mom[shift] < mom[shift-1])
       return(true);
   else 
       return(false);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

int GetIndicatorLastPeak(int shift)
{
    for(int i = shift+5; i < Bars; i++)
    {
       if(mom[i] >= mom[i+1] && mom[i] > mom[i+2] && mom[i] >= mom[i-1] && mom[i] > mom[i-2])
         return(i);
    }
return(-1);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

int GetIndicatorLastLow(int shift)
{
    for (int i = shift+5; i < Bars; i++)
    {
       if (mom[i] <= mom[i+1] && mom[i] < mom[i+2] && mom[i] <= mom[i-1] && mom[i] < mom[i-2])
         return(i);
}
     
return(-1);
}

//
//
//
//
//

void DisplayAlert(string message, int shift)
{
    if(shift <= 2 && Time[shift] != lastAlertTime)
    {
      lastAlertTime = Time[shift];
      if (alertsMessage) Alert(message+ Symbol()+ " , "+Period()+ " minutes chart");
      if (alertsEmail)   SendMail(StringConcatenate(Symbol()," Momentum "),message);
      if (alertsSound)   PlaySound(NameSound); 
      if (alertsPushnotification) SendNotification(message+ Symbol()+ " , "+Period()+ " minutes chart");
    }
}

//
//
//
//
//

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(indicatorName);
    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 StringUpperCase(string str)
{
   string   s = str;
   for (int length=StringLen(str)-1; length>=0; length--)
   {
      int char = StringGetChar(s, length);
         if((char > 96 && char < 123) || (char > 223 && char < 256))
                     s = StringSetChar(s, length, char - 32);
         else if(char > -33 && char < 0)
                     s = StringSetChar(s, length, char + 224);
   }
   return(s);
}