//+------------------------------------------------------------------+
//|                                                      QQE new.mq4 |
//|                                                           mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_chart_window
#property indicator_buffers 8


//
//
//
//
//

enum enTimeFrames
{
   tf_cu  = PERIOD_CURRENT, // Current time frame
   tf_m1  = PERIOD_M1,      // 1 minute
   tf_m5  = PERIOD_M5,      // 5 minutes
   tf_m15 = PERIOD_M15,     // 15 minutes
   tf_m30 = PERIOD_M30,     // 30 minutes
   tf_h1  = PERIOD_H1,      // 1 hour
   tf_h4  = PERIOD_H4,      // 4 hours
   tf_d1  = PERIOD_D1,      // Daily
   tf_w1  = PERIOD_W1,      // Weekly
   tf_mn1 = PERIOD_MN1,     // Monthly
   tf_n1  = -1,             // First higher time frame
   tf_n2  = -2,             // Second higher time frame
   tf_n3  = -3              // Third higher time frame
};

extern enTimeFrames TimeFrame       = tf_cu;      // Time frame
extern int    SF                    = 5;
extern int    RSIPeriod             = 14;
extern double WP                    = 4.236;
extern double UpperBound            = 60; 
extern double LowerBound            = 40; 
input int             WickWidth        = 1;                 // Candle wick width
input int             BodyWidth        = 2;                 // If auto width = false then use this
input bool            UseAutoWidth     = true;              // Auto adjust candle body width
extern color              cndl_UP    = clrSpringGreen;
extern color              cndl_Down    = clrOrangeRed;
extern color              cndl_Neutralu    = clrDarkGreen;
extern color              cndl_Neutrald    = clrDarkRed;
extern bool   alertsOn              = true;
extern bool   alertsSignalLineCross = false;
extern bool   alertsOnCurrent       = true;
extern bool   alertsMessage         = true;
extern bool   alertsSound           = false;
input bool    alertsNotify          = false;
extern bool   alertsEmail           = false;
input bool    arrowsVisible         = true;             // Show arrows?
input string  arrowsIdentifier      = "qqe arrows1";    // Arrows ID
input double  arrowsUpperGap        = 0.5;              // Arrows Upper Gap
input double  arrowsLowerGap        = 0.5;              // Arrows lower gap
input bool    arrowsOnNewest        = true;             // Arrows on newest mtf bar
input bool    arrowsOnZeroCross     = false;            // Show arrows on zero cross?
input bool    arrowsOnSignalCross   = false;            // Show arrows on signal cross?      
input color   arrowsUpColor         = clrLimeGreen;     // Up arrow color
input color   arrowsDnColor         = clrRed;           // Down arrows color
input int     arrowsUpCode          = 159;              // Up arrow code
input int     arrowsDnCode          = 159;              // Down arrow code
input int     arrowsUpSize          = 2;                // Up arrow size
input int     arrowsDnSize          = 2;                // Down arrow size 
 
extern int                btn_Subwindow = 0;
extern ENUM_BASE_CORNER   btn_corner            = CORNER_LEFT_UPPER; 
extern string             btn_text              = "QQE candles";
extern string             btn_Font              = "Arial";
extern int                btn_FontSize          = 10;                            
extern color              btn_text_ON_color     = clrLime;
extern color              btn_text_OFF_color    = clrRed;
extern string             btn_pressed           = "QQE OFF";            
extern string             btn_unpressed         = "QQE ON";
extern color              btn_background_color  = clrDimGray;
extern color              btn_border_color      = clrBlack;
extern int                button_x              = 900;                                 
extern int                button_y              = 0;                                   
extern int                btn_Width             = 75;                                 
extern int                btn_Height            = 20;                                
extern string             soundBT               = "tick.wav";  
//
//
//
//
bool                      show_data             = true;
string IndicatorName, IndicatorObjPrefix ,buttonId ;
//
int candlewidth=0;
double UpH[],UpH_bot[],DnH[],DnH_bot[],NuH_top[],NuH_bot[],NuH_topU[],NuH_botD[];
double state[];
double RsiMa[];
double Trend[];

double count[];
double work[][6];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,tf_cu,SF,RSIPeriod,WP,UpperBound,LowerBound,WickWidth,BodyWidth,UseAutoWidth,cndl_UP,cndl_Down,cndl_Neutralu,cndl_Neutrald,alertsOn,alertsSignalLineCross,alertsOnCurrent,alertsMessage,alertsSound,alertsNotify,alertsEmail,arrowsVisible,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsOnNewest,arrowsOnZeroCross,arrowsOnSignalCross,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,btn_Subwindow,btn_corner,btn_text,btn_Font,btn_FontSize,btn_text_ON_color,btn_text_OFF_color,btn_pressed,btn_unpressed,btn_background_color,btn_border_color,button_x,button_y,btn_Width,btn_Height,soundBT,_buff,_ind)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string GenerateIndicatorName(const string target)
{
   string name = target;
   int try = 2;
   while (WindowFind(name) != -1)
   {
      name = target + " #" + IntegerToString(try++);
   }
   return name;
}
int init()
{
if (UseAutoWidth)
   {
      int scale = int(ChartGetInteger(0,CHART_SCALE));
      switch(scale) 
	   {
	      case 0: candlewidth =  1; break;
	      case 1: candlewidth =  1; break;
		   case 2: candlewidth =  2; break;
		   case 3: candlewidth =  3; break;
		   case 4: candlewidth =  6; break;
		   case 5: candlewidth = 14; break;
	   }
	}
	else { candlewidth = BodyWidth; }
	
   IndicatorBuffers(12);
    SetIndexBuffer(0,UpH,INDICATOR_DATA); 
   SetIndexBuffer(1,DnH,INDICATOR_DATA); 
   SetIndexBuffer(2,NuH_top,INDICATOR_DATA); 
   SetIndexBuffer(3,NuH_bot,INDICATOR_DATA); 
   SetIndexBuffer(4,UpH_bot,INDICATOR_DATA);
   SetIndexBuffer(5,DnH_bot,INDICATOR_DATA);
   SetIndexBuffer(6,NuH_topU,INDICATOR_DATA);
   SetIndexBuffer(7,NuH_botD,INDICATOR_DATA);
   SetIndexBuffer(8,RsiMa);  
   SetIndexBuffer(9,Trend); 
    SetIndexBuffer(10,state); 
   SetIndexBuffer(11,count);
   
        SetIndexStyle(0,DRAW_HISTOGRAM, NULL, WickWidth,cndl_UP);
        SetIndexStyle(1,DRAW_HISTOGRAM, NULL, WickWidth,cndl_Down);
        SetIndexStyle(2,DRAW_HISTOGRAM, NULL, candlewidth,cndl_Neutralu);
        SetIndexStyle(3,DRAW_HISTOGRAM, NULL, candlewidth,cndl_Neutrald);
        SetIndexStyle(4,DRAW_HISTOGRAM, NULL, candlewidth,cndl_UP);
        SetIndexStyle(5,DRAW_HISTOGRAM, NULL, candlewidth,cndl_Down);
        SetIndexStyle(6,DRAW_HISTOGRAM, NULL, WickWidth,cndl_Neutralu);
        SetIndexStyle(7,DRAW_HISTOGRAM, NULL, WickWidth,cndl_Neutrald);
        
   indicatorFileName = WindowExpertName();
   TimeFrame         = (enTimeFrames)timeFrameValue(TimeFrame);
   
   SetLevelValue(0,UpperBound-50);
   SetLevelValue(1,LowerBound-50);
   IndicatorShortName(timeFrameToString(TimeFrame)+" QQE");
   
       IndicatorName = GenerateIndicatorName(btn_text);
   IndicatorObjPrefix = "__" + IndicatorName + "__";
   IndicatorShortName(WindowExpertName());
   IndicatorDigits(Digits);
      double val;
   if (GlobalVariableGet(IndicatorName + "_visibility", val))
   show_data = val != 0;

   ChartSetInteger(ChartID(), CHART_EVENT_MOUSE_MOVE, 1);
   buttonId = IndicatorObjPrefix+btn_text;
   createButton(buttonId, btn_text, btn_Width, btn_Height, btn_Font, btn_FontSize, btn_background_color, btn_border_color, btn_text_ON_color);
   ObjectSetInteger(ChartID(), buttonId, OBJPROP_YDISTANCE, button_y);
   ObjectSetInteger(ChartID(), buttonId, OBJPROP_XDISTANCE, button_x);   
   
return(0);
}

void createButton(string buttonID,string buttonText,int width,int height,string font,int fontSize,color bgColor,color borderColor,color txtColor)
{
      ObjectDelete    (ChartID(),buttonID);
       ObjectCreate(ChartID(),buttonID,OBJ_BUTTON,btn_Subwindow,0,0);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_COLOR,txtColor);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_BGCOLOR,bgColor);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_BORDER_COLOR,borderColor);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_XSIZE,width);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_YSIZE,height);
      ObjectSetString (ChartID(),buttonID,OBJPROP_FONT,font);
      ObjectSetString (ChartID(),buttonID,OBJPROP_TEXT,buttonText);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_FONTSIZE,fontSize);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_CORNER,btn_corner);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_HIDDEN,1);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_XDISTANCE,9999);
      ObjectSetInteger(ChartID(),buttonID,OBJPROP_YDISTANCE,9999);
}

void OnDeinit(const int reason)
 {
 
 ObjectsDeleteAll(0,"CCI1 candles");
   ObjectsDeleteAll(ChartID(), IndicatorObjPrefix);
  ObjectsDeleteAll(0,arrowsIdentifier+":");
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define iEma 0
#define iEmm 1
#define iQqe 2
#define iRsi 3
#define tQqe 4
#define tQqs 5

//
//
 bool recalc = true;
 void handleButtonClicks()
{
   if (ObjectGetInteger(ChartID(), buttonId, OBJPROP_STATE))
   {
      ObjectSetInteger(ChartID(), buttonId, OBJPROP_STATE, false);
      show_data = !show_data;
      GlobalVariableSet(IndicatorName + "_visibility", show_data ? 1.0 : 0.0);
      recalc = true;
      start();
   }
}
void OnChartEvent(const int id, 
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   handleButtonClicks();
   if (id==CHARTEVENT_OBJECT_CLICK && ObjectGet(sparam,OBJPROP_TYPE)==OBJ_BUTTON)
   {
   if (soundBT!="") PlaySound(soundBT);     
   }
   
  
	
}
//
//
//
//

int start()
{

handleButtonClicks();
   recalc = false;
   
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexStyle(2,DRAW_HISTOGRAM);
   SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexStyle(4,DRAW_HISTOGRAM);
   SetIndexStyle(5,DRAW_HISTOGRAM);
   SetIndexStyle(6,DRAW_HISTOGRAM);
   SetIndexStyle(7,DRAW_HISTOGRAM);
  
   start2();
   
  
   
      
   
   if (show_data)
      {
      ObjectSetInteger(ChartID(),buttonId,OBJPROP_COLOR,btn_text_ON_color);
      ObjectSetString(ChartID(),buttonId,OBJPROP_TEXT,btn_unpressed);
     
      }
      else
      {
      ObjectSetInteger(ChartID(),buttonId,OBJPROP_COLOR,btn_text_OFF_color);
      ObjectSetString(ChartID(),buttonId,OBJPROP_TEXT,btn_pressed);
       SetIndexStyle(0,DRAW_NONE);
         SetIndexStyle(1,DRAW_NONE);
        SetIndexStyle(2,DRAW_NONE);
        SetIndexStyle(3,DRAW_NONE);
          SetIndexStyle(4,DRAW_NONE);
         SetIndexStyle(5,DRAW_NONE);
          SetIndexStyle(6,DRAW_NONE);
           SetIndexStyle(7,DRAW_NONE);
         
   
      
   
      //template code     
      }
       return(0);
      }
//
//
//

int start2()
{
   int i,r,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(ChartGetInteger(0,CHART_SCALE) != candlewidth) init();
         if (TimeFrame != _Period)
         {
            limit = (int)fmax(limit,fmin(Bars-1,_mtfCall(11,0)*TimeFrame/_Period));
            for (i=limit;i>=0 && !_StopFlag; i--)
            {
                int y = iBarShift(NULL,TimeFrame,Time[i]);
                   RsiMa[i]  = _mtfCall(3,y);
                   Trend[i]  = _mtfCall(4,y);
                    state[i]  = _mtfCall(10,y);
                  
                              UpH[i]  =  EMPTY_VALUE;
                       DnH[i]  = EMPTY_VALUE;   
                       NuH_top[i]  =  EMPTY_VALUE; 
                       NuH_bot[i]  =  EMPTY_VALUE;
                       UpH_bot[i]  =  EMPTY_VALUE;
                       DnH_bot[i]  =  EMPTY_VALUE;  
                       NuH_topU[i]  =  EMPTY_VALUE; 
                       NuH_botD[i]  =  EMPTY_VALUE;  
                       
        if ( state[i] == 1 )   { UpH[i]  = High[i];  DnH[i]  = Low[i];UpH_bot[i] = MathMax(Open[i],Close[i]); DnH_bot[i] = MathMin(Open[i],Close[i]);}               
        if ( state[i] == -1 )  { UpH[i]  = Low[i]; DnH[i]  = High[i]; UpH_bot[i] = MathMin(Open[i],Close[i]);  DnH_bot[i] = MathMax(Open[i],Close[i]);} 
        if ( state[i] == 2 )   { NuH_topU[i]  = High[i];  NuH_botD[i]  = Low[i]; NuH_top[i] = MathMax(Open[i],Close[i]); NuH_bot[i] = MathMin(Open[i],Close[i]); }
        if (state[i]== -2)     { NuH_topU[i]  = Low[i]; NuH_botD[i]  = High[i]; NuH_top[i] = MathMin(Open[i],Close[i]); NuH_bot[i] = MathMax(Open[i],Close[i]);}
             
                   //
                     
                   
            }
           
   return(0);
   }
           

   //
   //
   //
   //
   //
   
   if (ArrayRange(work,0) != Bars) ArrayResize(work,Bars); 
   double alpha1 = 2.0/(SF+1.0);
   double alpha2 = 2.0/(RSIPeriod*2.0);
      
   for (i=limit, r=Bars-i-1; i>=0; i--,r++)
   {  
      work[r][iRsi] = work[r-1][iRsi] + alpha1*(iRSI(NULL,0,RSIPeriod,PRICE_CLOSE,i)   - work[r-1][iRsi]);
      work[r][iEma] = work[r-1][iEma] + alpha2*(MathAbs(work[r-1][iRsi]-work[r][iRsi]) - work[r-1][iEma]);
      work[r][iEmm] = work[r-1][iEmm] + alpha2*(work[r][iEma] - work[r-1][iEmm]);

      //
      //
      //
      //
      //

         double rsi0 = work[r  ][iRsi];
         double rsi1 = work[r-1][iRsi];
         double dar  = work[r  ][iEmm]*WP;
         double tr   = work[r-1][iQqe];
         double dv   = tr;
   
            if (rsi0 < tr) { tr = rsi0 + dar; if ((rsi1 < dv) && (tr > dv)) tr = dv; }
            if (rsi0 > tr) { tr = rsi0 - dar; if ((rsi1 > dv) && (tr < dv)) tr = dv; }
         
      //
      //
      //
      //
      //
         
         work[r][iQqe] = tr;
         work[r][tQqe] = work[r-1][tQqe];
         work[r][tQqs] = work[r-1][tQqs];
         RsiMa[i]      = work[r][iRsi]-50;
         Trend[i]      = tr           -50;
       
   
        
          state[i] = state[i+1];
               if (RsiMa[i] > (UpperBound-50)) state[i] =  1; 
               if  (RsiMa[i] < (LowerBound-50)) state[i] = -1;
                if  (RsiMa[i] > (LowerBound-50) &&  RsiMa[i] < 0) state[i] = -2;
                if  (RsiMa[i] < (UpperBound-50) &&  RsiMa[i] > 0) state[i] = 2;
      //

      //
                       UpH[i]  =  EMPTY_VALUE;
                       DnH[i]  = EMPTY_VALUE;   
                       NuH_top[i]  =  EMPTY_VALUE; 
                       NuH_bot[i]  =  EMPTY_VALUE;
                       UpH_bot[i]  =  EMPTY_VALUE;
                       DnH_bot[i]  =  EMPTY_VALUE;  
                       NuH_topU[i]  =  EMPTY_VALUE; 
                       NuH_botD[i]  =  EMPTY_VALUE;  
                       
        if ( state[i] == 1 )   { UpH[i]  = High[i];  DnH[i]  = Low[i];UpH_bot[i] = MathMax(Open[i],Close[i]); DnH_bot[i] = MathMin(Open[i],Close[i]);}               
        if ( state[i] == -1 )  { UpH[i]  = Low[i]; DnH[i]  = High[i]; UpH_bot[i] = MathMin(Open[i],Close[i]);  DnH_bot[i] = MathMax(Open[i],Close[i]);} 
        if ( state[i] == 2 )   { NuH_topU[i]  = High[i];  NuH_botD[i]  = Low[i]; NuH_top[i] = MathMax(Open[i],Close[i]); NuH_bot[i] = MathMin(Open[i],Close[i]); }
        if (state[i]== -2)     { NuH_topU[i]  = Low[i]; NuH_botD[i]  = High[i]; NuH_top[i] = MathMin(Open[i],Close[i]); NuH_bot[i] = MathMax(Open[i],Close[i]);}
             
      //
      //
      //
      //
               
         if (RsiMa[i] > 0)        work[r][tQqe] =  1;
         if (RsiMa[i] < 0)        work[r][tQqe] = -1;
         if (RsiMa[i] > Trend[i]) work[r][tQqs] =  1;
         if (RsiMa[i] < Trend[i]) work[r][tQqs] = -1;
        
        
      //
         
         if (arrowsVisible)
         {
            ObjectDelete(arrowsIdentifier+":1:"+(string)Time[i]);
            ObjectDelete(arrowsIdentifier+":2:"+(string)Time[i]);
            string lookFor = arrowsIdentifier+":"+(string)Time[i]; ObjectDelete(lookFor);
            if (r>0 && arrowsOnSignalCross && work[r][tQqs]!=work[r-1][tQqs])
            {
              if (work[r][tQqs] == 1) drawArrow("1",0.5,i,arrowsUpColor,arrowsUpCode,arrowsUpSize,false);
              if (work[r][tQqs] ==-1) drawArrow("1",0.5,i,arrowsDnColor,arrowsDnCode,arrowsDnSize,true);
            }
            if (r>0 && arrowsOnZeroCross && work[r][tQqe]!=work[r-1][tQqe])
            {
               if (work[r][tQqe] == 1) drawArrow("2",1.0,i,arrowsUpColor,arrowsUpCode,arrowsUpSize,false);
               if (work[r][tQqe] ==-1) drawArrow("2",1.0,i,arrowsDnColor,arrowsDnCode,arrowsDnSize,true);
            }
        }
   }    
   
   //
   //
   //
   //
   //
   
   if (alertsOn)
   {
      int whichBar = (alertsOnCurrent) ? Bars-1 : Bars-2;  
      if (alertsSignalLineCross)
      {
         if (work[whichBar][tQqs] != work[whichBar-1][tQqs])
         {
            if (work[whichBar][tQqs] ==  1) doAlert(" qqe line crossed signal line UP");
            if (work[whichBar][tQqs] == -1) doAlert(" qqe line crossed signal line DOWN");
         }         
      }
      else
      {
         if (work[whichBar][tQqe] != work[whichBar-1][tQqe])
         {
            if (work[whichBar][tQqe] ==  1) doAlert(" qqe line crossed 50 line UP");
            if (work[whichBar][tQqe] == -1) doAlert(" qqe line crossed 50 line DOWN");
         }         
      }
   }
return(0);
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[0]) {
       previousAlert  = doWhat;
       previousTime   = Time[0];

       //
       //
       //
       //
       //

       message = timeFrameToString(_Period)+" "+_Symbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" QQE "+doWhat;
             if (alertsMessage) Alert(message);
             if (alertsNotify)  SendNotification(message);
             if (alertsEmail)   SendMail(_Symbol+" QQE ",message);
             if (alertsSound)   PlaySound("alert2.wav");
   }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void drawArrow(string nameAdd, double gapMul, int i,color theColor, int theCode, int theWidth, bool up)
{
   string name = arrowsIdentifier+":"+nameAdd+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i)*gapMul;   
   
      //
      //
      //
      //
      //
      
      int add = 0; if (!arrowsOnNewest) add = _Period*60-1;
      ObjectCreate(name,OBJ_ARROW,0,Time[i],0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_WIDTH,theWidth);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}


//-------------------------------------------------------------------
//
//-------------------------------------------------------------------

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("");
}
int timeFrameValue(int _tf)
{
   int add  = (_tf>=0) ? 0 : fabs(_tf);
   if (add != 0) _tf = _Period;
   int size = ArraySize(iTfTable); 
      int i =0; for (;i<size; i++) if (iTfTable[i]==_tf) break;
                                   if (i==size) return(_Period);
                                                return(iTfTable[(int)fmin(i+add,size-1)]);
}


