//+------------------------------------------------------------------+
//|                                                    RSTL_COG.mq5  |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   5

//--- Plot settings
#property indicator_label1  "FX2"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrLightBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_label2  "SQH2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrYellow
#property indicator_style2  STYLE_DOT

#property indicator_label3  "SQL2"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrYellow
#property indicator_style3  STYLE_DOT

#property indicator_label4  "STDH2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrGold
#property indicator_style4  STYLE_DASH

#property indicator_label5  "STDL2"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrGold
#property indicator_style5  STYLE_DASH

//--- Input parameters
input int      InpBarsBack = 240; // Bars Back for Regression
input double   InpKStd     = 2.0; // Standard Deviation Multiplier

//--- Indicator buffers
double fx2Buffer[];
double sqh2Buffer[];
double sql2Buffer[];
double stdh2Buffer[];
double stdl2Buffer[];

//--- Temporary buffer for the RSTL digital filter
double RSTLbuffertmp[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- Mapping indicator buffers
   SetIndexBuffer(0, fx2Buffer,   INDICATOR_DATA);
   SetIndexBuffer(1, sqh2Buffer,  INDICATOR_DATA);
   SetIndexBuffer(2, sql2Buffer,  INDICATOR_DATA);
   SetIndexBuffer(3, stdh2Buffer, INDICATOR_DATA);
   SetIndexBuffer(4, stdl2Buffer, INDICATOR_DATA);

   //--- Set empty values for clean look on old bars
   for(int i=0; i<5; i++) {
      PlotIndexSetDouble(i, PLOT_EMPTY_VALUE, 0.0);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 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[])
{
   //--- Check for sufficient data
   if(rates_total < 300 + InpBarsBack + 100) return(0);

   //--- Handle array positioning (AsSeries allows indexing 0 as the newest bar)
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(fx2Buffer, true);
   ArraySetAsSeries(sqh2Buffer, true);
   ArraySetAsSeries(sql2Buffer, true);
   ArraySetAsSeries(stdh2Buffer, true);
   ArraySetAsSeries(stdl2Buffer, true);

   //--- Clean up dynamic temporary buffer sizing
   ArrayResize(RSTLbuffertmp, rates_total);
   ArraySetAsSeries(RSTLbuffertmp, true);
   
   //--- Initialize outputs to 0
   ArrayInitialize(fx2Buffer, 0.0);
   ArrayInitialize(sqh2Buffer, 0.0);
   ArrayInitialize(sql2Buffer, 0.0);
   ArrayInitialize(stdh2Buffer, 0.0);
   ArrayInitialize(stdl2Buffer, 0.0);

   //--- Step 1: Calculate RSTL Digital Filter Buffer
   int limit = 300; 
   for(int i = limit; i >= 0; i--)
   {
      RSTLbuffertmp[i] = 
         -0.00514293*close[i+0]   -0.00398417*close[i+1]   -0.00262594*close[i+2]   -0.00107121*close[i+3]   
         +0.00066887*close[i+4]   +0.00258172*close[i+5]   +0.00465269*close[i+6]   +0.00686394*close[i+7]   
         +0.00919334*close[i+8]   +0.01161720*close[i+9]   +0.01411056*close[i+10]  +0.01664635*close[i+11]  
         +0.01919533*close[i+12]  +0.02172747*close[i+13]  +0.02421320*close[i+14]  +0.02662203*close[i+15]  
         +0.02892446*close[i+16]  +0.03109071*close[i+17]  +0.03309496*close[i+18]  +0.03490921*close[i+19]  
         +0.03651145*close[i+20]  +0.03788045*close[i+21]  +0.03899804*close[i+22]  +0.03984915*close[i+23]  
         +0.04042329*close[i+24]  +0.04071263*close[i+25]  +0.04071263*close[i+26]  +0.04042329*close[i+27]  
         +0.03984915*close[i+28]  +0.03899804*close[i+29]  +0.03788045*close[i+30]  +0.03651145*close[i+31]  
         +0.03490921*close[i+32]  +0.03309496*close[i+33]  +0.03109071*close[i+34]  +0.02892446*close[i+35]  
         +0.02662203*close[i+36]  +0.02421320*close[i+37]  +0.02172747*close[i+38]  +0.01919533*close[i+39]  
         +0.01664635*close[i+40]  +0.01411056*close[i+41]  +0.01161720*close[i+42]  +0.00919334*close[i+43]  
         +0.00686394*close[i+44]  +0.00465269*close[i+45]  +0.00258172*close[i+46]  +0.00066887*close[i+47]  
         -0.00107121*close[i+48]  -0.00262594*close[i+49]  -0.00398417*close[i+50]  -0.00514293*close[i+51]  
         -0.00609634*close[i+52]  -0.00684602*close[i+53]  -0.00739452*close[i+54]  -0.00774847*close[i+55]  
         -0.00791630*close[i+56]  -0.00790940*close[i+57]  -0.00774085*close[i+58]  -0.00742482*close[i+59]  
         -0.00697718*close[i+60]  -0.00641613*close[i+61]  -0.00576108*close[i+62]  -0.00502957*close[i+63]  
         -0.00423873*close[i+64]  -0.00340812*close[i+65]  -0.00255923*close[i+66]  -0.00170217*close[i+67]  
         -0.00085902*close[i+68]  -0.00004113*close[i+69]  +0.00073700*close[i+70]  +0.00146422*close[i+71]  
         +0.00213007*close[i+72]  +0.00272649*close[i+73]  +0.00324752*close[i+74]  +0.00368922*close[i+75]  
         +0.00405000*close[i+76]  +0.00433024*close[i+77]  +0.00453068*close[i+78]  +0.00465046*close[i+79]  
         +0.00469058*close[i+80]  +0.00466041*close[i+81]  +0.00457855*close[i+82]  +0.00442491*close[i+83]  
         +0.00423019*close[i+84]  +0.00399201*close[i+85]  +0.00372169*close[i+86]  +0.00342736*close[i+87]  
         +0.00311822*close[i+88]  +0.00280309*close[i+89]  +0.00249088*close[i+90]  +0.00219089*close[i+91]  
         +0.00191283*close[i+92]  +0.00166683*close[i+93]  +0.00146419*close[i+94]  +0.00131867*close[i+95]  
         +0.00124645*close[i+96]  +0.00126836*close[i+97]  -0.00401854*close[i+98];
   }

   //--- Step 2: Polynomial Regression setup (Degree m=2)
   int m = 2;
   int p = InpBarsBack;
   int nn = m + 1;
   int shift = 0; // Center calculation starting at the most recent bar (bar 0)

   double sx[6]; 
   double b[4];  
   double x[4];  
   double ai[4][4]; 

   ArrayInitialize(sx, 0.0);
   ArrayInitialize(b, 0.0);
   ArrayInitialize(x, 0.0);
   
   sx[1] = p + 1;

   for(int mi = 1; mi <= nn * 2 - 2; mi++) {
      double sum = 0;
      for(int n = shift; n <= shift + p; n++) {
         sum += MathPow((double)n, (double)mi);
      }
      sx[mi + 1] = sum;
   }

   for(int mi = 1; mi <= nn; mi++) {
      double sum = 0;
      for(int n = shift; n <= shift + p; n++) {
         if(mi == 1) sum += RSTLbuffertmp[n];
         else sum += RSTLbuffertmp[n] * MathPow((double)n, (double)(mi - 1));
      }
      b[mi] = sum;
   }

   for(int jj = 1; jj <= nn; jj++) {
      for(int ii = 1; ii <= nn; ii++) {
         int kk = ii + jj - 1;
         ai[ii][jj] = sx[kk];
      }
   }

   //--- Gaussian Elimination
   for(int kk = 1; kk <= nn - 1; kk++) {
      int ll = 0; 
      double mm = 0;
      for(int ii = kk; ii <= nn; ii++) {
         if(MathAbs(ai[ii][kk]) > mm) {
            mm = MathAbs(ai[ii][kk]);
            ll = ii;
         }
      }
      
      if(ll == 0) return(rates_total); // Matrix Singular
      
      if(ll != kk) {
         for(int jj = 1; jj <= nn; jj++) {
            double tt = ai[kk][jj];
            ai[kk][jj] = ai[ll][jj];
            ai[ll][jj] = tt;
         }
         double tt = b[kk]; b[kk] = b[ll]; b[ll] = tt;
      }
      
      for(int ii = kk + 1; ii <= nn; ii++) {
         double qq = ai[ii][kk] / ai[kk][kk];
         for(int jj = 1; jj <= nn; jj++) {
            if(jj == kk) ai[ii][jj] = 0;
            else ai[ii][jj] = ai[ii][jj] - qq * ai[kk][jj];
         }
         b[ii] = b[ii] - qq * b[kk];
      }
   }

   x[nn] = b[nn] / ai[nn][nn];

   for(int ii = nn - 1; ii >= 1; ii--) {
      double tt = 0;
      for(int jj = 1; jj <= nn - ii; jj++) {
         tt += ai[ii][ii + jj] * x[ii + jj];
         x[ii] = (1.0 / ai[ii][ii]) * (b[ii] - tt);
      }
   }

   //--- Step 3: Populate regression channel values
   for(int n = shift; n <= shift + p; n++) {
      double sum = 0;
      for(int kk = 1; kk <= m; kk++) {
         sum += x[kk + 1] * MathPow((double)n, (double)kk);
      }
      fx2Buffer[n] = x[1] + sum;
   }

   //--- Step 4: Root Mean Square (RMS Error) Band Calculations
   double sq = 0;
   for(int n = shift; n <= shift + p; n++) {
      sq += MathPow((RSTLbuffertmp[n] - fx2Buffer[n]), 2);
   }
   sq = MathSqrt(sq / (p + 1)) * InpKStd;

   //--- Standard Deviation Calculation (Simulating native stddev)
   double mean = 0;
   for(int n = shift; n <= shift + p; n++) {
      mean += RSTLbuffertmp[n];
   }
   mean /= (p + 1);

   double stdDevVal = 0;
   for(int n = shift; n <= shift + p; n++) {
      stdDevVal += MathPow(RSTLbuffertmp[n] - mean, 2);
   }
   stdDevVal = MathSqrt(stdDevVal / (p + 1)) * InpKStd;

   //--- Assign values to our target plot buffers
   for(int n = shift; n <= shift + p; n++) {
      sqh2Buffer[n]  = fx2Buffer[n] + sq;
      sql2Buffer[n]  = fx2Buffer[n] - sq;
      stdh2Buffer[n] = fx2Buffer[n] + stdDevVal;
      stdl2Buffer[n] = fx2Buffer[n] - stdDevVal;
   }

   return(rates_total);
}