//+------------------------------------------------------------------+
//| 59_QQE_Confirmed_Arrows_v1_01.mq5                               |
//| Marks confirmed smoothed-RSI 50-level crosses on the price chart.|
//+------------------------------------------------------------------+
#property strict
#property version   "1.01"
#property indicator_chart_window
#property indicator_plots 2
#property indicator_buffers 2
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrLime
#property indicator_width1 2
#property indicator_label1 "QQE confirmed up"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrTomato
#property indicator_width2 2
#property indicator_label2 "QQE confirmed down"

input int InpRsiPeriod        = 14;
input int InpSmoothPeriod     = 5;
input int InpArrowOffsetPoints = 100;

double UpArrow[];
double DownArrow[];
int    g_rsi_handle = INVALID_HANDLE;

int OnInit()
{
   if(InpRsiPeriod < 2 || InpSmoothPeriod < 1 || InpArrowOffsetPoints < 0)
      return INIT_PARAMETERS_INCORRECT;

   SetIndexBuffer(0, UpArrow, INDICATOR_DATA);
   SetIndexBuffer(1, DownArrow, INDICATOR_DATA);
   ArraySetAsSeries(UpArrow, true);
   ArraySetAsSeries(DownArrow, true);
   PlotIndexSetInteger(0, PLOT_ARROW, 233);
   PlotIndexSetInteger(1, PLOT_ARROW, 234);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   const int draw_begin = InpRsiPeriod + InpSmoothPeriod;
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin);

   ResetLastError();
   g_rsi_handle = iRSI(_Symbol, _Period, InpRsiPeriod, PRICE_CLOSE);
   if(g_rsi_handle == INVALID_HANDLE)
   {
      PrintFormat("QQE arrows: iRSI failed error=%d", GetLastError());
      return INIT_FAILED;
   }

   IndicatorSetString(INDICATOR_SHORTNAME, "QQE confirmed arrows (14,5)");
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(g_rsi_handle != INVALID_HANDLE)
      IndicatorRelease(g_rsi_handle);
}

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[])
{
   const int minimum_bars = InpRsiPeriod + InpSmoothPeriod + 2;
   if(rates_total < minimum_bars || BarsCalculated(g_rsi_handle) < rates_total)
      return 0;

   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);

   double raw_rsi[];
   ArraySetAsSeries(raw_rsi, true);
   ResetLastError();
   const int copied = CopyBuffer(g_rsi_handle, 0, 0, rates_total, raw_rsi);
   if(copied != rates_total)
   {
      PrintFormat("QQE arrows: CopyBuffer requested=%d copied=%d error=%d",
                  rates_total, copied, GetLastError());
      return 0;
   }

   if(prev_calculated == 0)
   {
      ArrayInitialize(UpArrow, EMPTY_VALUE);
      ArrayInitialize(DownArrow, EMPTY_VALUE);
   }

   const int oldest_valid = rates_total - InpSmoothPeriod - 1;
   const int limit = (prev_calculated == 0)
                     ? oldest_valid
                     : MathMin(oldest_valid, rates_total - prev_calculated + 1);

   for(int i = limit; i >= 1; --i)
   {
      UpArrow[i] = EMPTY_VALUE;
      DownArrow[i] = EMPTY_VALUE;

      double current_sum = 0.0;
      double previous_sum = 0.0;
      bool valid = true;
      for(int j = 0; j < InpSmoothPeriod; ++j)
      {
         const double current_value = raw_rsi[i + j];
         const double previous_value = raw_rsi[i + 1 + j];
         if(current_value == EMPTY_VALUE || previous_value == EMPTY_VALUE ||
            !MathIsValidNumber(current_value) || !MathIsValidNumber(previous_value))
         {
            valid = false;
            break;
         }
         current_sum += current_value;
         previous_sum += previous_value;
      }
      if(!valid)
         continue;

      const double current_smoothed = current_sum / InpSmoothPeriod;
      const double previous_smoothed = previous_sum / InpSmoothPeriod;
      if(current_smoothed > 50.0 && previous_smoothed <= 50.0)
         UpArrow[i] = low[i] - InpArrowOffsetPoints * _Point;
      else if(current_smoothed < 50.0 && previous_smoothed >= 50.0)
         DownArrow[i] = high[i] + InpArrowOffsetPoints * _Point;
   }

   UpArrow[0] = EMPTY_VALUE;
   DownArrow[0] = EMPTY_VALUE;
   return rates_total;
}
//+------------------------------------------------------------------+
