//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_color1  C'0,187,0'
#property indicator_color2  C'0,187,0'
#property indicator_color3  clrPaleVioletRed
#property indicator_color4  clrPaleVioletRed
#property indicator_color5  clrSilver
#property indicator_color6  clrGold
#property indicator_width1  2
#property indicator_style2  STYLE_DOT
#property indicator_width3  2
#property indicator_style4  STYLE_DOT
#property indicator_width5  2
#property indicator_width6  2
#property indicator_level1  0

//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame          = PERIOD_CURRENT;   // Time frame
extern double             FastPeriod         = 14;
extern double             SlowPeriod         = 34;
extern int                SignalPeriod       =  9;
extern int                SignalMethod       = MODE_EMA;
extern ENUM_APPLIED_PRICE Price              = PRICE_CLOSE;
extern int                RsiPeriod          = 14;
extern int                RsiMethod          = 0;
extern bool               alertsOn           = false;
extern bool               alertsOnCurrent    = false;
extern bool               alertsZeroCross    = true;
extern bool               alertsSignalCross  = false;
extern bool               alertsSlopeChange  = false;
extern bool               alertsMessage      = true;
extern bool               alertsNotification = false;
extern bool               alertsSound        = false;
extern bool               alertsEmail        = false;
extern string             _                  = "rsi methods";
extern string             _0                 = "rsi";
extern string             _1                 = "Wilder\'s rsi";
extern string             _2                 = "rsx";
extern string             _3                 = "Cuttler\'s rsi";
extern bool               Interpolate        = true;             // Interpolate in mtf mode

double macdhuu[],macdhud[],macdhdd[],macdhdu[],macd[],signal[],trend[],slope[],value[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,FastPeriod,SlowPeriod,SignalPeriod,SignalMethod,Price,RsiPeriod,RsiMethod,alertsOn,alertsOnCurrent,alertsZeroCross,alertsSignalCross,alertsSlopeChange,alertsMessage,alertsNotification,alertsSound,alertsEmail,"","","","","",_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   for (int i=0; i<indicator_buffers; i++) SetIndexStyle(i,DRAW_LINE);
   IndicatorBuffers(10);   
   SetIndexBuffer(0,macdhuu); SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(1,macdhud); SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(2,macdhdd); SetIndexStyle(2,DRAW_HISTOGRAM);
   SetIndexBuffer(3,macdhdu); SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexBuffer(4,macd);
   SetIndexBuffer(5,signal);
   SetIndexBuffer(6,slope);
   SetIndexBuffer(7,trend);
   SetIndexBuffer(8,value);
   SetIndexBuffer(9,count);
   
   indicatorFileName = WindowExpertName();
   TimeFrame         = fmax(TimeFrame,_Period);
      IndicatorShortName(timeFrameToString(TimeFrame)+" MACD "+getRsiName(RsiMethod)+" adaptive ("+DoubleToStr(FastPeriod,2)+","+DoubleToStr(SlowPeriod,2)+","+SignalPeriod+","+RsiPeriod+")");
   return(0);
}
int deinit() { return(0); }

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int start()
{
   int i,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = fmin(Bars-counted_bars,Bars-1); count[0] = limit;
         if (TimeFrame != _Period)
         {
            limit = (int)fmax(limit,fmin(Bars-1,_mtfCall(9,0)*TimeFrame/_Period));
            for (i=limit;i>=0 && !_StopFlag; i--)
            {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                     macdhuu[i] = _mtfCall(0,y);
   	               macdhud[i] = _mtfCall(1,y);
   	               macdhdd[i] = _mtfCall(2,y);
   	               macdhdu[i] = _mtfCall(3,y);
   	               macd[i]    = _mtfCall(4,y);
   	               signal[i]  = _mtfCall(5,y);
                     slope[i]   = _mtfCall(6,y);
                     trend[i]   = _mtfCall(7,y);
                     
                     //
                     //
                     //
                     //
                     //
                     
                      if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
                      #define _interpolate(buff) buff[i+k] = buff[i]+(buff[i+n]-buff[i])*k/n
                      int n,k; datetime time = iTime(NULL,TimeFrame,y);
                         for(n = 1; (i+n)<Bars && Time[i+n] >= time; n++) continue;	
                         for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++) 
                         {
                            _interpolate(macd);
                            _interpolate(signal);
                         }
            }
            for(i=limit; i>=0; i--) 
            {
               macdhuu[i] = (trend[i] == 1 && slope[i] == 1) ? macd[i] : EMPTY_VALUE;
               macdhud[i] = (trend[i] == 1 && slope[i] ==-1) ? macd[i] : EMPTY_VALUE;
               macdhdd[i] = (trend[i] ==-1 && slope[i] ==-1) ? macd[i] : EMPTY_VALUE;
               macdhdu[i] = (trend[i] ==-1 && slope[i] == 1) ? macd[i] : EMPTY_VALUE;    
            }
      return(0);
      }
      
      //
      //
      //
      //
      //
      
      for(i = limit; i>=0; i--)
      {
         double price = iMA(NULL,0,1,0,MODE_SMA,Price,i);
         double rsi = iRsi(price,RsiPeriod,RsiMethod,i);
         macd[i]    = iREma(price,FastPeriod,RsiPeriod,rsi,i,0)-iREma(price,SlowPeriod,RsiPeriod,rsi,i,1);
      }            
      for(i = limit; i>=0; i--)
      {
         if (SignalMethod==4)
               signal[i] = iREma(macd[i],SignalPeriod,RsiPeriod,rsi,i,2);
         else  signal[i] = iMAOnArray(macd,0,SignalPeriod,0,SignalMethod,i);

         //
         //
         //
         //
         //
                  
         slope[i] = (i<Bars-1) ? (macd[i]>macd[i+1]) ? 1 : (macd[i]<macd[i+1])   ? -1 : slope[i+1] : 0;  
         trend[i] = (i<Bars-1) ? (macd[i]>0)         ? 1 : (macd[i]<0)           ? -1 : trend[i+1] : 0;  
         value[i] = (i<Bars-1) ? (macd[i]>signal[i]) ? 1 : (macd[i]<signal[i])   ? -1 : value[i+1] : 0;
         macdhuu[i] = (trend[i] == 1 && slope[i] == 1) ? macd[i] : EMPTY_VALUE;
         macdhud[i] = (trend[i] == 1 && slope[i] ==-1) ? macd[i] : EMPTY_VALUE;
         macdhdd[i] = (trend[i] ==-1 && slope[i] ==-1) ? macd[i] : EMPTY_VALUE;
         macdhdu[i] = (trend[i] ==-1 && slope[i] == 1) ? macd[i] : EMPTY_VALUE;       
      }         
      manageAlerts();
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double ema[][3];
double iREma(double price, int emaPeriod, int rsiPeriod, double rsi, int i, int instanceNo=0)
{
   if (ArrayRange(ema,0)!=Bars) ArrayResize(ema,Bars); int r=Bars-i-1;
   
   //
   //
   //
   //
   //
   
   double RSvoltl = MathAbs(rsi-50)+1.0;
   double multi   = (5.0+100.0/rsiPeriod)/(0.06+0.92*RSvoltl+0.02*MathPow(RSvoltl,2));
   double alpha   = 2.0 /(1.0+multi*emaPeriod);
           ema[r][instanceNo] = ema[r-1][instanceNo]+alpha*(price-ema[r-1][instanceNo]);
   return( ema[r][instanceNo]);           
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
//

string rsiMethodNames[] = {"rsi","Wilders rsi","rsx","Cuttler RSI"};
string getRsiName(int& method)
{
   int max = ArraySize(rsiMethodNames)-1;
      method=MathMax(MathMin(method,max),0); return(rsiMethodNames[method]);
}

//
//
//
//
//

double workRsi[][13];
#define _price  0
#define _change 1
#define _changa 2

double iRsi(double price, double period, int rsiMode, int i, int instanceNo=0)
{
   if (ArrayRange(workRsi,0)!=Bars) ArrayResize(workRsi,Bars);
      int z = instanceNo*13; 
      int r = Bars-i-1;
   
   //
   //
   //
   //
   //
   
   workRsi[r][z+_price] = price;
   switch (rsiMode)
   {
      case 0:
         double alpha = 1.0/period; 
         if (r<period)
            {
               int k; double sum = 0; for (k=0; k<period && (r-k-1)>=0; k++) sum += MathAbs(workRsi[r-k][z+_price]-workRsi[r-k-1][z+_price]);
                  workRsi[r][z+_change] = (workRsi[r][z+_price]-workRsi[0][z+_price])/MathMax(k,1);
                  workRsi[r][z+_changa] =                                         sum/MathMax(k,1);
            }
         else
            {
               double change = workRsi[r][z+_price]-workRsi[r-1][z+_price];
                               workRsi[r][z+_change] = workRsi[r-1][z+_change] + alpha*(        change  - workRsi[r-1][z+_change]);
                               workRsi[r][z+_changa] = workRsi[r-1][z+_changa] + alpha*(MathAbs(change) - workRsi[r-1][z+_changa]);
            }
         if (workRsi[r][z+_changa] != 0)
               return(50.0*(workRsi[r][z+_change]/workRsi[r][z+_changa]+1));
         else  return(50.0);
         
      //
      //
      //
      //
      //
      
      case 1 :
         workRsi[r][z+1] = iSmma(0.5*(MathAbs(workRsi[r][z+_price]-workRsi[r-1][z+_price])+(workRsi[r][z+_price]-workRsi[r-1][z+_price])),0.5*(period-1),Bars-i-1,instanceNo*2+0);
         workRsi[r][z+2] = iSmma(0.5*(MathAbs(workRsi[r][z+_price]-workRsi[r-1][z+_price])-(workRsi[r][z+_price]-workRsi[r-1][z+_price])),0.5*(period-1),Bars-i-1,instanceNo*2+1);
         if((workRsi[r][z+1] + workRsi[r][z+2]) != 0) 
               return(100.0 * workRsi[r][z+1]/(workRsi[r][z+1] + workRsi[r][z+2]));
         else  return(50);

      //
      //
      //
      //
      //

      case 2 :     
         double Kg = (3.0)/(2.0+period), Hg = 1.0-Kg;
         if (r<period) { for (k=1; k<13; k++) workRsi[r][k+z] = 0; return(50); }  

         //
         //
         //
         //
         //
      
         double mom = workRsi[r][_price+z]-workRsi[r-1][_price+z];
         double moa = MathAbs(mom);
         for (k=0; k<3; k++)
         {
            int kk = k*2;
            workRsi[r][z+kk+1] = Kg*mom                + Hg*workRsi[r-1][z+kk+1];
            workRsi[r][z+kk+2] = Kg*workRsi[r][z+kk+1] + Hg*workRsi[r-1][z+kk+2]; mom = 1.5*workRsi[r][z+kk+1] - 0.5 * workRsi[r][z+kk+2];
            workRsi[r][z+kk+7] = Kg*moa                + Hg*workRsi[r-1][z+kk+7];
            workRsi[r][z+kk+8] = Kg*workRsi[r][z+kk+7] + Hg*workRsi[r-1][z+kk+8]; moa = 1.5*workRsi[r][z+kk+7] - 0.5 * workRsi[r][z+kk+8];
         }
         if (moa != 0)
              return(MathMax(MathMin((mom/moa+1.0)*50.0,100.00),0.00)); 
         else return(50);
            
      //
      //
      //
      //
      //
      
      case 3 :
         double sump = 0;
         double sumn = 0;
         for (k=0; k<period; k++)
         {
            double diff = workRsi[r-k][z+_price]-workRsi[r-k-1][z+_price];
               if (diff > 0) sump += diff;
               if (diff < 0) sumn -= diff;
         }
         if (sumn > 0)
               return(100.0-100.0/(1.0+sump/sumn));
         else  return(50);
   } 
   return(0);
}

//
//
//
//
//
//

double workSmma[][2];
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]);
}

//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      int whichBar = 1; if (alertsOnCurrent) whichBar = 0; 
      static datetime prevTime1  = 0;
      static string   prevAlert1 = "";
         if (alertsZeroCross && trend[whichBar] != trend[whichBar+1])
         {
            if (trend[whichBar] ==  1) doAlert(prevTime1,prevAlert1," crossed zero line up");
            if (trend[whichBar] == -1) doAlert(prevTime1,prevAlert1," crossed zero line down");
         }
      static datetime prevTime2  = 0;
      static string   prevAlert2 = "";
         if (alertsSignalCross && value[whichBar] != value[whichBar+1])
         {
            if (value[whichBar] ==  1) doAlert(prevTime2,prevAlert2," crossed signal line up");
            if (value[whichBar] == -1) doAlert(prevTime2,prevAlert2," crossed signal line down");
         }
      static datetime prevTime3  = 0;
      static string   prevAlert3 = "";
         if (alertsSlopeChange && slope[whichBar] != slope[whichBar+1])
         {
            if (slope[whichBar] ==  1) doAlert(prevTime3,prevAlert3," sloping up");
            if (slope[whichBar] == -1) doAlert(prevTime3,prevAlert3," sloping down");
         }
   }
}

//
//
//
//
//

void doAlert(datetime& previousTime, string& previousAlert, string doWhat)
{
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[0]) {
       previousAlert  = doWhat;
       previousTime   = Time[0];

       //
       //
       //
       //
       //

       message = timeFrameToString(_Period)+" "+_Symbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" MACD - rsi adaptive "+doWhat;
          if (alertsMessage)      Alert(message);
          if (alertsNotification) SendNotification(message);
          if (alertsEmail)        SendMail(StringConcatenate(Symbol()," MACD - rsi adaptive "),message);
          if (alertsSound)        PlaySound("alert2.wav");
   }
}

