//+------------------------------------------------------------------+
//|                                                 Jim Sloman's ndx |
//|                                                      ocn ndx.mq4 |
//|                                                           mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1  DimGray
#property indicator_color2  DimGray
#property indicator_color3  DimGray
#property indicator_color4  DeepSkyBlue
#property indicator_style1  STYLE_DOT
#property indicator_style2  STYLE_DOT
#property indicator_style3  STYLE_DOT

//
//
//
//
//

extern string TimeFrame       = "Current time frame";
extern int    NDXPeriod       = 40;
extern int    NDXPrice        = PRICE_CLOSE;
extern int    TEMAPeriod      = 20;
extern int    OverBought      = 50;
extern int    OverSold        = -50; 
extern bool   Interpolate     = true;

extern bool   alertsOn        = true;
extern bool   alertsOnCurrent = true;
extern bool   alertsMessage   = true;
extern bool   alertsSound     = true;
extern bool   alertsEmail     = false;

//
//
//
//
//

double ndx[];
double obLine[];
double osLine[];
double zeroLine[];
double tBuffer[][4];
double trend[];
double alpha;

//
//
//
//
//

string indicatorFileName;
bool   calculateValue;
bool   returnBars;
int    timeFrame;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

int init()
{
  for (int i=0; i<indicator_buffers; i++) SetIndexStyle(i,DRAW_LINE);
  IndicatorBuffers(5);
    SetIndexBuffer(0,zeroLine); SetIndexLabel(0,NULL);
    SetIndexBuffer(1,obLine);   SetIndexLabel(1,NULL);
    SetIndexBuffer(2,osLine);   SetIndexLabel(2,NULL);
    SetIndexBuffer(3,ndx);
    SetIndexBuffer(4,trend);
    
    //
    //
    //
    //
    //
      string PriceType;
      switch(NDXPrice)
      {
         case PRICE_CLOSE:    PriceType = "Close";    break;  // 0
         case PRICE_OPEN:     PriceType = "Open";     break;  // 1
         case PRICE_HIGH:     PriceType = "High";     break;  // 2
         case PRICE_LOW:      PriceType = "Low";      break;  // 3
         case PRICE_MEDIAN:   PriceType = "Median";   break;  // 4
         case PRICE_TYPICAL:  PriceType = "Typical";  break;  // 5
         case PRICE_WEIGHTED: PriceType = "Weighted"; break;  // 6
      }      

   //
   //
   //
   //
   //

       TEMAPeriod = MathMax(TEMAPeriod,1);
       NDXPeriod  = MathMax(NDXPeriod ,1);
       alpha = 2.0 /(1.0 + TEMAPeriod);
      
   //
   //
   //
   //
   //
   
      indicatorFileName = WindowExpertName();
      calculateValue    = (TimeFrame=="calculateValue"); if (calculateValue) return(0);
      returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
      timeFrame         = stringToTimeFrame(TimeFrame);
      
   //
   //
   //
   //
   //
   
   IndicatorShortName(timeFrameToString(timeFrame)+"   ocn ndx ("+NDXPeriod+","+TEMAPeriod+","+PriceType+")");
 return(0);
}

//
//
//
//
//

int deinit() { return(0); }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define iPrc 3

//
//
//
//
//

int start()
{
   int    counted_bars=IndicatorCounted();
   int    i,r,n,s,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = MathMin(Bars-counted_bars,Bars-NDXPeriod-1);
         if (returnBars) { zeroLine[0] = limit+1; return(0); }
         if (ArrayRange(tBuffer,0) != Bars) ArrayResize(tBuffer,Bars);

   //
   //
   //
   //
   //
   
   if (calculateValue || timeFrame==Period())
   {
   
   //
   //
   //
   //
   //

   for(i=limit, r=Bars-i-1; i >= 0; i--,r++)
   {
      double currPrice = iMA(NULL,0,1,0,MODE_SMA,NDXPrice,i);
         if (currPrice > 0)
               tBuffer[r][iPrc] = MathLog(currPrice);
         else  tBuffer[r][iPrc] = 0.00;
      
         //
         //
         //
         //
         //

         double sumMom = 0;
         double sumDen = 0;
         double sumDif = 0;
         for (int k=1; k < NDXPeriod; k++)
         {
            sumDif += MathAbs(tBuffer[r-k+1][iPrc]-tBuffer[r-k][iPrc]);
            if (sumDif !=0)
                  double stoch = (tBuffer[r][iPrc]-tBuffer[r-k][iPrc])/sumDif;
            else         stoch = 0;
            double coeff = 1.0/MathSqrt(k);
          
            sumMom += coeff*stoch;
            sumDen += coeff;
         }
         
         //
         //
         //
         //
         //

         double ndxTemp = iTema(100.0*sumMom/sumDen,r);
            if (ndxTemp > 90) ndxTemp =  90+( ndxTemp-90)/2.0;
            if (ndxTemp <-90) ndxTemp = -90-(-ndxTemp-90)/2.0;

      //
      //
      //
      //
      //
      
      ndx[i]      = ndxTemp;
      obLine[i]   = OverBought;
      osLine[i]   = OverSold;
      zeroLine[i] = 0;
      trend[i]    = trend[i+1]; 
         
	   if (ndx[i] > osLine[i] && ndx[i+1] <= osLine[i+1]) trend[i]= 1; 
	   if (ndx[i] < obLine[i] && ndx[i+1] >= obLine[i+1]) 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]);
            zeroLine[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXPrice,TEMAPeriod,OverBought,OverSold,0,y);
            obLine[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXPrice,TEMAPeriod,OverBought,OverSold,1,y);
            osLine[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXPrice,TEMAPeriod,OverBought,OverSold,2,y);
            ndx[i]      = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXPrice,TEMAPeriod,OverBought,OverSold,3,y);
            trend[i]    = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXPrice,TEMAPeriod,OverBought,OverSold,4,y);
            
            //
            //
            //
            //
            //
            
            if (!Interpolate || y==iBarShift(NULL,timeFrame,Time[i-1])) continue;

            //
            //
            //
            //
            //

            datetime time = iTime(NULL,timeFrame,y);
            for(n = 1; i+n < Bars && Time[i+n] >= time; n++) continue;	
            for(s = 1; s < n; s++)
            {
  	            ndx[i+s] = ndx[i] + (ndx[i+n] - ndx[i]) * s/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,"oversold");
         if (trend[whichBar] ==-1) doAlert(whichBar,"overbought");
      }         
   }
}   

//
//
//
//
//

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)+" ocn ndx ",doWhat);
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(StringConcatenate(Symbol()," ocn ndx "),message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iTema(double price,int i)
{
   if (i < 1)
      {
         tBuffer[i][0] = price;
         tBuffer[i][1] = price;
         tBuffer[i][2] = price;
      }
   else
      {
         tBuffer[i][0] = tBuffer[i-1][0]+alpha*(price        -tBuffer[i-1][0]);
         tBuffer[i][1] = tBuffer[i-1][1]+alpha*(tBuffer[i][0]-tBuffer[i-1][1]);
         tBuffer[i][2] = tBuffer[i-1][2]+alpha*(tBuffer[i][1]-tBuffer[i-1][2]);
      }
   return(3.0*tBuffer[i][0] - 3.0*tBuffer[i][1] + tBuffer[i][2]);
}