//+------------------------------------------------------------------+
//|                                         Jurik Moving Average.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_chart_window
#property indicator_buffers 12
#property indicator_plots   1
//--- plot JMA
#property indicator_label1  "JMA"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  clrMediumTurquoise,clrMediumVioletRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  9
//--- 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_jma_period       = 21;       // Period (min val: 1)
input int               inp_jma_phase        = 100;        // Phase (range between -100 and 100)
input int               inp_jma_shift        = 0;        // Shift
input ENUM_PRICE        inp_rate             = pr_C;     // Applied Price
//--- indicator buffers
double                  JMABuffer[];
double                  JMAColors[];
double                  rate[];
double                  temp1[];
double                  temp2[];
double                  temp3[];
double                  temp4[];
double                  temp5[];
double                  temp6[];
double                  temp7[];
double                  temp8[];
double                  temp9[];
//--- indicator variables
int                     jma_period;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check input parameters
   jma_period = inp_jma_period < 1 ? 1 : inp_jma_period;
//--- indicator buffers mapping
   SetIndexBuffer(0, JMABuffer, INDICATOR_DATA);
   SetIndexBuffer(1, JMAColors, INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, rate, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, temp1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, temp2, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, temp3, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, temp4, INDICATOR_CALCULATIONS);
   SetIndexBuffer(7, temp5, INDICATOR_CALCULATIONS);
   SetIndexBuffer(8, temp6, INDICATOR_CALCULATIONS);
   SetIndexBuffer(9, temp7, INDICATOR_CALCULATIONS);
   SetIndexBuffer(10, temp8, INDICATOR_CALCULATIONS);
   SetIndexBuffer(11, temp9, INDICATOR_CALCULATIONS);
//--- set indicator accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);
//--- 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 = "JMA (" + IntegerToString(jma_period) + ", " + IntegerToString(inp_jma_phase) + ", " + IntegerToString(inp_jma_shift) + ", " + price_name + ")";
   IndicatorSetString(INDICATOR_SHORTNAME, short_name);
//--- indicator bar shift
   PlotIndexSetInteger(0, PLOT_SHIFT, inp_jma_shift);
//--- sets drawing lines to empty value
   PlotIndexSetDouble(0, 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 < jma_period)
      return(0);
//--- populate rate buffer
   PopRate(rates_total, prev_calculated, inp_rate, open, high, low, close, rate);
//--- populate JMA buffer
   JMA(rates_total, prev_calculated, jma_period, inp_jma_phase, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, rate, JMABuffer);
//--- calculate start position
   int bar;
   if(prev_calculated == 0)
      bar = 1;
   else
      bar = prev_calculated - 1;
//--- main loop
   for(int i = bar; i < rates_total && !_StopFlag; i++)
     {
      JMAColors[i] = JMABuffer[i] > JMABuffer[i - 1] ? 0.0 : JMABuffer[i] < JMABuffer[i - 1] ? 1.0 : JMAColors[i - 1];
     }
//--- 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: 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);
  }
//+------------------------------------------------------------------+
