//+------------------------------------------------------------------+
//|                                                   Supertrend.mq5 |
//|                                        Copyright @ 2022, Centaur |
//|                           https://www.mql5.com/en/users/centaur/ |
//+------------------------------------------------------------------+
#property copyright "Copyright @ 2022, Centaur"
#property link      "https://www.mql5.com/en/users/centaur/"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 10
#property indicator_plots   4
//--- plot Up
#property indicator_label1  "Up"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot Down
#property indicator_label2  "Down"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrTomato
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2
//--- plot Buy Signal
#property indicator_label3  "Buy Signal"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrDodgerBlue
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- plot Sell Signal
#property indicator_label4  "Sell Signal"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrTomato
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1
//--- input parameters
input int                     inp_atr_period          = 10;                // ATR Period
input ENUM_APPLIED_PRICE      inp_applied_price       = PRICE_MEDIAN;      // Applied Price
input double                  inp_atr_multiplier      = 3.0;               // ATR Multiplier
input bool                    inp_change_atr          = true;              // Change ATR Calculation Method ?
input bool                    inp_show_signals        = true;              // Show Buy/Sell Signals ?
//--- indicator buffers
double                        UpBuffer[];
double                        DownBuffer[];
double                        BuySignalBuffer[];
double                        SellSignalBuffer[];
double                        TR[];
double                        ATR[];
double                        Price[];
double                        Up[];
double                        Down[];
double                        Trend[];
//--- indicator variables
int                           atr_period;
double                        atr_multiplier;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check input parameters
   atr_period = inp_atr_period < 2 ? 2 : inp_atr_period;
   atr_multiplier = inp_atr_multiplier < 0.1 ? 0.1 : NormalizeDouble(inp_atr_multiplier, 1);
//--- indicator buffers mapping
   SetIndexBuffer(0, UpBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, DownBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, BuySignalBuffer, INDICATOR_DATA);
   SetIndexBuffer(3, SellSignalBuffer, INDICATOR_DATA);
   SetIndexBuffer(4, TR, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, ATR, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, Price, INDICATOR_CALCULATIONS);
   SetIndexBuffer(7, Up, INDICATOR_CALCULATIONS);
   SetIndexBuffer(8, Down, INDICATOR_CALCULATIONS);
   SetIndexBuffer(9, Trend, INDICATOR_CALCULATIONS);
//--- set indicator accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
//--- set indicator name display
   string applied_price = inp_applied_price == PRICE_OPEN ? "Open" : inp_applied_price == PRICE_HIGH ? "High" : inp_applied_price == PRICE_LOW ? "Low" : inp_applied_price == PRICE_CLOSE ? "Close" : inp_applied_price == PRICE_MEDIAN ? "Median" : inp_applied_price == PRICE_TYPICAL ? "Typical" : inp_applied_price == PRICE_WEIGHTED ? "Weighted" : " ";
   string short_name = "Supertrend (" + IntegerToString(atr_period) + ", " + applied_price + ", " + DoubleToString(atr_multiplier, 1) + ", " + string(inp_change_atr) + ", " + string(inp_show_signals) + ")";
   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);
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- initialize buffers
   ArrayInitialize(UpBuffer, EMPTY_VALUE);
   ArrayInitialize(DownBuffer, EMPTY_VALUE);
   ArrayInitialize(BuySignalBuffer, EMPTY_VALUE);
   ArrayInitialize(SellSignalBuffer, EMPTY_VALUE);
   ArrayInitialize(TR, 0.0);
   ArrayInitialize(ATR, 0.0);
   ArrayInitialize(Price, 0.0);
   ArrayInitialize(Up, 0.0);
   ArrayInitialize(Down, 0.0);
   ArrayInitialize(Trend, 0.0);
//--- setting a code from the Wingdings charset as the property of PLOT_ARROW
   PlotIndexSetInteger(2, PLOT_ARROW, 225);
   PlotIndexSetInteger(3, PLOT_ARROW, 226);
//--- Set the vertical shift of arrows in pixels
   PlotIndexSetInteger(2, PLOT_ARROW_SHIFT, 10);
   PlotIndexSetInteger(3, PLOT_ARROW_SHIFT, -10);
//--- 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(atr_period <= 1 || atr_period > rates_total)
      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++)
     {
      Price[i] = PopPrice(i, inp_applied_price, open, high, low, close);
      Trend[i] = -1.0;
      if(i > atr_period)
        {
         TR[i] = fmax(high[i] - low[i], fmax(fabs(high[i] - close[i - 1]), fabs(low[i] - close[i - 1])));
         ATR[i] = inp_change_atr == true ? RMA(i, atr_period, ATR[i - 1], TR) : SMA(i, atr_period, TR);
         Up[i] = Price[i] - (atr_multiplier * ATR[i]);
         Up[i] = close[i - 1] > Up[i - 1] ? fmax(Up[i], Up[i - 1]) : Up[i];
         Down[i] = Price[i] + (atr_multiplier * ATR[i]);
         Down[i] = close[i - 1] < Down[i - 1] ? fmin(Down[i], Down[i - 1]) : Down[i];
         Trend[i] = Trend[i - 1] == -1.0 && close[i] > Down[i - 1] ? 1.0 : Trend[i - 1] == 1.0 && close[i] < Up[i - 1] ? -1.0 : Trend[i - 1];
         UpBuffer[i] = Trend[i] == 1.0 ? Up[i] : EMPTY_VALUE;
         DownBuffer[i] = Trend[i] == -1.0 ? Down[i] : EMPTY_VALUE;
         BuySignalBuffer[i] = inp_show_signals == false ? EMPTY_VALUE : Trend[i] == 1.0 && Trend[i - 1] == -1.0 ? UpBuffer[i] : EMPTY_VALUE;
         SellSignalBuffer[i] = inp_show_signals == false ? EMPTY_VALUE : Trend[i] == -1.0 && Trend[i - 1] == 1.0 ? DownBuffer[i] : EMPTY_VALUE;
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Function: Populate Price Data from ENUM_APPLIED_PRICE            |
//+------------------------------------------------------------------+
double PopPrice(const int _position, const ENUM_APPLIED_PRICE _price, const double &_open[], const double &_high[], const double &_low[], const double &_close[])
  {
   switch(_price)
     {
      case PRICE_OPEN:
         return(_open[_position]);
         break;
      case PRICE_HIGH:
         return(_high[_position]);
         break;
      case PRICE_LOW:
         return(_low[_position]);
         break;
      case PRICE_CLOSE:
         return(_close[_position]);
         break;
      case PRICE_MEDIAN:
         return((_high[_position] + _low[_position]) / 2);
         break;
      case PRICE_TYPICAL:
         return((_high[_position] + _low[_position] + _close[_position]) / 3);
         break;
      case PRICE_WEIGHTED:
         return((_high[_position] + _low[_position] + _close[_position] + _close[_position]) / 4);
         break;
     }
   return(0.0);
  }
//+------------------------------------------------------------------+
//| Function: Simple Moving Average (SMA)                            |
//+------------------------------------------------------------------+
double SMA(const int position, const int period, const double &price[])
  {
   double result = 0.0;
   if(period > 0 && period <= (position + 1))
     {
      for(int i = 0; i < period; i++)
         result += price[position - i];
      result /= period;
     }
   return(result);
  }
//+------------------------------------------------------------------+
//| Function: Rolling / Smoothed Moving Average (RMA)                |
//+------------------------------------------------------------------+
double RMA(const int position, const int period, const double prev_value, const double &price[])
  {
   double result = 0.0;
   if(period > 0 && period <= (position + 1))
     {
      if(position == period - 1)
        {
         for(int i = 0; i < period; i++)
            result += price[position - i];
         result /= period;
        }
      result = (prev_value * (period - 1) + price[position]) / period;
     }
   return(result);
  }
//+------------------------------------------------------------------+
