//+------------------------------------------------------------------+
//|                                                      !GP QQE.mq4 |
//|                                 Copyright © 2009, Jaanus Jantson |
//|                                                jaanus@jantson.ee |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2009, Jaanus Jantson"
#property link      "jaanus@jantson.ee"

#property indicator_separate_window
#property indicator_buffers         4
#property indicator_color1          Navy
#property indicator_style1          STYLE_SOLID
#property indicator_width1          2
#property indicator_color2          Navy
#property indicator_style2          STYLE_DOT
#property indicator_color3 Blue
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
#property indicator_color4 Red
#property indicator_style4 STYLE_SOLID
#property indicator_width4 2
#property indicator_level1 50
#property indicator_level2 0
#property indicator_levelcolor Red
#property indicator_maximum 100
#property indicator_minimum -10


extern int     Smoothing_Period     = 5;
extern int     RSI_Timeframe        = PERIOD_M15;
extern int     RSI_Period           = 14;
extern double  D_Factor             = 4.236;

extern bool    Smoothing_EMA        = true;
extern bool    Smoothing_LR         = false;
extern bool    Smoothing_KAMA       = false;
extern int     KAMA_Fast_Period     = 2;
extern int     KAMA_Slow_Period     = 30;

extern bool    Show_Signal_Arrows   = true;
extern int     Signal_Distance_Pips = 10;

extern bool    Play_Signal_Sound    = true;
extern string  Play_Signal_Name     = "Trumpet1.wav";




int Wilders_Period;
int StartBar;
int LastCalculatedBarIndex;
int Sm_Per;
double TrLevelSlow[];
double AtrRsi[];
double MaAtrRsi[];
double Rsi[];
double RsiMa[];
double QQE_UP[];
double QQE_DOWN[];
double QQE_TREND[];
datetime Previous_Signal_Time;


int init()
{
   string Smoothing_Method;

   Sm_Per=Smoothing_Period;
   if(Sm_Per<1) Sm_Per=1;
   if((Smoothing_LR)&&(Sm_Per<2)) Sm_Per=2;

   Previous_Signal_Time=Time[0];

   if(Period()>RSI_Timeframe) RSI_Timeframe=Period();
   Wilders_Period=RSI_Period*2-1;
   if (Wilders_Period < Sm_Per) StartBar=Sm_Per;
   else StartBar=Wilders_Period;

   if(Smoothing_EMA) Smoothing_Method="Exponential MA";
   if(Smoothing_LR) Smoothing_Method="Linear Regression";
   if(Smoothing_KAMA) Smoothing_Method="Kaufman's Adaptive MA";

   IndicatorDigits(Digits+2);
   IndicatorBuffers(8);
   SetIndexBuffer(0, RsiMa);
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2);
   SetIndexLabel(0, "RSI Smoothed");
   SetIndexDrawBegin(0, StartBar);
   SetIndexStyle(1, DRAW_LINE, STYLE_DOT);
   SetIndexBuffer(1, TrLevelSlow);
   SetIndexLabel(1, "Trailing Slow Level");
   SetIndexBuffer(2, QQE_UP);
   SetIndexStyle(2, DRAW_HISTOGRAM, STYLE_SOLID, 2);
   SetIndexLabel(2, "QQE UP");
   SetIndexDrawBegin(2, StartBar);
   SetIndexBuffer(3, QQE_DOWN);
   SetIndexStyle(3, DRAW_HISTOGRAM, STYLE_SOLID, 2);
   SetIndexLabel(3, "QQE DOWN");
   SetIndexDrawBegin(3, StartBar);
   SetIndexBuffer(4, AtrRsi);
   SetIndexBuffer(5, MaAtrRsi);
   SetIndexBuffer(6, Rsi);
   SetIndexBuffer(7, QQE_TREND);
   IndicatorShortName(StringConcatenate("QQE inside bar 2.0 (Per_",RSI_Timeframe,", ",Sm_Per,", ",Smoothing_Method,")"));

   return(0);
}

int deinit()
{
   string   Obj_Name;
   for(int i=0;i<Bars;i++)
   {
      Obj_Name="BUY"+Time[i];
      if(ObjectFind(Obj_Name)!=-1) ObjectDelete(Obj_Name);
      
      Obj_Name="SELL"+Time[i];
      if(ObjectFind(Obj_Name)!=-1) ObjectDelete(Obj_Name);
   }
   
   return(0);
}


int start()
{
   int      counted=IndicatorCounted(),
            i,
            i_RSI_TF,
            jj;
   double   dar,
            RSI2,
            RSI1,
            vahe21,
            vahe10,
            G21,
            G10,
            L21,
            L10,
            RS2,
            RS1,
            RS0,
            AG0,
            AL0;
   string   Arrow_Name;

   if(Smoothing_EMA+Smoothing_LR+Smoothing_KAMA!=1)
   {
      Alert("Choose only one smoothing method: Smoothing_EMA, Smoothing_LR or Smoothing_KAMA");
      return(0);
   }
   if(Bars<=StartBar) return(0);
   LastCalculatedBarIndex=Bars - counted - 1;

   for(i=LastCalculatedBarIndex; i>=0; i--)
   {
      G21=0.0;
      G10=0.0;
      L21=0.0;
      L10=0.0;
      jj=1;

      i_RSI_TF=iBarShift(NULL,RSI_Timeframe,Time[i]);
      RSI1=iRSI(NULL,RSI_Timeframe,RSI_Period,PRICE_CLOSE,i_RSI_TF+1);
      RSI2=iRSI(NULL,RSI_Timeframe,RSI_Period,PRICE_CLOSE,i_RSI_TF+2);
      vahe21=iClose(NULL,RSI_Timeframe,i_RSI_TF+1)-iClose(NULL,RSI_Timeframe,i_RSI_TF+2);
      vahe10=Close[i]-iClose(NULL,RSI_Timeframe,i_RSI_TF+1);

      while((vahe21==0)&&(i_RSI_TF+2+jj<iBars(NULL,RSI_Timeframe)))
      {
         RSI2=iRSI(NULL,RSI_Timeframe,RSI_Period,PRICE_CLOSE,i_RSI_TF+2+jj);
         vahe21=iClose(NULL,RSI_Timeframe,i_RSI_TF+1)-iClose(NULL,RSI_Timeframe,i_RSI_TF+2+jj);
         jj++;
      }

      if(vahe21>0) G21=vahe21;
      if(vahe21<0) L21=-vahe21;
      if(vahe10>0) G10=vahe10;
      if(vahe10<0) L10=-vahe10;
      RS2=RSI2/(100-RSI2);
      RS1=RSI1/(100-RSI1);
      if(RS1!=RS2)
      {
         AG0=(RSI_Period-1)*((RS1*(G21-RS2*L21))/(RSI_Period*(RS1-RS2)))+G10;
         AL0=(RSI_Period-1)*((G21-RS2*L21)/(RSI_Period*(RS1-RS2)))+L10;
         if(AL0!=0.0) RS0=AG0/AL0;
         else RS0=RS1;
      }
      Rsi[i]=100-100/(1+RS0);
//      Rsi[i]=iRSI(NULL,0,RSI_Period,PRICE_CLOSE,i);
   }

   for(i=LastCalculatedBarIndex; i>=0; i--)
   {
      if(Smoothing_EMA) RsiMa[i]=iMAOnArray(Rsi, 0, Sm_Per, 0, MODE_EMA, i);
      if(Smoothing_LR) RsiMa[i]=Calc_LR_of_RSI(i);
      if(Smoothing_KAMA) RsiMa[i]=Calc_KAMA_of_RSI(i);

      AtrRsi[i]=MathAbs(RsiMa[i + 1] - RsiMa[i]);
   }

   for(i=LastCalculatedBarIndex; i>=0; i--) MaAtrRsi[i]=iMAOnArray(AtrRsi, 0, Wilders_Period, 0, MODE_EMA, i);

   for(i=LastCalculatedBarIndex; i>=0; i--)
   {
      QQE_UP[i]=0.0;
      QQE_DOWN[i]=0.0;
      QQE_TREND[i]=0.0;
      
      dar=iMAOnArray(MaAtrRsi, 0, Wilders_Period, 0, MODE_EMA, i) * D_Factor;
      if (RsiMa[i] < TrLevelSlow[i+1])
      {
         TrLevelSlow[i]=RsiMa[i] + dar;
         if (RsiMa[i+1] < TrLevelSlow[i+1]) if (TrLevelSlow[i] > TrLevelSlow[i+1]) TrLevelSlow[i]=TrLevelSlow[i+1];
      }
      else if (RsiMa[i] > TrLevelSlow[i+1])
      {
         TrLevelSlow[i]=RsiMa[i] - dar;
         if (RsiMa[i+1] > TrLevelSlow[i+1]) if (TrLevelSlow[i] < TrLevelSlow[i+1]) TrLevelSlow[i]=TrLevelSlow[i+1];
      }
      
      if(RsiMa[i]>TrLevelSlow[i]) QQE_UP[i]=-10.0;
      if(RsiMa[i]<TrLevelSlow[i]) QQE_DOWN[i]=-10.0;
      
      if(Show_Signal_Arrows)
      {
         if((RsiMa[i+1]>TrLevelSlow[i+1])&&(RsiMa[i+2]<TrLevelSlow[i+2]))
         {
            Arrow_Name="BUY"+Time[i];
            ObjectCreate(Arrow_Name,OBJ_ARROW,0,Time[i],Low[i+1]-Signal_Distance_Pips*Point);
            ObjectSet(Arrow_Name,OBJPROP_ARROWCODE,233);
            ObjectSet(Arrow_Name,OBJPROP_COLOR,Blue);
            ObjectSet(Arrow_Name,OBJPROP_WIDTH,2);
         }
         if((RsiMa[i+1]<TrLevelSlow[i+1])&&(RsiMa[i+2]>TrLevelSlow[i+2]))
         {
            Arrow_Name="SELL"+Time[i];
            ObjectCreate(Arrow_Name,OBJ_ARROW,0,Time[i],High[i+1]+(Signal_Distance_Pips+5)*Point);
            ObjectSet(Arrow_Name,OBJPROP_ARROWCODE,234);
            ObjectSet(Arrow_Name,OBJPROP_COLOR,Red);
            ObjectSet(Arrow_Name,OBJPROP_WIDTH,2);
         }
      }
      
      if(QQE_UP[i+2]==-10.0) QQE_TREND[i]+=100.0;
      if(QQE_UP[i+1]==-10.0) QQE_TREND[i]+= 10.0;
      if(QQE_UP[i]  ==-10.0) QQE_TREND[i]+=  1.0;
   }
   
   if((Play_Signal_Sound)&&(Time[0]>Previous_Signal_Time))
   {
      if((QQE_TREND[0]==10.0)||(QQE_TREND[0]==11.0)) //BUY
      {
         PlaySound(Play_Signal_Name);
         Sleep(5000);
         Alert(Symbol()," QQE inside bar BUY signal @",Open[0],"! Period: ", Period());
         Previous_Signal_Time=Time[0];
      }
      if((QQE_TREND[0]==100.0)||(QQE_TREND[0]==101.0)) //SELL
      {
         PlaySound(Play_Signal_Name);
         Sleep(5000);
         Alert(Symbol()," QQE inside bar SELL signal @",Open[0],"! Period: ", Period());
         Previous_Signal_Time=Time[0];
      }
   }
   

   return(0);
}






double Calc_LR_of_RSI(int ii)
{
   double   result,
            Sum_x = 0,
            Sum_y = 0,
            Sum_xy = 0,
            Sum_x2 = 0,
            a = 0,
            b = 0;
   int      j;

   for(j = 0; j<Sm_Per; j++)
   {
      Sum_x += j + 1;
      Sum_y += Rsi[ii+j];
      Sum_xy += Rsi[ii+j] * (j + 1);
      Sum_x2 += MathPow((j + 1), 2);
   }
   b = (Sm_Per*Sum_xy - Sum_x*Sum_y) / (Sm_Per*Sum_x2 - MathPow(Sum_x, 2));
   a =  (Sum_y - b*Sum_x) / Sm_Per;
   result = a + b;
   return(result);
}


double Calc_KAMA_of_RSI(int ii)
{
   double   result,
            Direction=0.0,
            Volatility=0.0,
            ER=0.0,
            SSC=0.0,
            C=0.0;

   Direction=MathAbs(Rsi[ii]-Rsi[ii+Sm_Per]);
   for(int j=0;j<Sm_Per;j++) Volatility+=MathAbs(Rsi[ii+j]-Rsi[ii+j+1]);
   if(Volatility!=0) ER=Direction/Volatility;
   SSC=ER*((2.0/(KAMA_Fast_Period+1))-(2.0/(KAMA_Slow_Period+1)))+(2.0/(KAMA_Slow_Period+1));
   C=MathPow(SSC,2);
   result=C*(Rsi[ii]-RsiMa[ii+1])+RsiMa[ii+1];
   if(ii>=Bars-Sm_Per) result=Rsi[ii];
   return(result);
}