//+------------------------------------------------------------------+
//|                                             Obv-Rsi_Flat_LBW.mq5 |
//|                           Copyright 2026, Firmament Level Trust. |
//|                              https://www.mql5.com/en/users/laxin | 
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Firmament Level Trust."
#property link      "https://www.mql5.com/en/users/laxin"
#property version   "1.00"

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

#property indicator_level1 85
#property indicator_level2 75
#property indicator_level3 50
#property indicator_level4 25
#property indicator_level5 15

#property indicator_type1   DRAW_LINE
#property indicator_color1  clrYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- inputs
input bool              UseRealVolume        = false;
input ENUM_APPLIED_PRICE ObvPrice            = PRICE_WEIGHTED;
input int               ObvRsi_Period        = 7;
input color             ObvRsi_Color         = clrYellow;
input int               ObvRsi_Width         = 2;
input ENUM_LINE_STYLE   ObvRsi_Style         = STYLE_SOLID;

//--- plot buffer
double RsiBuf[];

//--- internal arrays
double m_obv[];
double m_rsi[];

int    m_rsiPeriod;
int    m_size;
ENUM_APPLIED_PRICE m_obvPrice;

//--- applied price
double AppliedPrice(const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const int i)
{
   switch(m_obvPrice)
   {
      case PRICE_OPEN:    return open[i];
      case PRICE_HIGH:    return high[i];
      case PRICE_LOW:     return low[i];
      case PRICE_MEDIAN:  return (high[i]+low[i]) / 2.0;
      case PRICE_TYPICAL: return (high[i]+low[i]+close[i]) / 3.0;
      case PRICE_WEIGHTED:return (high[i]+low[i]+close[i]+close[i]) / 4.0;
      default:            return close[i];
   }
}

//--- your engine logic, flat
bool Compute(const double &open[],
             const double &high[],
             const double &low[],
             const double &close[],
             const long   &tick_volume[],
             const long   &real_volume[],
             const int     rates_total)
{
   m_size = rates_total;
   if(rates_total<=0 || m_size<rates_total)
      return(false);

   // 1) OBV
   for(int i=0;i<rates_total;i++)
   {
      double price      = AppliedPrice(open,high,low,close,i);
      double prev_price = (i==0 ? price : AppliedPrice(open,high,low,close,i-1));

      long vol_i = (UseRealVolume ? real_volume[i] : tick_volume[i]);

      if(i==0)
         m_obv[i] = 0.0;
      else
      {
         double prev_obv = m_obv[i-1];

         if(price > prev_price)      m_obv[i] = prev_obv + vol_i;
         else if(price < prev_price) m_obv[i] = prev_obv - vol_i;
         else                        m_obv[i] = prev_obv;
      }
   }

   // 2) RSI on OBV
   int p = m_rsiPeriod;
   if(p < 1) p = 1;

   if(rates_total <= p)
   {
      for(int i=0;i<rates_total;i++)
         m_rsi[i] = 50.0;
   }
   else
   {
      double gain=0.0, loss=0.0;

      for(int i=1;i<=p;i++)
      {
         double diff = m_obv[i] - m_obv[i-1];
         if(diff > 0) gain += diff;
         else         loss -= diff;
      }

      gain /= p;
      loss /= p;

      if(loss == 0.0)
         m_rsi[p] = (gain == 0.0 ? 50.0 : 100.0);
      else
      {
         double rs = gain / loss;
         m_rsi[p] = 100.0 - 100.0/(1.0 + rs);
      }

      for(int i=p+1;i<rates_total;i++)
      {
         double diff = m_obv[i] - m_obv[i-1];
         double up   = (diff > 0 ? diff : 0.0);
         double dn   = (diff < 0 ? -diff : 0.0);

         gain = (gain*(p-1) + up) / p;
         loss = (loss*(p-1) + dn) / p;

         if(loss == 0.0)
            m_rsi[i] = (gain == 0.0 ? 50.0 : 100.0);
         else
         {
            double rs = gain / loss;
            m_rsi[i] = 100.0 - 100.0/(1.0 + rs);
         }
      }

      for(int i=0;i<p;i++)
         m_rsi[i] = m_rsi[p];
   }

   return(true);
}

//--- OnInit
int OnInit()
{
   m_rsiPeriod = ObvRsi_Period;
   m_obvPrice  = ObvPrice;

   SetIndexBuffer(0, RsiBuf, INDICATOR_DATA);
   PlotIndexSetString(0, PLOT_LABEL, "RSI_OBV");
   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, ObvRsi_Color);
   PlotIndexSetInteger(0, PLOT_LINE_STYLE,    ObvRsi_Style);
   PlotIndexSetInteger(0, PLOT_LINE_WIDTH,    ObvRsi_Width);

   ArraySetAsSeries(m_obv, true);
   ArraySetAsSeries(m_rsi, true);

   return(INIT_SUCCEEDED);
}

//--- OnCalculate
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 &real_volume[],
                const int &spread[])
{
   if(rates_total<=0)
      return(0);

   ArrayResize(m_obv, rates_total);
   ArrayResize(m_rsi, rates_total);

   if(!Compute(open,high,low,close,tick_volume,real_volume,rates_total))
      return(prev_calculated);

   for(int i=0;i<rates_total;i++)
      RsiBuf[i] = m_rsi[i];

   return(rates_total);
}

