//+------------------------------------------------------------------+
//|                                      RangeSpreadRatioStable.mq4  |

//+------------------------------------------------------------------+
#property copyright "Forex-station.com"
#property link      "https://www.forex-station.com"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 clrTeal
#property indicator_color2 clrOrange
#property indicator_color3 clrGreen
#property indicator_color4 clrRed
#property indicator_width1 2
#property indicator_width2 2

// === Inputs ===
extern int SmoothLen = 5;          // Smoothing Length (EMA)
extern int SignalLen = 9;           // Signal Line Length (EMA)
extern double MinSpread = 0.0001;   // Minimum spread (point-based)
extern double MaxRatio = 10.0;      // Maximum allowed ratio value
extern bool ApplyNormalization = true; // Normalize extreme values

// === Buffers ===
double smoothedOscBuffer[];
double signalLineBuffer[];
double histUpBuffer[];
double histDownBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrTeal);
   SetIndexBuffer(0, smoothedOscBuffer);
   SetIndexLabel(0, "Smoothed Oscillator");
   
   SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, clrOrange);
   SetIndexBuffer(1, signalLineBuffer);
   SetIndexLabel(1, "Signal Line");
   
   SetIndexStyle(2, DRAW_HISTOGRAM, STYLE_SOLID, 1, clrGreen);
   SetIndexBuffer(2, histUpBuffer);
   SetIndexLabel(2, "Histogram Up");
   
   SetIndexStyle(3, DRAW_HISTOGRAM, STYLE_SOLID, 1, clrRed);
   SetIndexBuffer(3, histDownBuffer);
   SetIndexLabel(3, "Histogram Down");
   
   IndicatorShortName("Stable Range/Spread Ratio");
   IndicatorDigits(2);
   
   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[])
{
   int limit = rates_total - prev_calculated;
   if (prev_calculated > 0) limit++;
   
   for (int i = limit-1; i >= 0; i--)
   {
      // === Core Calculation with Safeguards ===
      double currentSpread = MathAbs(close[i] - open[i]);
      
      // Apply minimum spread constraint
      if (currentSpread < MinSpread) currentSpread = MinSpread;
      
      double rnge = high[i] - low[i];
      double osc = 0;
      
      // Calculate ratio with protection
      if (currentSpread > 0) 
      {
         osc = rnge / currentSpread;
         
         // Apply maximum value constraint
         if (ApplyNormalization && osc > MaxRatio)
         {
            osc = MaxRatio;
         }
      }
      
      // === Smoothing ===
      smoothedOscBuffer[i] = iEMA(osc, SmoothLen, i, rates_total, smoothedOscBuffer);
      signalLineBuffer[i] = iEMA(smoothedOscBuffer[i], SignalLen, i, rates_total, signalLineBuffer);
      
      // === Calculate Histogram ===
      double hist = smoothedOscBuffer[i] - signalLineBuffer[i];
      
      // Apply constraints to histogram if needed
      if (ApplyNormalization)
      {
         if (hist > MaxRatio) hist = MaxRatio;
         if (hist < -MaxRatio) hist = -MaxRatio;
      }
      
      // === Histogram Coloring ===
      if (hist >= 0)
      {
         histUpBuffer[i] = hist;
         histDownBuffer[i] = EMPTY_VALUE;
      }
      else
      {
         histUpBuffer[i] = EMPTY_VALUE;
         histDownBuffer[i] = hist;
      }
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Custom EMA function                                              |
//+------------------------------------------------------------------+
double iEMA(double value, int period, int i, int rates_total, double &buffer[])
{
   if (i >= rates_total - 1 || period <= 1)
      return value;
   
   double alpha = 2.0 / (1.0 + period);
   return buffer[i+1] + alpha * (value - buffer[i+1]);
}
//+------------------------------------------------------------------+