//+------------------------------------------------------------------+
//|                             AdaptiveSuperSmoother_Oscillator.mq4 |
//|                                   Copyright 2026, John F. Ehlers |
//|        https://forex-station.com/post1295586791.html#p1295586791 |
//|                                   PineCoders / Converted to MQL4 |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2026, John F. Ehlers / PineCoders"
#property link        "https://forex-station.com/post1295586791.html#p1295586791"
#property version     "1.00"
#property description "TASC Issue: September 2026"
#property description "Article: Improved Filter Performance - Adaptive SuperSmoother Oscillator"
#property description "Article By: John F. Ehlers"
#property strict

//--- Place in separate indicator subwindow
#property indicator_separate_window
#property indicator_buffers 4

//--- Plot 1: Adaptive SS Oscillator (Blue line)
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2
#property indicator_style1  STYLE_SOLID

//--- Plot 2: SS Base (Zero Reference Line, Red)
#property indicator_color2  clrRed
#property indicator_width2  1
#property indicator_style2  STYLE_SOLID

//--- Horizontal level at zero
#property indicator_level1      0.0
#property indicator_levelcolor  clrDarkGray
#property indicator_levelstyle  STYLE_DOT

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input string             InpSectionFilter = "--- Filter Settings ---"; // [Filter Parameters]
input ENUM_APPLIED_PRICE InpAppliedPrice  = PRICE_CLOSE;              // Price Source
input int                InpBasePeriod    = 20;                       // Base period (>= 2)
input int                InpRMSLength     = 81;                       // RMS length (>= 2)

//+------------------------------------------------------------------+
//| Indicator Buffers                                                |
//+------------------------------------------------------------------+
double BufferOscillator[]; // Plotted (Buffer 0): ssAdaptive - ssBase
double BufferZero[];       // Plotted (Buffer 1): 0 baseline (SS Base)
double BufferAdaptive[];   // Calculation (Buffer 2): Adaptive SuperSmoother
double BufferBase[];       // Calculation (Buffer 3): Base SuperSmoother

//+------------------------------------------------------------------+
//| Global Precalculated Filter Coefficients                         |
//+------------------------------------------------------------------+
double g_w_base;
double g_q_base;
double g_c1_base;
double g_c2_base;
double g_a0_base;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- Validate input parameters
   if(InpBasePeriod < 2)
   {
      PrintFormat("AdaptiveSuperSmoother_Oscillator: Base period must be >= 2. Received: %d", InpBasePeriod);
      return(INIT_PARAMETERS_INCORRECT);
   }
   if(InpRMSLength < 2)
   {
      PrintFormat("AdaptiveSuperSmoother_Oscillator: RMS length must be >= 2. Received: %d", InpRMSLength);
      return(INIT_PARAMETERS_INCORRECT);
   }

   //--- Allocate 4 buffers (2 plotted, 2 calculation buffers)
   IndicatorBuffers(4);

   //--- Buffer 0: Adaptive SS Oscillator
   SetIndexBuffer(0, BufferOscillator);
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrDodgerBlue);
   SetIndexLabel(0, "Adaptive SS Oscillator");

   //--- Buffer 1: SS Base (Zero Baseline)
   SetIndexBuffer(1, BufferZero);
   SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 1, clrRed);
   SetIndexLabel(1, "SS Base");

   //--- Buffer 2: Adaptive SuperSmoother
   SetIndexBuffer(2, BufferAdaptive);
   SetIndexStyle(2, DRAW_NONE);
   SetIndexLabel(2, "Adaptive SuperSmoother");

   //--- Buffer 3: Base SuperSmoother
   SetIndexBuffer(3, BufferBase);
   SetIndexStyle(3, DRAW_NONE);
   SetIndexLabel(3, "SuperSmoother (Base)");

   //--- Precision & Short Name
   IndicatorDigits(Digits);
   string short_name = StringFormat("Adaptive SS Osc (%d, %d)", InpBasePeriod, InpRMSLength);
   IndicatorShortName(short_name);

   //--- Precalculate fixed coefficients for base SuperSmoother
   g_w_base  = 1.414 * M_PI / (double)InpBasePeriod;
   g_q_base  = MathExp(-g_w_base);
   g_c1_base = 2.0 * g_q_base * MathCos(g_w_base);
   g_c2_base = g_q_base * g_q_base;
   g_a0_base = (1.0 - g_c1_base + g_c2_base) / 2.0;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Helper: Extract selected applied price                           |
//+------------------------------------------------------------------+
inline double GetAppliedPrice(const int index,
                              const double &open[],
                              const double &high[],
                              const double &low[],
                              const double &close[])
{
   switch(InpAppliedPrice)
   {
      case PRICE_OPEN:     return open[index];
      case PRICE_HIGH:     return high[index];
      case PRICE_LOW:      return low[index];
      case PRICE_MEDIAN:   return (high[index] + low[index]) * 0.5;
      case PRICE_TYPICAL:  return (high[index] + low[index] + close[index]) / 3.0;
      case PRICE_WEIGHTED: return (high[index] + low[index] + 2.0 * close[index]) * 0.25;
      case PRICE_CLOSE:
      default:             return close[index];
   }
}

//+------------------------------------------------------------------+
//| 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 < 5)
      return(0);

   //--- Determine calculation limit (timeseries order: oldest to newest)
   int limit;
   if(prev_calculated == 0)
      limit = rates_total;
   else
      limit = rates_total - prev_calculated + 1;

   if(limit > rates_total)
      limit = rates_total;

   //--- Main loop: iterates down from oldest uncalculated bar down to 0 (current bar)
   for(int i = limit - 1; i >= 0; i--)
   {
      double src = GetAppliedPrice(i, open, high, low, close);

      //--- 1. Base SuperSmoother Calculation
      // (The oldest 4 bars correspond to i >= rates_total - 4)
      if(i >= rates_total - 4)
      {
         BufferBase[i] = src;
      }
      else
      {
         double src_prev = GetAppliedPrice(i + 1, open, high, low, close);
         BufferBase[i] = g_a0_base * (src + src_prev)
                       + g_c1_base * BufferBase[i + 1]
                       - g_c2_base * BufferBase[i + 2];
      }

      //--- 2. Calculate Adaptive Period via Rate of Change (ROC) and RMS
      int aPeriod = InpBasePeriod;
      if(i <= rates_total - InpRMSLength)
      {
         // Calculate Root Mean Square (RMS) of 1-bar ROC over InpRMSLength bars
         double sum = 0.0;
         for(int k = 0; k < InpRMSLength; k++)
         {
            int idx = i + k;
            double r = (idx < rates_total - 1) ? (BufferBase[idx] - BufferBase[idx + 1]) : 0.0;
            sum += r * r;
         }
         double rocRMS = MathSqrt(sum / (double)InpRMSLength);
         double roc1   = BufferBase[i] - BufferBase[i + 1];
         double roc    = 0.0;

         if(rocRMS > 0.0 && MathIsValidNumber(rocRMS))
         {
            double rVal = MathAbs(roc1 / rocRMS);
            if(MathIsValidNumber(rVal))
               roc = MathMin(rVal, 2.0);
         }

         double factor = (1.0 - 0.5 * roc) * (1.0 - 0.5 * roc);
         aPeriod = (int)MathMax((int)(InpBasePeriod * factor), 2);
      }

      //--- 3. Adaptive SuperSmoother Calculation
      double w_a  = 1.414 * M_PI / (double)aPeriod;
      double q_a  = MathExp(-w_a);
      double c1_a = 2.0 * q_a * MathCos(w_a);
      double c2_a = q_a * q_a;
      double a0_a = (1.0 - c1_a + c2_a) / 2.0;

      if(i >= rates_total - 4)
      {
         BufferAdaptive[i] = src;
      }
      else
      {
         double src_prev = GetAppliedPrice(i + 1, open, high, low, close);
         BufferAdaptive[i] = a0_a * (src + src_prev)
                           + c1_a * BufferAdaptive[i + 1]
                           - c2_a * BufferAdaptive[i + 2];
      }

      //--- 4. Oscillator (ssAdaptive - ssBase) and Zero Baseline
      BufferOscillator[i] = BufferAdaptive[i] - BufferBase[i];
      BufferZero[i]       = 0.0;
   }

   return(rates_total);
}
//+------------------------------------------------------------------+
