//+------------------------------------------------------------------+
//|                                Jim Sloman's natural market combo |
//|                                                     ocn nmc2.mq4 |
//|                                                           mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1  DeepSkyBlue
#property indicator_color2  Gray
#property indicator_style2  STYLE_DOT

//
//
//
//
//

extern string TimeFrame                     = "Current time frame";
extern int    NMCPeriod                     = 40;
extern int    NMCPrice                      = PRICE_CLOSE;
extern int    TEMAPeriod                    = 10;
extern bool   UseRiver                      = true;
extern int    LinearRegressionLength        = 50;
extern double LinearRegressionChannelWidth  = 2.0;
extern string IndicatorUniqueID             = "Nmc slope divergence";
extern color  ChartLineColor                = Gold;
extern int    ChartLineMiddleStyle          = STYLE_DOT;
extern int    ChartLineStyle                = STYLE_SOLID;
extern int    LineWidth                     = 0;

extern color  NmcLineColor                  = Gold;
extern int    NmcLineMiddleStyle            = STYLE_DOT;
extern int    NmcLineStyle                  = STYLE_SOLID;
extern bool   Interpolate                   = false;



//
//
//
//
//

double nmc[];
double zeroLine[];
double tBuffer[][4];
double alpha;
string shortName;
string indicatorFileName;
bool   returnBars;
bool   calculateValue;
int    timeFrame;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   SetIndexBuffer(0,nmc);      SetIndexLabel(0,"NMC") ; SetIndexDrawBegin(0,NMCPeriod);
   SetIndexBuffer(1,zeroLine); SetIndexLabel(1,NULL);    
   
   //
   //
   //
   //
   //
  
   string PriceType;
      switch(NMCPrice)
      {
         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
      }      
   string using = "";           
      if (UseRiver)
            using = "river ";
      else  using = "mirror ";
 
   //
   //
   //
   //
   //
   
      TEMAPeriod = MathMax(TEMAPeriod,1);
      NMCPeriod  = MathMax(NMCPeriod ,1);
           alpha = 2.0 /(1.0 + TEMAPeriod);
           
      indicatorFileName = WindowExpertName();
      returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
      calculateValue    = (TimeFrame=="calculateValue");
      if (calculateValue)
      {
         int s = StringFind(IndicatorUniqueID,":",0);
               shortName = IndicatorUniqueID;
               IndicatorUniqueID = StringSubstr(IndicatorUniqueID,0,s);
               return(0);
      }            
      timeFrame = stringToTimeFrame(TimeFrame);
      
           
      shortName = IndicatorUniqueID+":  "+timeFrameToString(timeFrame)+" - nmc2 "+using+" ("+NMCPeriod+","+PriceType+","+TEMAPeriod+","+LinearRegressionLength+")";

   IndicatorShortName(shortName);
   return(0);
}

//
//
//
//
//

int deinit()
{
   for (int i=ObjectsTotal(); i>=0; i--)
   {
      string name = ObjectName(i); if (StringFind(name,IndicatorUniqueID)==0) ObjectDelete(name);
   }
   return(0);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

#define iPrc 3

//
//
//
//
//

int start()
{
   int    counted_bars=IndicatorCounted();
   int    i,k,r,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = Bars-counted_bars;
         if (ArrayRange(tBuffer,0) != Bars) ArrayResize(tBuffer,Bars);
         if (returnBars) { nmc[0] = limit+1; return(0); }

   //
   //
   //
   //
   //
   
   if (calculateValue || timeFrame==Period())
   {    
     for(i=limit, r=Bars-i-1; i >= 0; i--,r++)
     {
        double rawPrice  = iMA(NULL,0,1,0,MODE_SMA,NMCPrice,i);
        double currPrice = iTema(rawPrice,r);
           if (currPrice > 0)
                 tBuffer[r][iPrc] = MathLog(currPrice);
           else  tBuffer[r][iPrc] = 0.00;
      
         //
         //
         //
         //
         //

         nmc[i]      = 0;
         zeroLine[i] = 0;

            double nms = iNmsFunction(i);            
            double tmp;
            if (UseRiver)
                  tmp = iNmrFunction(1,i);
            else  tmp = iNmmFunction(1,i);
                  tmp = (MathAbs(nms)*tmp+MathAbs(tmp)*nms)/2.00;
              if (tmp > 0) nmc[i] =  MathSqrt( tmp);
              if (tmp < 0) nmc[i] = -MathSqrt(-tmp);
              fillLrArray(i,0,nmc[i]);
              fillLrArray(i,1,iMA(NULL,0,1,0,MODE_SMA,NMCPrice,i));
   }
   
   //
   //
   //
   //
   //
   
   double nmcError; double nmcSlope; double lrnmc = iLrValue(nmc[0],                             LinearRegressionLength,nmcSlope,nmcError,0,0);
   double prcError; double prcSlope; double lrPrc = iLrValue(iMA(NULL,0,1,0,MODE_SMA,NMCPrice,0),LinearRegressionLength,prcSlope,prcError,0,1);
   int window = WindowFind(shortName);
   createLine(window,lrnmc,lrnmc-(LinearRegressionLength-1.0)*nmcSlope,"nmcLine",NmcLineColor  ,NmcLineStyle  ,NmcLineMiddleStyle  ,nmcError*LinearRegressionChannelWidth);
   createLine(0     ,lrPrc,lrPrc-(LinearRegressionLength-1.0)*prcSlope,"prcLine",ChartLineColor,ChartLineStyle,ChartLineMiddleStyle,prcError*LinearRegressionChannelWidth);
   
   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]);
         nmc[i]      = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NMCPeriod,NMCPrice,TEMAPeriod,UseRiver,LinearRegressionLength,LinearRegressionChannelWidth,shortName,ChartLineColor,ChartLineMiddleStyle,ChartLineStyle,NmcLineColor,NmcLineMiddleStyle,NmcLineStyle,0,y);
         zeroLine[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",NMCPeriod,NMCPrice,TEMAPeriod,UseRiver,LinearRegressionLength,LinearRegressionChannelWidth,shortName,ChartLineColor,ChartLineMiddleStyle,ChartLineStyle,NmcLineColor,NmcLineMiddleStyle,NmcLineStyle,1,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 x = 1; x < n; x++) 
            {
              
               nmc[i+x] = nmc[i] + (nmc[i+n] - nmc[i])* x/n;
            }
     
     
   }
   
return(0);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//    natural market slope
//
//
//

double iNmsFunction(int tPos)
{
   int    pos    = Bars-tPos-1;
   double sumxy  = 0.00;
   double sumy   = 0.00;
   double sum    = 0.00;

   //
   //
   //
   //
   //
   
   for (int period=1; period<=NMCPeriod; period++)
   {
      double sumx  = period*(period+1.0)/2.0;
      double sumx2 = period*(period+1.0)*(2*period+1.0)/6.0;
      double price = tBuffer[pos-(period-1)][iPrc];
      
             sumxy += sumy+price;
             sumy  +=      price;
             
      double bot   = period*sumx2 - sumx*sumx;
      double slope = 0.00;
      if (bot != 0) slope = (period*sumxy - sumx*sumy)/bot;
             sum += slope*MathSqrt(period);
   }           
   return(sum*100.00);
}

//
//
//
//    natural market mirror
//
//
//

double iNmmFunction(int startPeriod, int tPos)
{
   int    endPeriod = startPeriod*NMCPeriod;
   int    pos       = Bars-tPos-1;
   double sum       = 0.00;
   double currPrice = tBuffer[pos][iPrc];
      
   for (int i= startPeriod; i <= endPeriod; i += startPeriod)
           sum += (currPrice-tBuffer[pos-i][iPrc])/MathSqrt(i);
   return((sum/NMCPeriod)*1000.00);
}

//
//
//
//    natural market river
//
//
//

double iNmrFunction(int startPeriod, int tPos)
{
   int    endPeriod = startPeriod*NMCPeriod;
   int    pos       = Bars-tPos-1;
   double sum       = 0.00;
      
   for (int i=startPeriod; i <= endPeriod; i+=startPeriod)
          sum += (tBuffer[pos-i+startPeriod][iPrc]-tBuffer[pos-i][iPrc])*(MathSqrt(i)-MathSqrt(i-startPeriod));
   return(sum*1000.00);
}

//
//
//
//
//

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*tBuffer[i][0] - 3*tBuffer[i][1] + tBuffer[i][2]);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

void createLine(int window, double price1, double price2, string addName, color theColor, int theStyle, int theMiddleStyle, double error)
{
   string name = IndicatorUniqueID+addName+"mid";
      if (ObjectFind(name)==-1)
           ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
              ObjectSet(name,OBJPROP_PRICE1,price1);
              ObjectSet(name,OBJPROP_PRICE2,price2);
              ObjectSet(name,OBJPROP_TIME1,Time[0]);
              ObjectSet(name,OBJPROP_TIME2,Time[LinearRegressionLength-1]);
              ObjectSet(name,OBJPROP_RAY,false);
              ObjectSet(name,OBJPROP_COLOR,theColor);
              ObjectSet(name,OBJPROP_STYLE,theMiddleStyle);
                  if (error<=0) return;
      name = IndicatorUniqueID+addName+"up";
      if (ObjectFind(name)==-1)
           ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
              ObjectSet(name,OBJPROP_PRICE1,price1+error);
              ObjectSet(name,OBJPROP_PRICE2,price2+error);
              ObjectSet(name,OBJPROP_TIME1,Time[0]);
              ObjectSet(name,OBJPROP_TIME2,Time[LinearRegressionLength-1]);
              ObjectSet(name,OBJPROP_RAY,false);
              ObjectSet(name,OBJPROP_COLOR,theColor);
              ObjectSet(name,OBJPROP_STYLE,theStyle);
              ObjectSet(name,OBJPROP_WIDTH,LineWidth);
              
      name = IndicatorUniqueID+addName+"down";
      if (ObjectFind(name)==-1)
           ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
              ObjectSet(name,OBJPROP_PRICE1,price1-error);
              ObjectSet(name,OBJPROP_PRICE2,price2-error);
              ObjectSet(name,OBJPROP_TIME1,Time[0]);
              ObjectSet(name,OBJPROP_TIME2,Time[LinearRegressionLength-1]);
              ObjectSet(name,OBJPROP_RAY,false);
              ObjectSet(name,OBJPROP_COLOR,theColor);
              ObjectSet(name,OBJPROP_STYLE,theStyle);
              ObjectSet(name,OBJPROP_WIDTH,LineWidth);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

double workLr[][2];
void fillLrArray(int i, int instanceNo, double value)
{
   if (ArrayRange(workLr,0)!=Bars) ArrayResize(workLr,Bars); i = Bars-i-1; workLr[i][instanceNo] = value;
}
double iLrValue(double value, int period, double& slope, double& error, int r, int instanceNo=0)
{
   if (ArrayRange(workLr,0)!=Bars) ArrayResize(workLr,Bars); r = Bars-r-1; workLr[r][instanceNo] = value;
   if (r<period || period<2) return(value);

   //
   //
   //
   //
   //

      double sumx=0, sumxx=0, sumxy=0, sumy=0, sumyy=0;
         for (int k=0; k<period; k++)
         {
            double price = workLr[r-k][instanceNo];
                   sumx  += k;
                   sumxx += k*k;
                   sumxy += k*price;
                   sumy  +=   price;
                   sumyy +=   price*price;
         }
         slope = (period*sumxy-sumx*sumy)/(sumx*sumx-period*sumxx);
         error = MathSqrt((period*sumyy-sumy*sumy-slope*slope*(period*sumxx-sumx*sumx))/(period*(period-2)));

   //
   //
   //
   //
   //
         
   return((sumy + slope*sumx)/period);
}

//
//
//
//
//

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);
}
