//------------------------------------------------------------------
#property copyright "www.forex-tsd.cm"
#property link      "www.forex-tsd.cm"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1  Lime
#property indicator_width1  3
#property indicator_color2  Red
#property indicator_width2  3
#property indicator_color3  DarkSlateGray
#property indicator_width3  3
#property indicator_level1  0

//
//
//
//
//

extern int    Length                       = 14;
extern int    Price                        = 0;
extern int    LinearRegressionLength       = 50;
extern double LinearRegressionChannelWidth = 2.0;
extern string IndicatorUniqueID            = "velocity slope divergence";
extern color  ChartLineColor               = Goldenrod;
extern int    ChartLineMiddleStyle         = STYLE_DOT;
extern int    ChartLineStyle               = STYLE_SOLID;
extern color  VelocityLineColor            = Goldenrod;
extern int    VelocityLineMiddleStyle      = STYLE_DOT;
extern int    VelocityLineStyle            = STYLE_SOLID;

//
//
//
//
//

double upH[];
double dnH[];
double vel[];
double trend[];
string shortName;

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(4);
   SetIndexBuffer(0,upH); SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(1,dnH); SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(2,vel);
   SetIndexBuffer(3,trend);
   shortName = IndicatorUniqueID+" - velocity ("+Length+","+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);
}

//
//
//
//
//

int start()
{
   int counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = MathMin(Bars-counted_bars,Bars-1);

   //
   //
   //
   //
   //
   
   for(int i=limit; i>=0; i--) 
   {  
      vel[i] = iVelocity(iMA(NULL,0,1,0,MODE_SMA,Price,i),Length,i);
      upH[i] = EMPTY_VALUE;
      dnH[i] = EMPTY_VALUE;
      trend[i] = trend[i+1];
      if (vel[i]>0) trend[i] = 1;
      if (vel[i]<0) trend[i] =-1;
      if (trend[i] == 1) upH[i] = vel[i];
      if (trend[i] ==-1) dnH[i] = vel[i];
      fillLrArray(i,0,vel[i]);
      fillLrArray(i,1,iMA(NULL,0,1,0,MODE_SMA,Price,i));
   }
   
   //
   //
   //
   //
   //
   
   double velError; double velSlope; double lrvel = iLrValue(vel[0],                          LinearRegressionLength,velSlope,velError,0,0);
   double prcError; double prcSlope; double lrPrc = iLrValue(iMA(NULL,0,1,0,MODE_SMA,Price,0),LinearRegressionLength,prcSlope,prcError,0,1);
   int window = WindowFind(shortName);
   createLine(window,lrvel,lrvel-(LinearRegressionLength-1.0)*velSlope,"velLine",VelocityLineColor,VelocityLineStyle,VelocityLineMiddleStyle,velError*LinearRegressionChannelWidth);
   createLine(0     ,lrPrc,lrPrc-(LinearRegressionLength-1.0)*prcSlope,"prcLine",ChartLineColor,   ChartLineStyle,   ChartLineMiddleStyle,   prcError*LinearRegressionChannelWidth);
return(0);
}
  
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double prices[];
double iVelocity(double price, double length, int i)
{
   if (ArraySize(prices)!=Bars) ArrayResize(prices,Bars); i=Bars-i-1;
   
      prices[i] = price;
      double suma = 0.0, sumwa=0;
      double sumb = 0.0, sumwb=0;

      for(int k=0; k<length; k++)
      {
         double weight = length-k;
            suma  += prices[i-k] * weight;
            sumb  += prices[i-k] * weight * weight;
            sumwa += weight;
            sumwb += weight*weight;
      }
   return(sumb/sumwb-suma/sumwa);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

void createLine(int window, double price1, double price2, string addName, color theColor, int theStyle, int theMiddleStyle, double error)
{
   string name = IndicatorUniqueID+addName;
      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);
      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);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

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);
}