//+------------------------------------------------------------------+
//|                                                 Jim Sloman's nxc |
//|                                                      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    NDXTEMAPeriod  =  20;
extern int    NDXPrice        =  PRICE_CLOSE;
extern int    NSTPeriod       =  20;
extern int    NSTTEMAPeriod  =  10;
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 nxc[];
double obLine[];
double osLine[];
double zeroLine[];
double trend[];
double tBuffer[][7];
double alphaNdx;
double alphaNst;

//
//
//
//
//

string indicatorFileName;
bool   calculateValue;
bool   returnBars;
int    timeFrame;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
 IndicatorBuffers(5);
   SetIndexBuffer(0,zeroLine); SetIndexLabel(0,NULL);
   SetIndexBuffer(1,obLine);   SetIndexLabel(1,NULL);
   SetIndexBuffer(2,osLine);   SetIndexLabel(2,NULL);
   SetIndexBuffer(3,nxc);
   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
      }      

   //
   //
   //
   //
   //

   NDXTEMAPeriod = MathMax(NDXTEMAPeriod,1);
   NDXPeriod     = MathMax(NDXPeriod ,1);
   NSTTEMAPeriod = MathMax(NSTTEMAPeriod,1);
   NSTPeriod     = MathMax(NSTPeriod ,1);
     alphaNdx = 2.0 /(1.0 + NDXTEMAPeriod);
     alphaNst = 2.0 /(1.0 + NSTTEMAPeriod);
     
   //
   //
   //
   //
   //
   
      indicatorFileName = WindowExpertName();
      calculateValue    = (TimeFrame=="calculateValue"); if (calculateValue) return(0);
      returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
      timeFrame         = stringToTimeFrame(TimeFrame);
      
   //
   //
   //
   //
   //
           
   IndicatorShortName(timeFrameToString(timeFrame)+"  nxc (ndx: "+NDXPeriod+","+NDXTEMAPeriod+","+PriceType+") (nst: "+NSTPeriod+","+NSTTEMAPeriod+")");
   return(0);
}

//
//
//
//
//

int deinit() { return(0); }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define iPrc 6

//
//
//
//
//

int start()
{
   int    counted_bars=IndicatorCounted();
   int    i,k,r,n,s,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = MathMin(Bars-counted_bars,Bars-NDXPeriod-1);
         limit = MathMin(limit            ,Bars-NSTPeriod-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 (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,alphaNdx,0,r);
               if (ndxTemp > 90) ndxTemp =  90+( ndxTemp-90)/2.0;
               if (ndxTemp <-90) ndxTemp = -90-(-ndxTemp-90)/2.0;

      //
      //
      //   
      //
      //
      
         double max    = High[i];
         double min    = Low[i];
         double sumSto = 0;
      
            for (k=0, sumDen=0; k < NSTPeriod; k++)
            {
               if (max < High[i+k]) max = High[i+k];
               if (min >  Low[i+k]) min =  Low[i+k];
               if (max!=min)
                     stoch = (Close[i]-min)/(max-min);
               else  stoch = 0;
               coeff = 1.0/MathSqrt(k+1.0);
          
               sumSto += coeff*stoch;
               sumDen += coeff;
            }

            double nstTemp = iTema((200.0*sumSto/sumDen)-100.0,alphaNst,3,r);
               if (nstTemp > 85) nstTemp =  85+( nstTemp-85)/2.0;
               if (nstTemp <-85) nstTemp = -85-(-nstTemp-85)/2.0;

      //
      //
      //
      //
      //
      
         double nxcTemp = ((MathAbs(ndxTemp)*nstTemp)+(MathAbs(nstTemp)*ndxTemp))/2.0;
            if (nxcTemp>0)
                  nxcTemp = MathSqrt(nxcTemp);
            else  nxcTemp = MathSqrt(MathAbs(nxcTemp))*(-1);

         nxc[i]      = nxcTemp;
         obLine[i]   = OverBought;
         osLine[i]   = OverSold;
         zeroLine[i] = 0;
         trend[i]    = trend[i+1]; 
         
	   if (nxc[i] > osLine[i] && nxc[i+1] <= osLine[i+1]) trend[i]= 1; 
	   if (nxc[i] < obLine[i] && nxc[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,NDXTEMAPeriod,NDXPrice,NSTPeriod,NSTTEMAPeriod,OverBought,OverSold,0,y);
            obLine[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXTEMAPeriod,NDXPrice,NSTPeriod,NSTTEMAPeriod,OverBought,OverSold,1,y);
            osLine[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXTEMAPeriod,NDXPrice,NSTPeriod,NSTTEMAPeriod,OverBought,OverSold,2,y);
            nxc[i]      = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXTEMAPeriod,NDXPrice,NSTPeriod,NSTTEMAPeriod,OverBought,OverSold,3,y);
            trend[i]    = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NDXPeriod,NDXTEMAPeriod,NDXPrice,NSTPeriod,NSTTEMAPeriod,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++)
            {
  	            nxc[i+s] = nxc[i] + (nxc[i+n] - nxc[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 nxc ",doWhat);
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(StringConcatenate(Symbol()," ocn nxc "),message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iTema(double price,double alpha,int sbuf,int i)
{
   if (i < 1)
      {
         tBuffer[i][sbuf+0] = price;
         tBuffer[i][sbuf+1] = price;
         tBuffer[i][sbuf+2] = price;
      }
   else
      {
         tBuffer[i][sbuf+0] = tBuffer[i-1][sbuf+0]+alpha*(price             -tBuffer[i-1][sbuf+0]);
         tBuffer[i][sbuf+1] = tBuffer[i-1][sbuf+1]+alpha*(tBuffer[i][sbuf+0]-tBuffer[i-1][sbuf+1]);
         tBuffer[i][sbuf+2] = tBuffer[i-1][sbuf+2]+alpha*(tBuffer[i][sbuf+1]-tBuffer[i-1][sbuf+2]);
      }
   return(3.0*tBuffer[i][sbuf+0] - 3.0*tBuffer[i][sbuf+1] + tBuffer[i][sbuf+2]);
}