//+------------------------------------------------------------------+
//|                                        RSI+JMA Filter+Signal.mq5 |
//|                                        Copyright @ 2022, Centaur |
//|      forex-station.com/memberlist.php?mode=viewprofile&u=4948703 |
//+------------------------------------------------------------------+
#property copyright "Copyright @ 2022, Centaur"
#property link      "forex-station.com/memberlist.php?mode=viewprofile&u=4948703"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 17
#property indicator_plots   3
//--- plot RSI
#property indicator_label1  "RSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot JMA Filter
#property indicator_label2  "JMA Filter"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLime
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Signal
#property indicator_label3  "Signal"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrTomato
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- enumerations
enum ENUM_PRICE
  {
   pr_O,    // Open O
   pr_H,    // High H
   pr_L,    // Low L
   pr_C,    // Close C
   pr_M,    // Median (H+L)/2
   pr_T,    // Typical (H+L+C)/3
   pr_W,    // Weighted (H+L+C+C)/4
   pr_A,    // Average (O+H+L+C)/4
   pr_AB,   // Average Body (O+C)/2
   pr_HAO,  // Heikin-Ashi Open HAO
   pr_HAH,  // Heikin-Ashi High HAH
   pr_HAL,  // Heikin-Ashi Low HAL
   pr_HAC,  // Heikin-Ashi Close HAC
   pr_HAM,  // Heikin-Ashi Median (HAH+HAL)/2
   pr_HAT,  // Heikin-Ashi Typical (HAH+HAL+HAC)/3
   pr_HAW,  // Heikin-Ashi Weighted (HAH+HAL+HAC+HAC)/4
   pr_HAA,  // Heikin-Ashi Average (HAO+HAH+HAL+HAC)/4
   pr_HAAB  // Heikin-Ashi Average Body (HAO+HAC)/2
  };
//--- input parameters
input int            inp_rsi_length          = 14;    // RSI Period (min val: 1)
input ENUM_PRICE     inp_rate                = pr_C;  // RSI Applied Price
input bool           inp_show_rsi            = true;  // Show RSI ?
input int            inp_jma_filter_length   = 8;     // JMA Filter Period (min val: 1)
input int            inp_jma_filter_phase    = -100;  // JMA Filter Phase (range between -100 and 100)
input bool           inp_show_signal         = true;  // Show Signal Line ?
//--- indicator buffers
double               RSIBuffer[];
double               JMA_FilterBuffer[];
double               SignalBuffer[];
double               rate[];
double               temp11[];
double               temp12[];
double               temp13[];
double               temp14[];
double               temp21[];
double               temp22[];
double               temp23[];
double               temp24[];
double               temp25[];
double               temp26[];
double               temp27[];
double               temp28[];
double               temp29[];
//--- indicator variables
int                  rsi_length;
int                  jma_filter_length;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check input parameters
   rsi_length = inp_rsi_length < 1 ? 1 : inp_rsi_length;
   jma_filter_length = inp_jma_filter_length < 1 ? 1 : inp_jma_filter_length;
//--- indicator buffers mapping
   SetIndexBuffer(0, RSIBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, JMA_FilterBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, SignalBuffer, INDICATOR_DATA);
   SetIndexBuffer(3, rate, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, temp11, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, temp12, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, temp13, INDICATOR_CALCULATIONS);
   SetIndexBuffer(7, temp14, INDICATOR_CALCULATIONS);
   SetIndexBuffer(8, temp21, INDICATOR_CALCULATIONS);
   SetIndexBuffer(9, temp22, INDICATOR_CALCULATIONS);
   SetIndexBuffer(10, temp23, INDICATOR_CALCULATIONS);
   SetIndexBuffer(11, temp24, INDICATOR_CALCULATIONS);
   SetIndexBuffer(12, temp25, INDICATOR_CALCULATIONS);
   SetIndexBuffer(13, temp26, INDICATOR_CALCULATIONS);
   SetIndexBuffer(14, temp27, INDICATOR_CALCULATIONS);
   SetIndexBuffer(15, temp28, INDICATOR_CALCULATIONS);
   SetIndexBuffer(16, temp29, INDICATOR_CALCULATIONS);
//--- set indicator accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, 2);
//--- set indicator name display
   string price_name = inp_rate == pr_O ? "Open" : inp_rate == pr_H ? "High" : inp_rate == pr_L ? "Low" : inp_rate == pr_C ? "Close" : inp_rate == pr_M ? "Median" : inp_rate == pr_T ? "Typical" : inp_rate == pr_W ? "Weighted" : inp_rate == pr_A ? "Average" : inp_rate == pr_AB ? "Average Body" : inp_rate == pr_HAO ? "HA Open" : inp_rate == pr_HAH ? "HA High" : inp_rate == pr_HAL ? "HA Low" : inp_rate == pr_HAC ? "HA Close" : inp_rate == pr_HAM ? "HA Median" : inp_rate == pr_HAT ? "HA Typical" : inp_rate == pr_HAW ? "HA Weighted" : inp_rate == pr_HAA ? "HA Average" : inp_rate == pr_HAAB ? "HA Average Body" : " ";
   string short_name = "RSI + JMA FILTER + SIGNAL (" + IntegerToString(rsi_length) + ", " + price_name + ", " + IntegerToString(jma_filter_length) + ", " + IntegerToString(inp_jma_filter_phase) + ")";
   IndicatorSetString(INDICATOR_SHORTNAME, short_name);
//--- sets drawing lines to empty value
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- initialization succeeded
   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 period
   if(rates_total < rsi_length + jma_filter_length)
      return(0);
//--- populate rate buffer
   PopRate(rates_total, prev_calculated, inp_rate, open, high, low, close, rate);
//--- populate RSI buffer
   RSI(rates_total, prev_calculated, rsi_length, temp11, temp12, temp13, temp14, rate, RSIBuffer);
//--- populate JMA Filter buffer
   JMA(rates_total, prev_calculated, jma_filter_length, inp_jma_filter_phase, temp21, temp22, temp23, temp24, temp25, temp26, temp27, temp28, temp29, RSIBuffer, JMA_FilterBuffer);
//--- calculate start position
   int bar;
   if(prev_calculated == 0)
      bar = 0;
   else
      bar = prev_calculated - 1;
//--- main loop
   for(int i = bar; i < rates_total && !_StopFlag; i++)
     {
      if(i < rsi_length + jma_filter_length)
        {
         RSIBuffer[i] = EMPTY_VALUE;
         JMA_FilterBuffer[i] = EMPTY_VALUE;
         SignalBuffer[i] = EMPTY_VALUE;
        }
      else
        {
         RSIBuffer[i] = inp_show_rsi == false ? EMPTY_VALUE : RSIBuffer[i];
         SignalBuffer[i] = inp_show_signal == false ? EMPTY_VALUE : (JMA_FilterBuffer[i] + 2 * JMA_FilterBuffer[i - 1] + 2 * JMA_FilterBuffer[i - 2] + JMA_FilterBuffer[i - 3]) / 6;
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Function: Populate price buffer (PopRate)                        |
//+------------------------------------------------------------------+
int PopRate(const int rates_total, const int prev_calculated, const ENUM_PRICE app_price, const double &open_buf[], const double &high_buf[], const double &low_buf[], const double &close_buf[], double &out_buf[])
  {
//--- calculate start position
   int bar;
   if(prev_calculated == 0)
      bar = 0;
   else
      bar = prev_calculated - 1;
//--- main loop
   for(int i = bar; i < rates_total && !_StopFlag; i++)
     {
      if(i < 1)
         out_buf[i] = close_buf[i];
      if(i >= 1)
        {
         switch(app_price)
           {
            case pr_O:
               out_buf[i] = open_buf[i];
               break;
            case pr_H:
               out_buf[i] = high_buf[i];
               break;
            case pr_L:
               out_buf[i] = low_buf[i];
               break;
            case pr_C:
               out_buf[i] = close_buf[i];
               break;
            case pr_M:
               out_buf[i] = (high_buf[i] + low_buf[i]) / 2;
               break;
            case pr_T:
               out_buf[i] = (high_buf[i] + low_buf[i] + close_buf[i]) / 3;
               break;
            case pr_W:
               out_buf[i] = (high_buf[i] + low_buf[i] + close_buf[i] + close_buf[i]) / 4;
               break;
            case pr_A:
               out_buf[i] = (open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4;
               break;
            case pr_AB:
               out_buf[i] = (open_buf[i] + close_buf[i]) / 2;
               break;
            case pr_HAO:
               out_buf[i] = (open_buf[i - 1] + close_buf[i - 1]) / 2;
               break;
            case pr_HAH:
               out_buf[i] = fmax(high_buf[i], fmax(open_buf[i], close_buf[i]));
               break;
            case pr_HAL:
               out_buf[i] = fmin(low_buf[i], fmin(open_buf[i], close_buf[i]));
               break;
            case pr_HAC:
               out_buf[i] = (open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4;
               break;
            case pr_HAM:
               out_buf[i] = (fmax(high_buf[i], fmax(open_buf[i], close_buf[i])) + fmin(low_buf[i], fmin(open_buf[i], close_buf[i]))) / 2;
               break;
            case pr_HAT:
               out_buf[i] = (fmax(high_buf[i], fmax(open_buf[i], close_buf[i])) + fmin(low_buf[i], fmin(open_buf[i], close_buf[i])) + ((open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4)) / 3;
               break;
            case pr_HAW:
               out_buf[i] = (fmax(high_buf[i], fmax(open_buf[i], close_buf[i])) + fmin(low_buf[i], fmin(open_buf[i], close_buf[i])) + ((open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4) + ((open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4)) / 4;
               break;
            case pr_HAA:
               out_buf[i] = (((open_buf[i - 1] + close_buf[i - 1]) / 2) + fmax(high_buf[i], fmax(open_buf[i], close_buf[i])) + fmin(low_buf[i], fmin(open_buf[i], close_buf[i])) + ((open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4)) / 4;
               break;
            case pr_HAAB:
               out_buf[i] = (((open_buf[i - 1] + close_buf[i - 1]) / 2) + ((open_buf[i] + high_buf[i] + low_buf[i] + close_buf[i]) / 4)) / 2;
               break;
           }
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Function: Relative Strength Index (RSI)                          |
//+------------------------------------------------------------------+
int RSI(const int rates_total, const int prev_calculated, const int length, double &gain[], double &loss[], double &avg_gain[], double &avg_loss[], const double &in_buf[], double &out_buf[])
  {
//--- check period
   if(rates_total < length)
      return(0);
//--- calculate start position
   int bar;
   if(prev_calculated == 0)
      bar = 0;
   else
      bar = prev_calculated - 1;
//--- main loop
   for(int i = bar; i < rates_total && !_StopFlag; i++)
     {
      gain[i] = i < 1 ? 0.0 : in_buf[i] - in_buf[i - 1] > 0.0 ? in_buf[i] - in_buf[i - 1] : 0.0;
      loss[i] = i < 1 ? 0.0 : in_buf[i] - in_buf[i - 1] < 0.0 ? fabs(in_buf[i] - in_buf[i - 1]) : 0.0;
      double RS = 0.0;
      if(i < length)
        {
         avg_gain[i] = 0.0;
         avg_loss[i] = 0.0;
         out_buf[i] = 0.0;
        }
      else
         if(i == length)
           {
            double sum_gain = 0.0, sum_loss = 0.0;
            for(int m = 0; m < length; m++)
              {
               sum_gain += gain[i - m];
               sum_loss += loss[i - m];
              }
            avg_gain[i] = sum_gain / length;
            avg_loss[i] = sum_loss / length;
            RS = avg_gain[i] / avg_loss[i];
            out_buf[i] = avg_loss[i] == 0.0 ? 100.0 : 100.0 - (100.0 / (1.0 + RS));
           }
         else
           {
            avg_gain[i] = (avg_gain[i - 1] * (length - 1) + gain[i]) / length;
            avg_loss[i] = (avg_loss[i - 1] * (length - 1) + loss[i]) / length;
            RS = avg_gain[i] / avg_loss[i];
            out_buf[i] = avg_loss[i] == 0.0 ? 100.0 : 100.0 - (100.0 / (1.0 + RS));
           }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Function: Jurik Moving Average (JMA)                             | by Mark Jurik, code converted from tradingview, gorx1: https://in.tradingview.com/v/gwzRz6tI/
//+------------------------------------------------------------------+
int JMA(const int rates_total, const int prev_calculated, const int length, const int phase, double &lower_band[], double &upper_band[], double &vola[], double &vola_sum[], double &avg_vola[], double &ma1[], double &det0[], double &det1[], double &jma[], const double &in_buf[], double &out_buf[])
  {
//--- check period
   if(rates_total < 10)
      return(0);
//--- calculate start position
   int bar;
   if(prev_calculated == 0)
      bar = 0;
   else
      bar = prev_calculated - 1;
//--- main loop
   for(int i = bar; i < rates_total && !_StopFlag; i++)
     {
      if(i < 10)
        {
         out_buf[i] = in_buf[i];
         lower_band[i] = in_buf[i];
         upper_band[i] = in_buf[i];
         vola[i] = 0.0;
         vola_sum[i] = 0.0;
         avg_vola[i] = 0.0;
         ma1[i] = 0.0;
         det0[i] = 0.0;
         jma[i] = 0.0;
         det1[i] = 0.0;
        }
      if(i >= 10)
        {
         double del1 = fabs(in_buf[i] - upper_band[i - 1]), del2 = fabs(in_buf[i] - lower_band[i - 1]);
         vola[i] = del1 == del2 ? 0 : fmax(del1, del2);
         vola_sum[i] = vola_sum[i - 1] + 0.1 * (vola[i] - vola[i - 10]);
         int avg_len = 65, y = i + 1;
         if(y <= avg_len + 1)
            avg_vola[i] = avg_vola[i - 1] + 2.0 * (vola_sum[i] - avg_vola[i - 1]) / (avg_len + 1);
         else
           {
            double sma = 0.0;
            for(int k = 0; k < avg_len; k++)
               sma += vola_sum[i - k];
            sma /= avg_len;
            avg_vola[i] = sma;
           }
         double len  = 0.5 * (length - 1), len1 = fmax(log(sqrt(len)) / log(2) + 2, 0.0), pow1 = fmax(len1 - 2, 0.5), r_vola = avg_vola[i] > 0.0 ? vola[i] / avg_vola[i] : 0.0;
         if(r_vola > pow(len1, 1 / pow1))
            r_vola = pow(len1, 1 / pow1);
         else
            if(r_vola < 1.0)
               r_vola = 1.0;
            else
               r_vola = r_vola;
         double pow2 = pow(r_vola, pow1), len2 = sqrt(len) * len1, bet = len2 / (len2 + 1), kv = pow(bet, sqrt(pow2));
         lower_band[i] = y == 1 ? in_buf[i] : del2 < 0.0 ? in_buf[i] : in_buf[i] - kv * del2;
         upper_band[i] = y == 1 ? in_buf[i] : del1 < 0.0 ? in_buf[i] : in_buf[i] + kv * del1;
         double beta = 0.45 * (len - 1) / (0.45 * (len - 1) + 2), pr = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100 + 1.5, alpha = pow(beta, pow2);
         ma1[i] = (1 - alpha) * in_buf[i] + alpha * ma1[i - 1];
         det0[i] = (in_buf[i] - ma1[i]) * (1 - beta) + beta * det0[i - 1];
         double ma2 = ma1[i] + pr * det0[i];
         det1[i] = (ma2 - jma[i - 1]) * pow((1 - alpha), 2) + pow(alpha, 2) * det1[i - 1];
         jma[i] = jma[i - 1] + det1[i];
         out_buf[i] = i <= 2 * length ? in_buf[i] : length <= 3 ? in_buf[i] : jma[i];
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
