//+------------------------------------------------------------------+
//|                                  Absolute Strength Histogram.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_separate_window
#property indicator_level1  0
#property indicator_buffers 13
#property indicator_plots   3
//--- plot ASH
#property indicator_label1  "ASH"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrLimeGreen,clrOrangeRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  5
//--- plot Bulls
#property indicator_label2  "Bulls"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2
//--- plot Bears
#property indicator_label3  "Bears"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrMagenta
#property indicator_style3  STYLE_SOLID
#property indicator_width3  2
enum ENUM_MODE
  {
   MODE_RSI,      // RSI
   MODE_STOCH     // Stochastic
  };
//--- input parameters
input ENUM_MODE               inp_mode          = MODE_RSI;       // Applied Mode
input int                     inp_length        = 9;              // Period
input int                     inp_smooth        = 1;              // Smoothing Period
input ENUM_APPLIED_PRICE      inp_price         = PRICE_CLOSE;    // Applied Price
input ENUM_MA_METHOD          inp_ma_method     = MODE_LWMA;      // Moving Average Method
//--- indicator buffers
double                        ASH[];
double                        ASHColor[];
double                        BullsPlot[];
double                        BearsPlot[];
double                        Price[];
double                        Price1[];
double                        Price2[];
double                        Bulls[];
double                        Bears[];
double                        AvgBulls[];
double                        AvgBears[];
double                        SmthBulls[];
double                        SmthBears[];
//--- indicator variables
int                           length;
int                           smooth;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check input parameters
   length = inp_length < 1 ? 1 : inp_length;
   smooth = inp_smooth < 1 ? 1 : inp_smooth;
//--- indicator buffers mapping
   SetIndexBuffer(0, ASH, INDICATOR_DATA);
   SetIndexBuffer(1, ASHColor, INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, BullsPlot, INDICATOR_DATA);
   SetIndexBuffer(3, BearsPlot, INDICATOR_DATA);
   SetIndexBuffer(4, Price, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, Price1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, Price2, INDICATOR_CALCULATIONS);
   SetIndexBuffer(7, Bulls, INDICATOR_CALCULATIONS);
   SetIndexBuffer(8, Bears, INDICATOR_CALCULATIONS);
   SetIndexBuffer(9, AvgBulls, INDICATOR_CALCULATIONS);
   SetIndexBuffer(10, AvgBears, INDICATOR_CALCULATIONS);
   SetIndexBuffer(11, SmthBulls, INDICATOR_CALCULATIONS);
   SetIndexBuffer(12, SmthBears, INDICATOR_CALCULATIONS);
//--- set indicator accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, 2);
//--- set indicator name display
   string mode = inp_mode == MODE_RSI ? "RSI" : inp_mode == MODE_STOCH ? "Stoch" : " ";
   string applied_price = inp_price == PRICE_OPEN ? "Open" : inp_price == PRICE_HIGH ? "High" : inp_price == PRICE_LOW ? "Low" : inp_price == PRICE_CLOSE ? "Close" : inp_price == PRICE_MEDIAN ? "Median" : inp_price == PRICE_TYPICAL ? "Typical" : inp_price == PRICE_WEIGHTED ? "Weighted" : " ";
   string ma_method = inp_ma_method == MODE_SMA ? "SMA" : inp_ma_method == MODE_EMA ? "EMA" : inp_ma_method == MODE_SMMA ? "RMA" : inp_ma_method == MODE_LWMA ? "WMA" : " ";
   string short_name = "ASH (" + mode + ", " + IntegerToString(length) + ", " + IntegerToString(smooth) + ", " + applied_price + ", " + ma_method + ")";
   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(ASH, EMPTY_VALUE);
   ArrayInitialize(ASHColor, EMPTY_VALUE);
   ArrayInitialize(BullsPlot, EMPTY_VALUE);
   ArrayInitialize(BearsPlot, EMPTY_VALUE);
   ArrayInitialize(Price, 0.0);
   ArrayInitialize(Price1, 0.0);
   ArrayInitialize(Price2, 0.0);
   ArrayInitialize(Bulls, 0.0);
   ArrayInitialize(Bears, 0.0);
   ArrayInitialize(AvgBulls, 0.0);
   ArrayInitialize(AvgBears, 0.0);
   ArrayInitialize(SmthBulls, 0.0);
   ArrayInitialize(SmthBears, 0.0);
//--- 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(fmax(length, smooth) <= 1 || fmax(length, smooth) > 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++)
     {
      //--- populate price array
      Price[i] = inp_price == PRICE_CLOSE ? close[i] : inp_price == PRICE_HIGH ? high[i] : inp_price == PRICE_LOW ? low[i] : inp_price == PRICE_MEDIAN ? (high[i] + low[i]) / 2 : inp_price == PRICE_OPEN ? open[i] : inp_price == PRICE_TYPICAL ? (high[i] + low[i] + close[i]) / 3 : inp_price == PRICE_WEIGHTED ? (high[i] + low[i] + close[i] + close[i]) / 4 : 0.0;
      if(i > fmax(length, smooth))
        {
         Price1[i] = SMA(i, 1, Price);
         Price2[i] = SMA(i - 1, 1, Price);
         Bulls[i] = inp_mode == MODE_RSI ? 0.5 * (fabs(Price1[i] - Price2[i]) + (Price1[i] - Price2[i])) : inp_mode == MODE_STOCH ? Price1[i] - MIN(i, length, low) : 0.0;
         Bears[i] = inp_mode == MODE_RSI ? 0.5 * (fabs(Price1[i] - Price2[i]) - (Price1[i] - Price2[i])) : inp_mode == MODE_STOCH ? MAX(i, length, high) - Price1[i] : 0.0;
         AvgBulls[i] = inp_ma_method == MODE_SMA ? SMA(i, length, Bulls) : inp_ma_method == MODE_EMA ? EMA(i, length, AvgBulls[i - 1], Bulls) : inp_ma_method == MODE_SMMA ? RMA(i, length, AvgBulls[i - 1], Bulls) : inp_ma_method == MODE_LWMA ? WMA(i, length, Bulls) : 0.0;
         AvgBears[i] = inp_ma_method == MODE_SMA ? SMA(i, length, Bears) : inp_ma_method == MODE_EMA ? EMA(i, length, AvgBears[i - 1], Bears) : inp_ma_method == MODE_SMMA ? RMA(i, length, AvgBears[i - 1], Bears) : inp_ma_method == MODE_LWMA ? WMA(i, length, Bears) : 0.0;
         SmthBulls[i] = inp_ma_method == MODE_SMA ? SMA(i, smooth, AvgBulls) : inp_ma_method == MODE_EMA ? EMA(i, smooth, SmthBulls[i - 1], AvgBulls) : inp_ma_method == MODE_SMMA ? RMA(i, smooth, SmthBulls[i - 1], AvgBulls) : inp_ma_method == MODE_LWMA ? WMA(i, smooth, AvgBulls) : 0.0;
         SmthBears[i] = inp_ma_method == MODE_SMA ? SMA(i, smooth, AvgBears) : inp_ma_method == MODE_EMA ? EMA(i, smooth, SmthBears[i - 1], AvgBears) : inp_ma_method == MODE_SMMA ? RMA(i, smooth, SmthBears[i - 1], AvgBears) : inp_ma_method == MODE_LWMA ? WMA(i, smooth, AvgBears) : 0.0;
         ASH[i] = SmthBulls[i] > SmthBears[i] ? SmthBulls[i] / _Point : SmthBulls[i] < SmthBears[i] ? SmthBears[i] / _Point : ASH[i - 1];
         ASHColor[i] = SmthBulls[i] > SmthBears[i] ? 0.0 : SmthBulls[i] < SmthBears[i] ? 1.0 : ASHColor[i - 1];
         BullsPlot[i] = SmthBulls[i] / _Point;
         BearsPlot[i] = SmthBears[i] / _Point;
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Function: Maximum Value                                          |
//+------------------------------------------------------------------+
double MAX(const int position, const int period, const double &price[])
  {
   double result = -DBL_MAX;
   for(int j = 0; j <= period; j++)
     {
      result = price[position - j] > result ? price[position - j] : result;
     }
   return(result);
  }
//+------------------------------------------------------------------+
//| Function: Minimum Value                                          |
//+------------------------------------------------------------------+
double MIN(const int position, const int period, const double &price[])
  {
   double result = DBL_MAX;
   for(int j = 0; j <= period; j++)
     {
      result = price[position - j] < result ? price[position - j] : result;
     }
   return(result);
  }
//+------------------------------------------------------------------+
//| 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: Exponential Moving Average (EMA)                       |
//+------------------------------------------------------------------+
double EMA(const int position, const int period, const double prev_value, const double &price[])
  {
   double result = 0.0;
   if(period > 0)
     {
      double pr = 2.0 / (period + 1.0);
      result = price[position] * pr + prev_value * (1 - pr);
     }
   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);
  }
//+------------------------------------------------------------------+
//| Function: Linear Weighted Moving Average (WMA)                   |
//+------------------------------------------------------------------+
double WMA(const int position, const int period, const double &price[])
  {
   double result = 0.0;
   if(period > 0 && period <= (position + 1))
     {
      double sum = 0.0;
      int    wsum = 0;
      for(int i = period; i > 0; i--)
        {
         wsum += i;
         sum += price[position - i + 1] * (period - i + 1);
        }
      result = sum / wsum;
     }
   return(result);
  }
//+------------------------------------------------------------------+
