//+------------------------------------------------------------------+
//|                                   Copyright © 2012, Ivan Kornilov|
//|                                RegressionPolynomial with ATR.mq4 |
//|                                   excelf@gmail.com, skype: excelf|
//|                                              updated by alToronto|
//+------------------------------------------------------------------+

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1 DeepSkyBlue
#property indicator_color2 OrangeRed
#property indicator_color3 Red
#property indicator_color4 DodgerBlue

extern int history = 10000;
extern int period  = 55;
extern int signal  = 5;
extern int degree  = 2;
extern int price   = 0;
extern int ATRPeriod = 14;
extern bool drawArrow = true;
extern double ArrowsGap = 0.5;
extern string ArrowsID = "RegrArrows1";

double Line[];
double Signal[];
double atr1[];
double atr2[];


int init(){
   SetIndexStyle(0, DRAW_LINE, DRAW_LINE, 2);
   SetIndexStyle(1, DRAW_LINE, DRAW_LINE, 2);
   SetIndexStyle(2, DRAW_LINE, STYLE_DOT, 1);
   SetIndexStyle(3, DRAW_LINE, STYLE_DOT, 1);
   SetIndexBuffer(0, Line);
   SetIndexBuffer(1, Signal);
   SetIndexBuffer(2, atr1);
   SetIndexBuffer(3, atr2);
   IndicatorDigits(Digits);
   IndicatorShortName("RegressionPolynomial (" + period + ", " + degree + ", "+ history + ")");
   if(period < 2) {
      period = 2;
   }
}
int deinit()
{
   int lookForLength = StringLen(ArrowsID);
      for (int i=ObjectsTotal()-1; i>=0; i--) 
      {
         string name = ObjectName(i);  if (StringSubstr(name,0,lookForLength) == ArrowsID) ObjectDelete(name);
      }
   return(0); 
}


int start() {
   int countedBars = IndicatorCounted();
   if(countedBars < 0) {
      countedBars = 0;
   }
   if(countedBars > 0) {
      countedBars--;
   }
   int limit = MathMin(Bars - countedBars -1, history);
   for(int shift = limit; shift >= 0; shift--){
      if(degree == 1) {
         Line[shift] = regression.LRMA(period, shift, price);
      } else if(degree == 2) {
         Line[shift] = regression.QRMA(period, shift, price);
      } else {
         Line[shift] = regression.regressionPolynomial(shift, period, degree, price);
      }      
   }  
   for(shift = limit; shift >= 0; shift--){
      Signal[shift] = iMAOnArray(Line, Bars, signal, 0, 0, shift);
      double atr = iATR(Symbol(), 0, ATRPeriod, shift);
      atr1[shift] = Line[shift] + atr;
      atr2[shift] = Line[shift] - atr;
      
      if(drawArrow) {
         string name = ArrowsID+":"+Time[shift];ObjectDelete(name);
         if(Signal[shift] < Line[shift] && Signal[shift + 1] > Line[shift + 1]) {
             addArrow(1, shift);
         } else if(Signal[shift] > Line[shift] && Signal[shift + 1] < Line[shift + 1]) {
             addArrow(-1, shift);
         }     
      } 
   } 
}

double regression.QRMA(int period, int shift, int price) {
   double lwma = iMA(NULL, 0, period, 0, MODE_LWMA, price, shift);
   double sma = iMA(NULL, 0, period, 0, MODE_SMA, price, shift);
   double qwma = ma.qwma(period, shift, price);
   double value = 3.0 * sma + qwma * (10 - 15 / (period + 2 )) - lwma * (12 - 15 / (period + 2));
   return(value);
}

double ma.qwma(int period, int shift, int price) {
   double sum = 0;
   int j, i;
   for(j = shift,i = 1;j < shift + period; j++, i++ ) {
      sum += switch.getPrice(price, j) * MathPow(period - i + 1, 2);
   }
   double value = 6.0 / (period * ( period + 1) * (2 * period + 1)) ;
   return(value * sum);
}

double a[10, 10];
double b[10];
double x[10];
double sx[20];
int n;
int i;
int k;
double t;
int degree1;

double regression.regressionPolynomial(int shift, int period, int degree, int priceCase) {
      sx[1] = period + 1;
      degree1 = degree + 1;
   
      for(i = 1;i <= degree1 * 2 - 2; i++) {
         sx[i+1] = 0;
         for(n = 0;n <= period; n++){
            sx[i+1] += MathPow(n, i);
         }
      }  
      double t;
      for(i = 1; i <= degree1; i++){
         b[i] = 0;
         for(n = 0; n <= period; n++){ 
            if(i == 1) {
               b[i] += switch.getPrice(priceCase, n + shift);
            } else {
               b[i] += switch.getPrice(priceCase, n + shift) * MathPow(n, i - 1);
            }
         }
      } 

      for(int j = 1;j <= degree1; j++){
         for(i = 1; i <= degree1; i++){
            k = i + j - 1;
            a[i, j] = sx[k];
         }
      }  

      for(k = 1; k <= degree1-1; k++){
         int l = 0;
         double mm = 0;
         for(i = k; i <= degree1; i++){
            if(MathAbs(a[i, k]) > mm){
               mm = MathAbs(a[i, k]);
               l = i;
            }
         }
         if(l == 0) {
            return(0);   
         }
         if (l != k){
            for(j = 1; j <= degree1; j++){
               t = a[k, j];
               a[k, j] = a[l, j];
               a[l, j] = t;
            }
            t = b[k];
            b[k] = b[l];
            b[l] = t;
         }  
         double div = 0;
         for(i = k + 1;i <= degree1; i++){
            div = a[i, k] / a[k, k];
            for(j = 1;j <= degree1; j++){
               if(j == k) {
                  a[i, j] = 0;
               } else { 
                  a[i, j] = a[i, j] - div * a[k, j];
               }
            }
            b[i] = b[i] - div * b[k];
         }
      }  
   
      x[degree1] = b[degree1] / a[degree1, degree1];
      for(i = degree;i >= 1; i--){
         t = 0;
         for(j = 1;j <= degree1 - i; j++){
            t = t + a[i, i + j] * x[i + j];
            x[i] = (1 / a[i, i]) * (b[i] - t);
         }
      } 

      double value = x[1];
      for(i = 1;k <= degree; i++){
         value += x[i + 1] * MathPow(n, i);
      }
      return (value);
}

double regression.LRMA(int period, int shift, int price){     
      return(3 * iMA(NULL, 0, period, 0, MODE_LWMA, price, shift) - 2 * iMA(NULL, 0, period, 0, MODE_SMA, price, shift));
}


double switch.getPrice(int price, int shift) {
    switch(price) {
      case 0:
          return(Close[shift]);
      case 1:
          return(Open[shift]);
      case 2:
          return(High[shift]);
      case 3:
          return(Low[shift]);
      case 4:
          return((High[shift] + Low[shift]) / 2);
      case 5:
          return((High[shift] + Low[shift] + Close[shift]) / 3);
      case 6:
          return((High[shift] + Low[shift] + Close[shift] + Close[shift]) / 4);
      default: 
          return(Close[shift]);
   }
}

//
//
//
//
//

void addArrow(int signal, int i) {
   string name = ArrowsID+":"+Time[i];
   double atr = iATR(NULL,0,20,i);
   ObjectDelete(name);
      ObjectCreate(name, OBJ_ARROW, 0, Time[i], 0, 0, 0, 0, 0);                
      if(signal == 1) {
         ObjectSet(name, OBJPROP_PRICE1, MathMin(Line[i],Signal[i])-atr*ArrowsGap);
         ObjectSet(name, OBJPROP_ARROWCODE, 225);
         ObjectSet(name, OBJPROP_COLOR, DeepSkyBlue);
      } else if(signal == -1) {
         ObjectSet(name, OBJPROP_PRICE1, MathMax(Line[i],Signal[i])+atr*ArrowsGap);
         ObjectSet(name, OBJPROP_ARROWCODE, 226);
         ObjectSet(name, OBJPROP_COLOR, OrangeRed);
      }
}