//+------------------------------------------------------------------+
//|                                             StochasticRSI.mq5   |
//|                                                                  |
//| Stochastic RSI                                                   |
//|                                                                  |
//| Algorithm taken from book                                        |
//|     "Cybernetics Analysis for Stock and Futures"                 |
//| by John F. Ehlers                                                |
//|                                                                  |
//|                                          contact@mqlsoft.com     |
//|                                      http://www.mqlsoft.com/     |
//+------------------------------------------------------------------+
//  Converted to MQL5 from MQL4 original
//+------------------------------------------------------------------+
#property copyright "Coded by Witold Wozniak"
#property link      "www.mqlsoft.com"
#property version   "1.00"

#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   2

#property indicator_label1  "Stochastic RSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
#property indicator_width1  1

#property indicator_label2  "Trigger"
#property indicator_type2   DRAW_LINE
#property indicator_color2  Blue
#property indicator_width2  1

#property indicator_level1  0
#property indicator_minimum -1
#property indicator_maximum  1

//---- input parameters
input int RSILength  = 8;
input int StocLength = 8;
input int WMALength  = 8;

//---- plot buffers
double StocRSI[];
double Trigger[];

//---- calculation buffer (hidden)
double Value3[];

//---- RSI indicator handle
int hRSI = INVALID_HANDLE;

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Plot buffers
   SetIndexBuffer(0, StocRSI, INDICATOR_DATA);
   PlotIndexSetString(0, PLOT_LABEL, "Stochastic RSI");
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, RSILength);

   SetIndexBuffer(1, Trigger, INDICATOR_DATA);
   PlotIndexSetString(1, PLOT_LABEL, "Trigger");
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, RSILength);

   // Hidden calculation buffer
   SetIndexBuffer(2, Value3, INDICATOR_CALCULATIONS);

   IndicatorSetString(INDICATOR_SHORTNAME,
      "Stochastic RSI [" + IntegerToString(RSILength) + ", " +
                           IntegerToString(StocLength) + ", " +
                           IntegerToString(WMALength)  + "]");

   // Create RSI indicator handle
   hRSI = iRSI(_Symbol, PERIOD_CURRENT, RSILength, PRICE_CLOSE);
   if(hRSI == INVALID_HANDLE)
   {
      Print("StochasticRSI: failed to create RSI handle, error ", GetLastError());
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(hRSI != INVALID_HANDLE)
      IndicatorRelease(hRSI);
}

//+------------------------------------------------------------------+
//| Iteration                                                        |
//+------------------------------------------------------------------+
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 drawBegin = RSILength;
   if(rates_total <= drawBegin) return(0);

   // Copy RSI values into a local array (series order: index 0 = most recent)
   double rsiArr[];
   ArraySetAsSeries(rsiArr, true);
   int copied = CopyBuffer(hRSI, 0, 0, rates_total, rsiArr);
   if(copied <= 0) return(0);

   // Set all working arrays as series
   ArraySetAsSeries(StocRSI, true);
   ArraySetAsSeries(Trigger, true);
   ArraySetAsSeries(Value3,  true);

   // Determine how many bars to (re)calculate
   int limit;
   if(prev_calculated <= 0)
      limit = rates_total - drawBegin - 1;
   else
      limit = rates_total - prev_calculated + 1;

   if(limit <= 0) return(rates_total);
   if(limit > rates_total - drawBegin - 1)
      limit = rates_total - drawBegin - 1;

   // Pass 1: Stochastic of RSI -> Value3[]
   for(int s = limit; s >= 0; s--)
   {
      if(s + StocLength - 1 >= copied) continue;   // not enough RSI data yet

      double rsi = rsiArr[s];
      double hh  = rsi, ll = rsi;
      for(int i = 1; i < StocLength; i++)
      {
         double tmp = rsiArr[s + i];
         hh = MathMax(hh, tmp);
         ll = MathMin(ll, tmp);
      }
      double val1 = rsi - ll;
      double val2 = hh  - ll;
      Value3[s] = (val2 != 0.0) ? val1 / val2 : 0.0;
   }

   // Pass 2: WMA of Value3, scaled to [-1, 1]; Trigger = previous bar's StocRSI
   for(int s = limit - 1; s >= 0; s--)
   {
      StocRSI[s] = 2.0 * (CalcLWMA(Value3, s, WMALength, rates_total) - 0.5);
      Trigger[s] = StocRSI[s + 1];
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Linearly-weighted moving average at position pos over period     |
//| (series-indexed: pos 0 = newest, higher = older)                |
//+------------------------------------------------------------------+
double CalcLWMA(const double &arr[], int pos, int period, int total)
{
   double weightedSum = 0.0;
   double weightSum   = 0.0;
   for(int i = 0; i < period; i++)
   {
      int idx = pos + i;
      if(idx >= total) break;
      double w    = period - i;   // weight: period for newest, 1 for oldest
      weightedSum += arr[idx] * w;
      weightSum   += w;
   }
   if(weightSum == 0.0) return(0.0);
   return(weightedSum / weightSum);
}
//+------------------------------------------------------------------+