//+------------------------------------------------------------------+
//|                                                          PVI.mq5 |
//|                                          Copyright 2024,JBlanked |
//|                                        https://www.jblanked.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024,JBlanked"
#property link      "https://www.jblanked.com/"
#property version   "1.01"
#property indicator_separate_window

#property indicator_buffers 1
#property indicator_plots   1

#property indicator_label1  "PVI"
#property indicator_type1   DRAW_LINE
#property indicator_style1  STYLE_SOLID
#property indicator_color1  clrLime
#property indicator_width1  1

#ifdef __MQL4__ enum ENUM_APPLIED_VOLUME { VOLUME_TICK, VOLUME_REAL }; #endif

input ENUM_APPLIED_VOLUME  inpVolumeType  = VOLUME_TICK; // Volume Type

double PVI[];
MqlRates rates[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0, PVI, INDICATOR_DATA);
//---
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   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[])
  {
//---
   int limit = rates_total - prev_calculated;

   if(prev_calculated < 1)
     {
      ArrayInitialize(PVI, 1.0);
      ArraySetAsSeries(PVI, true);
     }
   else
     {
      limit++;
     }

   for(int pos = limit - 1; pos >= 0; pos--)
     {
      PVI[pos] = iPVI(_Symbol, PERIOD_CURRENT, inpVolumeType, pos);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Positive Volume Index (PVI)                                      |
//+------------------------------------------------------------------+
double iPVI(string symbol, ENUM_TIMEFRAMES timeframe, ENUM_APPLIED_VOLUME volumeType, int shift)
  {
   static double lastValue = 1.0;  // Initialize lastValue to 1.0, if not already set

   long Vol0, Vol1;
   MqlRates mqlRates[];
   ArraySetAsSeries(mqlRates, true);  // Ensure array is set as series
   if(CopyRates(symbol, timeframe, shift, 2, mqlRates) < 2)  // Fetch the last 2 bars
     {
      return lastValue;
     }
// Fetch volumes based on volume type (tick or real)
   if(volumeType == VOLUME_TICK)
     {
      Vol0 = mqlRates[0].tick_volume;
      Vol1 = mqlRates[1].tick_volume;
     }
   else
     {
      Vol0 = mqlRates[0].real_volume;
      Vol1 = mqlRates[1].real_volume;
     }

// Calculate the new PVI value
   if(Vol0 > Vol1)
     {
      lastValue = lastValue * (1 + ((mqlRates[0].close - mqlRates[1].close) / mqlRates[1].close));
     }

// Return the updated PVI value
   return lastValue;
  }
//+------------------------------------------------------------------+
