//+------------------------------------------------------------------+
//|         Jim Sloman's natural moving average + standard deviation |
//|                                                 ocn nma & SD.mq4 |
//|                                                           mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_chart_window
#property indicator_buffers 7
#property indicator_color1  clrDimGray
#property indicator_color2  clrLightBlue 
#property indicator_color3  clrLightBlue 
#property indicator_color4  clrTomato 
#property indicator_color5  clrTomato 
#property indicator_color6  clrGreen
#property indicator_color7  clrRed
#property indicator_width1  2
#property indicator_width2  2
#property indicator_width3  2
#property indicator_width4  2
#property indicator_width5  2
#property indicator_style6  STYLE_DOT
#property indicator_style7  STYLE_DOT


//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame   = PERIOD_CURRENT; // Time frame
extern int                NMAPeriod   = 40;
extern ENUM_APPLIED_PRICE NMAPrice    = PRICE_CLOSE;
extern int                TEMAPeriod  =  1;
extern int                SDPeriod    = 30;
extern double             SDUp        = 2.0;
extern double             SDDown      = 2.0;
extern bool               UseLog      = true;
input bool                Interpolate = true;              // Interpolate in mtf mode

//
//
//
//
//

double nma[];
double sdUp[];
double sdDo[];
double tBuffer[][5];
double trend[],valda[],valdb[],valua[],valub[],count[];
double alpha;
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,NMAPeriod,NMAPrice,TEMAPeriod,SDPeriod,SDUp,SDDown,UseLog,_buff,_ind)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(9);
   SetIndexBuffer(0,nma);
   SetIndexBuffer(1,valua); 
   SetIndexBuffer(2,valub);
   SetIndexBuffer(3,valda);
   SetIndexBuffer(4,valdb); 
   SetIndexBuffer(5,sdUp);
   SetIndexBuffer(6,sdDo);
   SetIndexBuffer(7,trend);
   SetIndexBuffer(8,count);
   
   TEMAPeriod = MathMax(TEMAPeriod,1);
   NMAPeriod  = MathMax(NMAPeriod ,1);
   alpha = 2.0 /(1.0 + TEMAPeriod);
   
   indicatorFileName = WindowExpertName();
   TimeFrame         = fmax(TimeFrame,_Period);
           
   IndicatorShortName ("NMA");
   return(0);
}
int deinit()
{
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define iPrc 3
#define iMom 4

//
//
//
//
//

int start()
{
   int    counted_bars=IndicatorCounted();
   int    i,k,r,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = fmin(Bars-counted_bars,Bars-1); count[0] = limit;
         if (ArrayRange(tBuffer,0) != Bars) ArrayResize(tBuffer,Bars);
   //
   //
   //
         
   if (TimeFrame != Period())
   {
      limit = (int)MathMax(limit,MathMin(Bars-1,_mtfCall(8,0)*TimeFrame/_Period));
      if (trend[limit]==-1) CleanPoint(limit,Bars,valda,valdb);
      if (trend[limit]== 1) CleanPoint(limit,Bars,valua,valub);
      for(i=limit; i>=0 && !_StopFlag; i--)
      {
         int y = iBarShift(NULL,TimeFrame,Time[i]);
            nma[i]   = _mtfCall(0,y);
            sdUp[i]  = _mtfCall(5,y);
            sdDo[i]  = _mtfCall(6,y);
            trend[i] = _mtfCall(7,y); 
            valda[i] = valdb[i] = valua[i] = valub[i] = EMPTY_VALUE;
              
            //
            //
            //
                     
            if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
              #define _interpolate(buff) buff[i+k] = buff[i]+(buff[i+n]-buff[i])*k/n
              int n; datetime btime = iTime(NULL,TimeFrame,y);
                 for(n = 1; (i+n)<Bars && Time[i+n] >= btime; n++) continue;	
                 for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++)
                 {
                     _interpolate(nma); 
                     _interpolate(sdUp); 
                     _interpolate(sdDo);
                 }
                                       
        }
        for (i=limit; i >= 0; i--)
        {
           if (trend[i]==-1) PlotPoint(i,Bars,valda,valdb,nma);
           if (trend[i]== 1) PlotPoint(i,Bars,valua,valub,nma);
	     }     
   return(0);
   }  
   
   //
   //
   //
   
   if (trend[limit]==-1) CleanPoint(limit,Bars,valda,valdb);
   if (trend[limit]== 1) CleanPoint(limit,Bars,valua,valub);
   for(i=limit, r=Bars-i-1; i >= 0; i--,r++)
   {
      double deviation = iStdDev(NULL,0,SDPeriod,0,MODE_SMA,NMAPrice,i);
      double rawPrice  = iMA(NULL,0,1,0,MODE_SMA,NMAPrice,i);
      double calPrice  = iTema(rawPrice,i);
      
         tBuffer[r][iPrc] = calPrice;
                 if (UseLog && (calPrice>0)) tBuffer[r][iPrc] = MathLog(calPrice);
         tBuffer[r][iMom] = tBuffer[r][iPrc]-tBuffer[r-1][iPrc];
   
         //
         //
         //
         //
         //
                      
            double momRatio = 0.00;
            double sumMomen = 0.00;
            double ratio    = 0.00;
      
            for (k = 0; k<NMAPeriod; k++)
            {
               sumMomen += MathAbs(tBuffer[r-k][iMom]);
               momRatio +=         tBuffer[r-k][iMom]*(MathSqrt(k+1)-MathSqrt(k));
            }
            if (sumMomen != 0) ratio = MathAbs(momRatio)/sumMomen;
      
         //
         //
         //
         //
         //
                 
         nma[i]  = nma[i+1]+ratio*(rawPrice-nma[i+1]);
         if (SDUp   !=0) sdUp[i] = nma[i]+deviation*SDUp;
         if (SDDown !=0) sdDo[i] = nma[i]-deviation*SDDown;
         valda[i] = valdb[i] = valua[i] = valub[i] = EMPTY_VALUE;
         trend[i] = (i<Bars-1) ? (nma[i]>nma[i+1]) ? 1 : (nma[i]<nma[i+1]) ? -1 :  trend[i+1] : 0;
         if (trend[i]==-1) PlotPoint(i,Bars,valda,valdb,nma);
         if (trend[i]== 1) PlotPoint(i,Bars,valua,valub,nma);
   }
   
   //
   //
   //
   //
   //
   
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iTema(double price,int pos)
{
   int i = Bars-pos-1;
   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*tBuffer[i][0] - 3*tBuffer[i][1] + tBuffer[i][2]);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------

void CleanPoint(int i,int bars,double& first[],double& second[])
{
   if (i>=bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

void PlotPoint(int i,int bars,double& first[],double& second[],double& from[])
{
   if (i>=bars-2) return;
   if (first[i+1] == EMPTY_VALUE)
      if (first[i+2] == EMPTY_VALUE) 
            { first[i]  = from[i]; first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
      else  { second[i] = from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else     { first[i]  = from[i];                          second[i] = EMPTY_VALUE; }
}

//
//
//

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("");
}


