//+------------------------------------------------------------------+
//|                                                 Ultimate_RSI.mq5 |
//|                                  Copyright 2026, LuxAlgo / MQL5  |
//| https://creativecommons.org/licenses/by-nc-sa/4.0/ (CC BY-NC-SA) |
//+------------------------------------------------------------------+
#property copyright "LuxAlgo / Converted to MQL5"
#property link      "https://creativecommons.org/licenses/by-nc-sa/4.0/"
#property version   "1.40"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   4

// Configuración de niveles
#property indicator_level1 80.0
#property indicator_level2 50.0
#property indicator_level3 20.0
#property indicator_levelcolor clrDarkGray
#property indicator_levelstyle STYLE_DOT

// Plot 1: Ultimate RSI
#property indicator_label1  "Ultimate RSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

// Plot 2: Signal Line
#property indicator_label2  "Signal Line"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

// Plot 3: Flechas Alcistas
#property indicator_label3  "Cruce Alcista"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrLime
#property indicator_width3  2

// Plot 4: Flechas Bajistas
#property indicator_label4  "Cruce Bajista"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrRed
#property indicator_width4  2

// Enumeración para tipos de promedio
enum ENUM_MA_TYPE
  {
   MA_EMA = 0, // EMA
   MA_SMA = 1, // SMA
   MA_RMA = 2, // RMA (Wilder's Smoothing)
   MA_TMA = 3  // TMA (Triangular MA)
  };

//--- Parámetros de Entrada (Inputs)
input group "=== Configuración RSI ==="
input int            InpLength   = 14;          // Longitud Ultimate RSI
input ENUM_MA_TYPE   InpSmoType1 = MA_RMA;      // Método Suavizado RSI
input ENUM_APPLIED_PRICE InpSource = PRICE_CLOSE; // Precio Fuente

input group "=== Línea de Señal ==="
input int            InpSmooth   = 14;          // Longitud Señal
input ENUM_MA_TYPE   InpSmoType2 = MA_EMA;      // Método Suavizado Señal

input group "=== Flechas y Niveles ==="
input bool           InpShowArrows = true;      // Mostrar Flechas de Cruce
input double         InpOB         = 80.0;      // Sobrecompra (Overbought)
input double         InpOS         = 20.0;      // Sobreventa (Oversold)

input group "=== Configuración de Alertas ==="
input bool           InpEnableAlerts = true;    // Activar Alertas (ON/OFF)
input bool           InpSoundAlert   = true;    // Alerta Pop-up y Sonora
input bool           InpPushAlert    = false;   // Notificación Push al Celular
input bool           InpEmailAlert   = false;   // Notificación por Email

//--- Buffers de Indicadores
double ExtArsiBuffer[];
double ExtSignalBuffer[];
double ExtBuyBuffer[];
double ExtSellBuffer[];

//--- Variables y arreglos dinámicos internos
double ExtDiffBuffer[];
double ExtAbsDiffBuffer[];
double ExtNumBuffer[];
double ExtDenBuffer[];

datetime lastAlertTime = 0; // Control para evitar alertas duplicadas en la misma vela

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Asignación de buffers visibles
   SetIndexBuffer(0, ExtArsiBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, ExtSignalBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, ExtBuyBuffer, INDICATOR_DATA);
   SetIndexBuffer(3, ExtSellBuffer, INDICATOR_DATA);

   // Configuración visual de las flechas (Wingdings)
   PlotIndexSetInteger(2, PLOT_ARROW, 233); // Flecha arriba
   PlotIndexSetInteger(3, PLOT_ARROW, 234); // Flecha abajo

   PlotIndexSetString(0, PLOT_LABEL, "Ultimate RSI");
   PlotIndexSetString(1, PLOT_LABEL, "Signal Line");
   PlotIndexSetString(2, PLOT_LABEL, "Cruce Alcista");
   PlotIndexSetString(3, PLOT_LABEL, "Cruce Bajista");

   IndicatorSetString(INDICATOR_SHORTNAME, "Ultimate RSI (" + IntegerToString(InpLength) + ")");
   IndicatorSetInteger(INDICATOR_DIGITS, 2);

   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, InpOB);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, 50.0);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 2, InpOS);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Función auxiliar para cálculo de promedios                       |
//+------------------------------------------------------------------+
double CalculateMA(const double &srcArr[], const double &destArr[], int period, ENUM_MA_TYPE type, int i)
  {
   if(i < period) return 0.0;

   switch(type)
     {
      case MA_SMA:
        {
         double sum = 0.0;
         for(int j = 0; j < period; j++) sum += srcArr[i - j];
         return sum / period;
        }

      case MA_EMA:
        {
         double alpha = 2.0 / (period + 1.0);
         if(i == period || destArr[i - 1] == 0.0)
           {
            double sum = 0.0;
            for(int j = 0; j < period; j++) sum += srcArr[i - j];
            return sum / period;
           }
         return (srcArr[i] * alpha) + (destArr[i - 1] * (1.0 - alpha));
        }

      case MA_RMA:
        {
         double alpha = 1.0 / period;
         if(i == period || destArr[i - 1] == 0.0)
           {
            double sum = 0.0;
            for(int j = 0; j < period; j++) sum += srcArr[i - j];
            return sum / period;
           }
         return (srcArr[i] * alpha) + (destArr[i - 1] * (1.0 - alpha));
        }

      case MA_TMA:
        {
         int len2 = (int)MathCeil((double)period / 2.0);
         double sumOuter = 0.0;
         for(int k = 0; k < len2; k++)
           {
            double sumInner = 0.0;
            for(int j = 0; j < len2; j++)
              {
               if((i - k - j) >= 0) sumInner += srcArr[i - k - j];
              }
            sumOuter += (sumInner / len2);
           }
         return sumOuter / len2;
        }
     }
   return 0.0;
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   if(rates_total < InpLength + InpSmooth + 2) return(0);

   // Dimensionar arreglos de cálculo dinámicos
   ArrayResize(ExtDiffBuffer, rates_total);
   ArrayResize(ExtAbsDiffBuffer, rates_total);
   ArrayResize(ExtNumBuffer, rates_total);
   ArrayResize(ExtDenBuffer, rates_total);

   int start = prev_calculated - 1;
   if(start < 1)
     {
      start = 1;
      ArrayInitialize(ExtArsiBuffer, 0.0);
      ArrayInitialize(ExtSignalBuffer, 0.0);
      ArrayInitialize(ExtBuyBuffer, EMPTY_VALUE);
      ArrayInitialize(ExtSellBuffer, EMPTY_VALUE);
      ArrayInitialize(ExtDiffBuffer, 0.0);
      ArrayInitialize(ExtAbsDiffBuffer, 0.0);
      ArrayInitialize(ExtNumBuffer, 0.0);
      ArrayInitialize(ExtDenBuffer, 0.0);
     }

   // Bucle principal de cálculo
   for(int i = start; i < rates_total; i++)
     {
      ExtBuyBuffer[i]  = EMPTY_VALUE;
      ExtSellBuffer[i] = EMPTY_VALUE;

      // Obtener precio fuente
      double src = close[i];
      double prev_src = close[i - 1];

      if(InpSource == PRICE_OPEN)     { src = open[i]; prev_src = open[i - 1]; }
      if(InpSource == PRICE_HIGH)     { src = high[i]; prev_src = high[i - 1]; }
      if(InpSource == PRICE_LOW)      { src = low[i];  prev_src = low[i - 1]; }
      if(InpSource == PRICE_MEDIAN)   { src = (high[i] + low[i]) / 2.0; prev_src = (high[i - 1] + low[i - 1]) / 2.0; }
      if(InpSource == PRICE_TYPICAL)  { src = (high[i] + low[i] + close[i]) / 3.0; prev_src = (high[i - 1] + low[i - 1] + close[i - 1]) / 3.0; }
      if(InpSource == PRICE_WEIGHTED) { src = (high[i] + low[i] + 2.0 * close[i]) / 4.0; prev_src = (high[i - 1] + low[i - 1] + 2.0 * close[i - 1]) / 4.0; }

      if(i >= InpLength)
        {
         // Calcular máximo y mínimo local
         double upper = src;
         double lower = src;
         for(int j = 0; j < InpLength; j++)
           {
            double p = close[i - j];
            if(InpSource == PRICE_OPEN) p = open[i - j];
            if(InpSource == PRICE_HIGH) p = high[i - j];
            if(InpSource == PRICE_LOW)  p = low[i - j];

            if(p > upper) upper = p;
            if(p < lower) lower = p;
           }

         double upper1 = prev_src;
         double lower1 = prev_src;
         for(int j = 1; j <= InpLength; j++)
           {
            double p = close[i - j];
            if(InpSource == PRICE_OPEN) p = open[i - j];
            if(InpSource == PRICE_HIGH) p = high[i - j];
            if(InpSource == PRICE_LOW)  p = low[i - j];

            if(p > upper1) upper1 = p;
            if(p < lower1) lower1 = p;
           }

         double r = upper - lower;
         double d = src - prev_src;

         if(upper > upper1)
            ExtDiffBuffer[i] = r;
         else if(lower < lower1)
            ExtDiffBuffer[i] = -r;
         else
            ExtDiffBuffer[i] = d;

         ExtAbsDiffBuffer[i] = MathAbs(ExtDiffBuffer[i]);

         // Promedios numeradores y denominadores
         ExtNumBuffer[i] = CalculateMA(ExtDiffBuffer, ExtNumBuffer, InpLength, InpSmoType1, i);
         ExtDenBuffer[i] = CalculateMA(ExtAbsDiffBuffer, ExtDenBuffer, InpLength, InpSmoType1, i);

         if(ExtDenBuffer[i] != 0.0)
            ExtArsiBuffer[i] = (ExtNumBuffer[i] / ExtDenBuffer[i]) * 50.0 + 50.0;
         else
            ExtArsiBuffer[i] = 50.0;
        }

      // Línea de Señal
      if(i >= InpLength + InpSmooth)
        {
         ExtSignalBuffer[i] = CalculateMA(ExtArsiBuffer, ExtSignalBuffer, InpSmooth, InpSmoType2, i);

         // Cruces para dibujar Flechas
         if(InpShowArrows)
           {
            bool crossOver  = (ExtArsiBuffer[i - 1] <= ExtSignalBuffer[i - 1]) && (ExtArsiBuffer[i] > ExtSignalBuffer[i]);
            bool crossUnder = (ExtArsiBuffer[i - 1] >= ExtSignalBuffer[i - 1]) && (ExtArsiBuffer[i] < ExtSignalBuffer[i]);

            if(crossOver)
               ExtBuyBuffer[i] = ExtSignalBuffer[i] - 2.5;
            if(crossUnder)
               ExtSellBuffer[i] = ExtSignalBuffer[i] + 2.5;

            // Gestión de Alertas
            if(InpEnableAlerts && i == rates_total - 1 && time[i] != lastAlertTime)
              {
               bool alertBuy  = (ExtArsiBuffer[rates_total - 2] <= ExtSignalBuffer[rates_total - 2]) && (ExtArsiBuffer[rates_total - 1] > ExtSignalBuffer[rates_total - 1]);
               bool alertSell = (ExtArsiBuffer[rates_total - 2] >= ExtSignalBuffer[rates_total - 2]) && (ExtArsiBuffer[rates_total - 1] < ExtSignalBuffer[rates_total - 1]);

               if(alertBuy || alertSell)
                 {
                  string msg = alertBuy ? "Ultimate RSI: ¡Cruce ALCISTA (COMPRA) en " + _Symbol + "!" 
                                        : "Ultimate RSI: ¡Cruce BAJISTA (VENTA) en " + _Symbol + "!";
                  
                  if(InpSoundAlert) Alert(msg);
                  if(InpPushAlert)  SendNotification(msg);
                  if(InpEmailAlert) SendMail("Alerta Ultimate RSI - " + _Symbol, msg);

                  lastAlertTime = time[i]; // Evita disparar múltiples alertas en la misma vela
                 }
              }
           }
        }
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+