//+------------------------------------------------------------------+
//|                                      VolumeProfileCalculator.mqh |
//|                      Session Volume Profile Engine               |
//+------------------------------------------------------------------+
#ifndef VOLUME_PROFILE_CALCULATOR_MQH
#define VOLUME_PROFILE_CALCULATOR_MQH

#include "PriceHistogram.mqh"

//+------------------------------------------------------------------+
//| Volume Profile Calculator Class                                  |
//+------------------------------------------------------------------+
class CVolumeProfileCalculator
  {
private:
   int                m_poc_bin;
   double             m_poc_price;
   double             m_vah_price;
   double             m_val_price;

public:
                     CVolumeProfileCalculator(void);
                    ~CVolumeProfileCalculator(void);
   bool               Calculate(const CPriceHistogram &histogram,
                                double value_area_percent);
   double             GetPOC(void) const;
   double             GetVAH(void) const;
   double             GetVAL(void) const;
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CVolumeProfileCalculator::CVolumeProfileCalculator(void)
   : m_poc_bin(0),
     m_poc_price(0.0),
     m_vah_price(0.0),
     m_val_price(0.0)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CVolumeProfileCalculator::~CVolumeProfileCalculator(void)
  {
  }

//+------------------------------------------------------------------+
//| Calculate POC and Value Area                                     |
//+------------------------------------------------------------------+
bool CVolumeProfileCalculator::Calculate(const CPriceHistogram &histogram,
      double value_area_percent)
  {
   int    bin_count    = histogram.GetBinCount();
   double total_volume = histogram.GetTotalVolume();

   if(bin_count <= 0 || total_volume <= 0.0)
      return(false);

//--- Locate the Point of Control
   m_poc_bin = 0;
   double max_volume = histogram.GetBinVolume(0);

   for(int i = 1; i < bin_count; i++)
     {
      double v = histogram.GetBinVolume(i);
      if(v > max_volume)
        {
         max_volume = v;
         m_poc_bin  = i;
        }
     }

   m_poc_price = histogram.GetBinPrice(m_poc_bin);

//--- Compute the volume threshold for the Value Area
   double va_threshold = total_volume * (value_area_percent / 100.0);
   double va_volume    = histogram.GetBinVolume(m_poc_bin);

   int upper_bin = m_poc_bin;
   int lower_bin = m_poc_bin;

//--- Expand the Value Area outward from the POC
   while(va_volume < va_threshold)
     {
      double vol_above = (upper_bin + 1 < bin_count)
                         ? histogram.GetBinVolume(upper_bin + 1)
                         : -1.0;
      double vol_below = (lower_bin - 1 >= 0)
                         ? histogram.GetBinVolume(lower_bin - 1)
                         : -1.0;

      if(vol_above < 0.0 && vol_below < 0.0)
         break;

      //--- Add the side with greater volume
      if(vol_above >= vol_below)
        {
         upper_bin++;
         va_volume += vol_above;
        }
      else
        {
         lower_bin--;
         va_volume += vol_below;
        }
     }

   m_vah_price = histogram.GetBinPrice(upper_bin);
   m_val_price = histogram.GetBinPrice(lower_bin);

   return(true);
  }

//+------------------------------------------------------------------+
//| Accessor: Point of Control price                                 |
//+------------------------------------------------------------------+
double CVolumeProfileCalculator::GetPOC(void) const
  {
   return(m_poc_price);
  }

//+------------------------------------------------------------------+
//| Accessor: Value Area High price                                  |
//+------------------------------------------------------------------+
double CVolumeProfileCalculator::GetVAH(void) const
  {
   return(m_vah_price);
  }

//+------------------------------------------------------------------+
//| Accessor: Value Area Low price                                   |
//+------------------------------------------------------------------+
double CVolumeProfileCalculator::GetVAL(void) const
  {
   return(m_val_price);
  }

#endif // VOLUME_PROFILE_CALCULATOR_MQH
//+------------------------------------------------------------------+