//+------------------------------------------------------------------+
//|                              Jim Sloman's natural moving average |
//|                                                      ocn nma.mq4 |
//|                                                           mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1  SteelBlue
#property indicator_width1  2

//
//
//
//
//

extern string TimeFrame     = "Current time frame";
extern int    NMAPeriod     = 40;
extern int    NMAPrice      = PRICE_CLOSE;
extern int    TEMAPeriod    = 1;
extern int    SmoothPeriod  = 8;
extern bool   Interpolate   = true;

//
//
//
//
//

double nma[];
double nmas[];
double tBuffer[][5];
double alpha;
double coef1;
double coef2;
double coef3;

string indicatorFileName;
bool   returnBars;
bool   calculateValue;
int    timeFrame;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define Pi 3.141592653589793238462643

int init()
{
   IndicatorBuffers(2);
   SetIndexBuffer(0,nmas);
   SetIndexBuffer(1,nma);

      //
      //
      //
      //
      //
         
      TEMAPeriod = MathMax(TEMAPeriod,1);
      NMAPeriod  = MathMax(NMAPeriod ,1);
           alpha = 2.0 /(1.0 + TEMAPeriod);
           SmoothPeriod  = MathMax(SmoothPeriod,1);
                double a = MathExp(-1.414*Pi/SmoothPeriod);
                double b = 2*a*MathCos(1.414*Pi/SmoothPeriod);

                  coef2 = b;
                  coef3 = -a*a;
                  coef1 = 1-coef2-coef3;

      //
      //
      //
      //
      //
   
         indicatorFileName = WindowExpertName();
         returnBars        = (TimeFrame == "returnBars");     if (returnBars)     return(0);
         calculateValue    = (TimeFrame == "calculateValue"); if (calculateValue) return(0);
         timeFrame         = stringToTimeFrame(TimeFrame);   

      //
      //
      //
      //
      //
   IndicatorShortName (timeFrameToString(timeFrame)+" NMA ("+NMAPeriod+")");
   return(0);
}
int deinit()
{
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define iPrc 3
#define iMom 4

//
//
//
//
//

int start()
{
   int i,k,n,r,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = MathMin(Bars-counted_bars,Bars-1);
         if (returnBars) { nmas[0] = limit+1; return(0); }

   //
   //
   //
   //
   //

   if (calculateValue || timeFrame == Period())
   {
      if (ArrayRange(tBuffer,0) != Bars) ArrayResize(tBuffer,Bars);
      for(i=limit, r=Bars-i-1; i >= 0; i--,r++)
      {
         double rawPrice = iMA(NULL,0,1,0,MODE_SMA,NMAPrice,i);
      
         tBuffer[r][iPrc] = iTema(rawPrice,i);
         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 (i >= Bars-2)
               nmas[i] = nma[i];
         else  nmas[i] = coef1*nma[i]+coef2*nmas[i+1]+coef3*nmas[i+2];         
      }
      return(0);
   }      
   
   //
   //
   //
   //
   //

   limit = MathMax(limit,MathMin(Bars-1,iCustom(NULL,timeFrame,indicatorFileName,"returnBars",0,0)*timeFrame/Period())); 
   for(i=limit; i>=0; i--)
   {
      int y = iBarShift(NULL,timeFrame,Time[i]);
         nmas[i]    = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NMAPeriod,NMAPrice,TEMAPeriod,SmoothPeriod,0,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(k = 1; k < n; k++)
                  nmas[i+k] = nmas[i] + (nmas[i+n] - nmas[i])*k/n;
   }
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iTema(double price,int shift)
{
   int i = Bars-shift-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]);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

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);
}