//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

//
//
//
//
//

#property indicator_separate_window
#property indicator_buffers    4
#property indicator_color1     Red
#property indicator_color2     DeepSkyBlue
#property indicator_color3     Magenta
#property indicator_style3     STYLE_DASH
#property indicator_color4     LimeGreen
#property indicator_style4     STYLE_DASH
#property indicator_width1     2
#property indicator_width2     2
#property indicator_level1     0
#property indicator_level2     100
#property indicator_levelcolor DimGray

//
//
//
//
//

extern string TimeFrame         = "Current time frame";
extern int    Len               = 5;
extern int    SmoothLength      = 50; 
extern double SmoothPhase       = 0;
extern bool   SmoothDouble      = false;
extern int    WsoSignalPeriod   = 10;
extern double WsoSignalHot      = 0.7;
extern bool   WsoSignalOriginal = false;
extern int    WroSignalPeriod   = 10;
extern double WroSignalHot      = 0.7;
extern bool   WroSignalOriginal = false;
extern int    buylevel          = 0;
extern int    selllevel         = 100;
extern bool   Interpolate       = true;

extern string _                 = "alerts settings";
extern bool   alertsOn          = true;
extern bool   alertsOnCurrent   = false;
extern bool   alertsMessage     = true;
extern bool   alertsSound       = false;
extern bool   alertsEmail       = false;

//
//
//
//
//

double wso[];
double wro[];
double wsoMa[];
double wroMa[];
double trend[];
double wrk[][12];
int    center;

//
//
//
//
//

string indicatorFileName;
int    timeFrame;
bool   returnBars;
bool   calculateValue;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(5);
   SetIndexBuffer(0,wso); 
   SetIndexBuffer(1,wro);
   SetIndexBuffer(2,wsoMa); 
   SetIndexBuffer(3,wroMa); 
   SetIndexBuffer(4,trend); 
   
   //
   //
   //
   //
   //
   
   if (MathMod(Len,2)==0) Len++;  center = (Len-1)/2;
   
   //
   //
   //
   //
   //
   
     indicatorFileName = WindowExpertName();
     calculateValue    = (TimeFrame=="calculateValue"); if (calculateValue) return(0);
     returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
     timeFrame         = stringToTimeFrame(TimeFrame);
      
   //
   //
   //
   //
   //
      
   IndicatorShortName(timeFrameToString(timeFrame)+"  wso & wro T3ma ("+Len+","+SmoothLength+","+WsoSignalPeriod+","+WroSignalPeriod+")");
 
   return(0);
}

int deinit(){ return(0);}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define S1 0
#define S2 1
#define S3 2
#define S4 3
#define S5 4
#define S6 5
#define R1 6
#define R2 7
#define R3 8
#define R4 9
#define R5 10
#define R6 11

int start()
{
   int counted_bars=IndicatorCounted();
   int i,r,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = MathMin(Bars-counted_bars,Bars-1);
         if (ArrayRange(wrk,0)!=Bars) ArrayResize(wrk,Bars);
         if (returnBars) { wso[0] = limit+1; return(0); }

   //
   //
   //
   //
   //

   if (calculateValue || timeFrame==Period())
   {
   
   //
   //
   //
   //
   //

   for(i=limit, r=Bars-i-1; i>=0; i--,r++)
   {
      if (r==0)
      {
         for (int k=0; k<6; k++)
         {
            wrk[r][k]   = Low[i];
            wrk[r][k+6] = High[i];
         }
         continue;
      }
      
      //
      //
      //
      //
      //
      
      int lowest  = iLowest(NULL,0,MODE_LOW,Len,i)-i;
      int highest = iHighest(NULL,0,MODE_HIGH,Len,i)-i;

         for (k=0; k<12; k++) wrk[r][k] = wrk[r-1][k];
         if (lowest==center)
         {
            wrk[r][S1] = Low[i+center];
            wrk[r][S2] = wrk[r-1][S1];
            wrk[r][S3] = wrk[r-1][S2];
            wrk[r][S4] = wrk[r-1][S3];
            wrk[r][S5] = wrk[r-1][S4];
            wrk[r][S6] = wrk[r-1][S5];
         }
         if (highest==center)
         {
            wrk[r][R1] = High[i+center];
            wrk[r][R2] = wrk[r-1][R1];
            wrk[r][R3] = wrk[r-1][R2];
            wrk[r][R4] = wrk[r-1][R3];
            wrk[r][R5] = wrk[r-1][R4];
            wrk[r][R6] = wrk[r-1][R5];
         }
         
         
      //
      //
      //
      //
      //

      wso[i] = iDSmooth(100*(1-(MathInt(wrk[r][S1]/Close[i])+
                                MathInt(wrk[r][S2]/Close[i])+
                                MathInt(wrk[r][S3]/Close[i])+
                                MathInt(wrk[r][S4]/Close[i])+
                                MathInt(wrk[r][S5]/Close[i])+
                                MathInt(wrk[r][S6]/Close[i]))/6.0),SmoothLength,SmoothPhase,SmoothDouble,i, 0);
      wro[i] = iDSmooth(100*(1-(MathInt(wrk[r][R1]/Close[i])+
                                MathInt(wrk[r][R2]/Close[i])+
                                MathInt(wrk[r][R3]/Close[i])+
                                MathInt(wrk[r][R4]/Close[i])+
                                MathInt(wrk[r][R5]/Close[i])+
                                MathInt(wrk[r][R6]/Close[i]))/6.0),SmoothLength,SmoothPhase,SmoothDouble,i,20);
      }
      
      //
      //
      //
      //
      //
     
      for(i=limit; i >= 0; i--)   
      {
         wsoMa[i] = iT3(wso[i],WsoSignalPeriod,WsoSignalHot,WsoSignalOriginal,i,0);
         wroMa[i] = iT3(wro[i],WroSignalPeriod,WroSignalHot,WroSignalOriginal,i,1);
         trend[i] = trend[i+1]; 
         
	         if (wro[i] <= selllevel && wro[i+1] >= selllevel)  trend[i] = -1; 
            if (wso[i] >= buylevel  && wso[i+1] <= buylevel )  trend[i] =  1; 
	    
      }
   manageAlerts();  
   return(0);
   }
   
   //
   //
   //
   //
   //
   
   limit = MathMax(limit,MathMin(Bars,iCustom(NULL,timeFrame,indicatorFileName,"returnBars",0,0)*timeFrame/Period()));
          for (i=limit;i>=0; i--)
          {
             int y = iBarShift(NULL,timeFrame,Time[i]);
                 wso[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",Len,SmoothLength,SmoothPhase,SmoothDouble,WsoSignalPeriod,WsoSignalHot,WsoSignalOriginal,WroSignalPeriod,WroSignalHot,WroSignalOriginal,buylevel,selllevel,0,y);
                 wro[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",Len,SmoothLength,SmoothPhase,SmoothDouble,WsoSignalPeriod,WsoSignalHot,WsoSignalOriginal,WroSignalPeriod,WroSignalHot,WroSignalOriginal,buylevel,selllevel,1,y);
                 wsoMa[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",Len,SmoothLength,SmoothPhase,SmoothDouble,WsoSignalPeriod,WsoSignalHot,WsoSignalOriginal,WroSignalPeriod,WroSignalHot,WroSignalOriginal,buylevel,selllevel,2,y);
                 wroMa[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",Len,SmoothLength,SmoothPhase,SmoothDouble,WsoSignalPeriod,WsoSignalHot,WsoSignalOriginal,WroSignalPeriod,WroSignalHot,WroSignalOriginal,buylevel,selllevel,3,y);
                 trend[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",Len,SmoothLength,SmoothPhase,SmoothDouble,WsoSignalPeriod,WsoSignalHot,WsoSignalOriginal,WroSignalPeriod,WroSignalHot,WroSignalOriginal,buylevel,selllevel,4,y);
            
                 //
                 //
                 //
                 //
                 //
            
                 if (!Interpolate || y==iBarShift(NULL,timeFrame,Time[i-1])) continue;

                 //
                 //
                 //
                 //
                 //

                 datetime time = iTime(NULL,timeFrame,y);
                 for(int n = 1; i+n < Bars && Time[i+n] >= time; n++) continue;	
                 for(int j = 1; j < n; j++)
                 {
  	                  wso[i+j]   = wso[i]   + (wso[i+n]   - wso[i])   * j/n;
  	                  wro[i+j]   = wro[i]   + (wro[i+n]   - wro[i])   * j/n;
  	                  wsoMa[i+j] = wsoMa[i] + (wsoMa[i+n] - wsoMa[i]) * j/n;
  	                  wroMa[i+j] = wroMa[i] + (wroMa[i+n] - wroMa[i]) * j/n;
  	          
                  }  	            
   }
   manageAlerts();
   return(0);
}
                                                     
//+-------------------------------------------------------------------
//
//
//
//
//

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 timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//
//
//
//
//

string stringUpperCase(string str)
{
   string   s = str;

   for (int length=StringLen(str)-1; length>=0; length--)
   {
      int tchar = StringGetChar(s, length);
         if((tchar > 96 && tchar < 123) || (tchar > 223 && tchar < 256))
                     s = StringSetChar(s, length, tchar - 32);
         else if(tchar > -33 && tchar < 0)
                     s = StringSetChar(s, length, tchar + 224);
   }
   return(s);
}

//
//
//
//
//

void manageAlerts()
{
   if (!calculateValue && alertsOn)
   {
      if (alertsOnCurrent)
           int whichBar = 0;
      else     whichBar = 1; whichBar = iBarShift(NULL,0,iTime(NULL,timeFrame,whichBar));
      if (trend[whichBar] != trend[whichBar+1])
      {
         if (trend[whichBar] == 1) doAlert(whichBar,"buy");
         if (trend[whichBar] ==-1) doAlert(whichBar,"sell");
      }         
   }
}   

//
//
//
//
//

void doAlert(int forBar, string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[forBar]) {
       previousAlert  = doWhat;
       previousTime   = Time[forBar];

       //
       //
       //
       //
       //

       message =  StringConcatenate(Symbol()," at ",TimeToStr(TimeLocal(),TIME_SECONDS)," - ",timeFrameToString(timeFrame)+" wso & wro ma ",doWhat);
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(StringConcatenate(Symbol()," wso & wro ma "),message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}

//
//
//
//
//

double MathInt(double number)
{
   if (number>=1.0)
         return(1);
   else  return(0);    
}

//
//
//
//
//

double wrks[][40];

#define bsmax  5
#define bsmin  6
#define volty  7
#define vsum   8
#define avolty 9

//
//
//
//
//

double iDSmooth(double price, double length, double phase, bool isDouble, int i, int s=0)
{
   if (isDouble)
         return (iSmooth(iSmooth(price,MathSqrt(length),phase,i,s),MathSqrt(length),phase,i,s+10));
   else  return (iSmooth(price,length,phase,i,s));
}

//
//
//
//
//

double iSmooth(double price, double length, double phase, int i, int s=0)
{
   if (length <=1) return(price);
   if (ArrayRange(wrks,0) != Bars) ArrayResize(wrks,Bars);
   
   int r = Bars-i-1; 
      if (r==0) { for(int k=0; k<7; k++) wrks[r][k+s]=price; for(; k<10; k++) wrks[r][k+s]=0; return(price); }

   //
   //
   //
   //
   //
   
      double len1   = MathMax(MathLog(MathSqrt(0.5*(length-1)))/MathLog(2.0)+2.0,0);
      double pow1   = MathMax(len1-2.0,0.5);
      double del1   = price - wrks[r-1][bsmax+s];
      double del2   = price - wrks[r-1][bsmin+s];
      double div    = 1.0/(10.0+10.0*(MathMin(MathMax(length-10,0),100))/100);
      int    forBar = MathMin(r,10);
	
         wrks[r][volty+s] = 0;
               if(MathAbs(del1) > MathAbs(del2)) wrks[r][volty+s] = MathAbs(del1); 
               if(MathAbs(del1) < MathAbs(del2)) wrks[r][volty+s] = MathAbs(del2); 
         wrks[r][vsum+s] =	wrks[r-1][vsum+s] + (wrks[r][volty+s]-wrks[r-forBar][volty+s])*div;
         
         //
         //
         //
         //
         //
   
         wrks[r][avolty+s] = wrks[r-1][avolty+s]+(2.0/(MathMax(4.0*length,30)+1.0))*(wrks[r][vsum+s]-wrks[r-1][avolty+s]);
            if (wrks[r][avolty+s] > 0)
               double dVolty = wrks[r][volty+s]/wrks[r][avolty+s]; else dVolty = 0;   
	               if (dVolty > MathPow(len1,1.0/pow1)) dVolty = MathPow(len1,1.0/pow1);
                  if (dVolty < 1)                      dVolty = 1.0;

      //
      //
      //
      //
      //
	        
   	double pow2 = MathPow(dVolty, pow1);
      double len2 = MathSqrt(0.5*(length-1))*len1;
      double Kv   = MathPow(len2/(len2+1), MathSqrt(pow2));

         if (del1 > 0) wrks[r][bsmax+s] = price; else wrks[r][bsmax+s] = price - Kv*del1;
         if (del2 < 0) wrks[r][bsmin+s] = price; else wrks[r][bsmin+s] = price - Kv*del2;
	
   //
   //
   //
   //
   //
      
      double R     = MathMax(MathMin(phase,100),-100)/100.0 + 1.5;
      double beta  = 0.45*(length-1)/(0.45*(length-1)+2);
      double alpha = MathPow(beta,pow2);

         wrks[r][0+s] =  price + alpha*(wrks[r-1][0+s]-price);
         wrks[r][1+s] = (price - wrks[r][0+s])*(1-beta) + beta*wrks[r-1][1+s];
         wrks[r][2+s] = (wrks[r][0+s]   + R*wrks[r][1+s]);
         wrks[r][3+s] = (wrks[r][2+s]   - wrks[r-1][4+s])*MathPow((1-alpha),2) + MathPow(alpha,2)*wrks[r-1][3+s];
         wrks[r][4+s] = (wrks[r-1][4+s] + wrks[r][3+s]); 

   //
   //
   //
   //
   //

   return(wrks[r][4+s]);
}

//+------------------------------------------------------------------
//|
//+------------------------------------------------------------------
//
//
//
//
//

double workT3[][12];
double workT3Coeffs[][6];
#define _period 0
#define _c1     1
#define _c2     2
#define _c3     3
#define _c4     4
#define _alpha  5

//
//
//
//
//

double iT3(double price, double period, double hot, bool original, int i, int forInstance=0)
{
   if (ArrayRange(workT3,0) !=Bars)                  ArrayResize(workT3,Bars);
   if (ArrayRange(workT3Coeffs,0) < (forInstance+1)) ArrayResize(workT3Coeffs,forInstance+1);

   if (workT3Coeffs[forInstance][_period] != period)
   {
     workT3Coeffs[forInstance][_period] = period;
        double a = hot;
            workT3Coeffs[forInstance][_c1] = -a*a*a;
            workT3Coeffs[forInstance][_c2] = 3*a*a+3*a*a*a;
            workT3Coeffs[forInstance][_c3] = -6*a*a-3*a-3*a*a*a;
            workT3Coeffs[forInstance][_c4] = 1+3*a+a*a*a+3*a*a;
            if (original)
                 workT3Coeffs[forInstance][_alpha] = 2.0/(1.0 + period);
            else workT3Coeffs[forInstance][_alpha] = 2.0/(2.0 + (period-1.0)/2.0);
   }
   
   //
   //
   //
   //
   //
   
   int buffer = forInstance*6;
   int r = Bars-i-1;
   if (r == 0)
      {
         workT3[r][0+buffer] = price;
         workT3[r][1+buffer] = price;
         workT3[r][2+buffer] = price;
         workT3[r][3+buffer] = price;
         workT3[r][4+buffer] = price;
         workT3[r][5+buffer] = price;
      }
   else
      {
         workT3[r][0+buffer] = workT3[r-1][0+buffer]+workT3Coeffs[forInstance][_alpha]*(price              -workT3[r-1][0+buffer]);
         workT3[r][1+buffer] = workT3[r-1][1+buffer]+workT3Coeffs[forInstance][_alpha]*(workT3[r][0+buffer]-workT3[r-1][1+buffer]);
         workT3[r][2+buffer] = workT3[r-1][2+buffer]+workT3Coeffs[forInstance][_alpha]*(workT3[r][1+buffer]-workT3[r-1][2+buffer]);
         workT3[r][3+buffer] = workT3[r-1][3+buffer]+workT3Coeffs[forInstance][_alpha]*(workT3[r][2+buffer]-workT3[r-1][3+buffer]);
         workT3[r][4+buffer] = workT3[r-1][4+buffer]+workT3Coeffs[forInstance][_alpha]*(workT3[r][3+buffer]-workT3[r-1][4+buffer]);
         workT3[r][5+buffer] = workT3[r-1][5+buffer]+workT3Coeffs[forInstance][_alpha]*(workT3[r][4+buffer]-workT3[r-1][5+buffer]);
      }

   //
   //
   //
   //
   //
   
   return(workT3Coeffs[forInstance][_c1]*workT3[r][5+buffer] + 
          workT3Coeffs[forInstance][_c2]*workT3[r][4+buffer] + 
          workT3Coeffs[forInstance][_c3]*workT3[r][3+buffer] + 
          workT3Coeffs[forInstance][_c4]*workT3[r][2+buffer]);
}
     