//+----------------------------------------------------------------------+
//| https://www.forexfactory.com/showthread.php?p=10306064#post10306064  |
//| https://en.wikipedia.org/wiki/Theil%E2%80%93Sen_estimator            |
//+----------------------------------------------------------------------+
//| Contributors:
//| https://www.forexfactory.com/mladen                                  |
//| https://www.forexfactory.com/lukeb                                   |
//+----------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"
#property version   "1.01"
#property description "LukeB: Re-write including removal various unused code and adjusted to work in the strategy tester"
#property strict
//------------------------------------------------------------------
#property indicator_chart_window
#property indicator_buffers 6
enum   INDI_ARRAYS                    {RMEC,            RMEH,         RMEL,             RMECF,             RMEHF,            RMELF,   endIndiArrays};
string indiName[endIndiArrays]      = {"RMEC",          "RMEH",       "RMEL",           "RMECF",           "RMEHF",          "RMELF"               };
int    indiShape[endIndiArrays]     = {DRAW_LINE,       DRAW_LINE,    DRAW_LINE,        DRAW_LINE,         DRAW_LINE,        DRAW_LINE             };
int    indiDrawStyle[endIndiArrays] = {STYLE_SOLID,      STYLE_SOLID, STYLE_SOLID,      STYLE_DASHDOTDOT,  STYLE_DASHDOTDOT, STYLE_DASHDOTDOT      };
int    indiWidth[endIndiArrays]     = {    1,              1,           1,                1,                 1,                1                   };
int    indiShift[endIndiArrays]     = {    0,              0,           0,                0,                 0,                0                   };
color  indiClr[endIndiArrays]       = {clrDimGray,     clrDodgerBlue, clrPaleVioletRed, clrDimGray,        clrDeepSkyBlue,   clrMagenta            };
//
struct indicaorStruct
 {
   double indiBuff[];
 } indiBuffs[endIndiArrays];
//
double worksFull[];
double worksTemp[];
double workiFull[];
double workiTemp[];
//
extern int                UniqueNum   = 7236;         // Number to make this Indicator Instance Unique
extern int                Length      = 200;          // Period
extern int                FutureBars  = 25;           // "Future" bars to show
//------------------------------------------------------------------
string indiShortName = "RepeatedMedianLine";
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//|------------------------------------------------------------------|
int init()
 {
   int initResult = INIT_SUCCEEDED;
   indiShortName = StringConcatenate(indiShortName+" "+IntegerToString(UniqueNum));
   IndicatorShortName( indiShortName );
   int minBars = getMinBars();
   if( Bars < minBars )
    {
      // initResult = INIT_FAILED;
      Print(__FUNCTION__,"INIT_FAILED, isuficient bars, ",IntegerToString(Bars)," found, ",IntegerToString(minBars)," bars are required.");
    }
   indiShift[RMECF] = FutureBars ;
   indiShift[RMEHF] = FutureBars ;
   indiShift[RMELF] = FutureBars ;
   initResult = sizeTheWorkArrays(Length);
   for(INDI_ARRAYS idx = 0; idx<endIndiArrays; idx++)
    {
      indiName[idx] = indiName[idx]+IntegerToString(UniqueNum);
    }
   IndicatorBuffers(endIndiArrays);
   for(INDI_ARRAYS idx = 0; idx<endIndiArrays; idx++)
    {
      initializeIndiBuffs(idx);
    }
   return ( initResult );
}
void initializeIndiBuffs(INDI_ARRAYS idx)
 {
   SetIndexBuffer    (idx, indiBuffs[idx].indiBuff);
   SetIndexLabel     (idx, indiName[idx]);
   SetIndexStyle     (idx, indiShape[idx], indiDrawStyle[idx], indiWidth[idx], indiClr[idx]);
   SetIndexEmptyValue(idx, EMPTY_VALUE);
   SetIndexShift     (idx, indiShift[idx]);
   SetIndexDrawBegin (idx, 0);
 }
int getMinBars()
 {
   return( MathMax(Length,FutureBars)+10 ); // Some reasonable minimum number of bars
 }
int sizeTheWorkArrays(int _period)
 {   
   ArrayResize(worksFull,_period);  // Opportity to check for resize errors
   ArrayResize(worksTemp,_period);
   ArrayResize(workiFull,_period);
   ArrayResize(workiTemp,_period);
   //
   return(INIT_SUCCEEDED);
 }
//+------------------------------------------------------------------+
//| Custor indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
 {
   //
 }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[],
                const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])
 {
   if( rates_total>getMinBars() )
    {
      
      int newBars = rates_total-prev_calculated;
      if(prev_calculated < 1 ) // First time
       {
         drawMedianLines( high, low, close );
       }
      else if(newBars>0)
       {
         cleanOldValues(newBars);
         drawMedianLines( high, low, close );
       }
    }
   return(rates_total);
 }
void drawMedianLines( const double &high[], const double &low[], const double &close[] )
 {
   double slopec=0,interceptc=0;  
   double slopeh=0,intercepth=0;  
   double slopel=0,interceptl=0;
   //
   iRepeatedMedianLine(Length,close,slopec,interceptc);         
   iRepeatedMedianLine(Length,high,slopeh,intercepth);         
   iRepeatedMedianLine(Length,low,slopel,interceptl);         
   //
   for (int k=0; k< Length; k++) 
    {
      indiBuffs[RMEC].indiBuff[k] = interceptc-slopec*k;
      indiBuffs[RMEH].indiBuff[k] = intercepth-slopeh*k;
      indiBuffs[RMEL].indiBuff[k] = interceptl-slopel*k;
    }         
   indiBuffs[RMECF].indiBuff[FutureBars+1] = indiBuffs[RMEC].indiBuff[1];
   indiBuffs[RMEHF].indiBuff[FutureBars+1] = indiBuffs[RMEH].indiBuff[1];
   indiBuffs[RMELF].indiBuff[FutureBars+1] = indiBuffs[RMEL].indiBuff[1];
   for (int k=0; k<=FutureBars; k++)
    {
      indiBuffs[RMECF].indiBuff[FutureBars-k] = interceptc+slopec*k;
      indiBuffs[RMEHF].indiBuff[FutureBars-k] = intercepth+slopeh*k;
      indiBuffs[RMELF].indiBuff[FutureBars-k] = interceptl+slopel*k;
    }
 }
void iRepeatedMedianLine(const int& _period, const double& _values[], double& _slope, double& _intercept)
{
   for (int j=0; j<_period; j++)
   {
      for (int k=0; k<_period; k++)
      {
         if (k==j) continue;
            worksTemp[k] = (  _values[k] -   _values[j])/(j-k);
            workiTemp[k] = (j*_values[k] - k*_values[j])/(j-k);
      }   
      ArraySort(worksTemp);
      ArraySort(workiTemp);
      double index = (_period-2)/2.0; // 198/2 = 96  
      int    ind   = (int)index;
      if (ind == NormalizeDouble(index,5)) // ix index == whole number to 5 decimal places?  yes...
       {
         worksFull[j] = worksTemp[ind];
         workiFull[j] = workiTemp[ind];
       }                        
      else  
       {
         worksFull[j] = worksTemp[ind]/2.0+worksTemp[ind+1]/2.0;
         workiFull[j] = workiTemp[ind]/2.0+workiTemp[ind+1]/2.0;
       }                     
    }           
   ArraySort(worksFull);
   ArraySort(workiFull);
   double index = (_period-1)/2.0;   // 200-1/2 = 99.5
   int    ind   = (int)index;
   if (ind == NormalizeDouble(index,5))  // is Index == Whole number to 5 decimal places?  no....
    {
      _slope     = worksFull[ind];
      _intercept = workiFull[ind];
    }                  
   else
    {
      _slope     = worksFull[ind]/2.0+worksFull[ind+1]/2.0;
      _intercept = workiFull[ind]/2.0+workiFull[ind+1]/2.0;
    }    
}
//
void cleanOldValues(int barsToClean)
 {
   for(int i=0; i<barsToClean; i++ )
    {
      int idx = Length+barsToClean-i;
      //
      indiBuffs[RMEC].indiBuff[idx] = EMPTY_VALUE;
      indiBuffs[RMEH].indiBuff[idx] = EMPTY_VALUE;
      indiBuffs[RMEL].indiBuff[idx] = EMPTY_VALUE;
    }
   for(int i=-1; i<=(barsToClean); i++ )
    {
      int idx = FutureBars+barsToClean-i;
      //
      indiBuffs[RMECF].indiBuff[idx] = EMPTY_VALUE;
      indiBuffs[RMEHF].indiBuff[idx] = EMPTY_VALUE;
      indiBuffs[RMELF].indiBuff[idx] = EMPTY_VALUE;
    }
 }
//