#property copyright "Copyright 2018-2020, Level Up Software"
#property link      "http://www.az-invest.eu"
#property version   "3.00"

input bool UseOnRangeBarChart = true; // Use this indicator on RangeBar chart

//#define DEVELOPER_VERSION
#include <AZ-INVEST/SDK/RangeBars.mqh>

class RangeBarIndicator
{
   private: 
   
      RangeBars          * rangeBars;
      int                  rates_total;
      int                  prev_calculated;
      bool                 getVolumes;
      bool                 getVolumeBreakdown;
      bool                 getTime;
      bool                 useAppliedPrice;
      ENUM_APPLIED_PRICE   applied_price;
      
      bool                 firstRun;
      bool                 dataReady;
      
      datetime             prevTime;
      int                  prevRatesTotal;

   public:
   
      datetime Time[];
      double   Open[];
      double   Low[];
      double   High[];
      double   Close[];
      double   Price[];
      long     Tick_volume[];
      long     Real_volume[];
      double   Buy_volume[];
      double   Sell_volume[];
      double   BuySell_volume[];

      datetime GetTime(int index) { return GetArrayValueDateTime(Time, index); };
      double   GetOpen(int index) { return GetArrayValueDouble(Open, index); };
      double   GetLow(int index) { return GetArrayValueDouble(Low, index); };
      double   GetHigh(int index) { return GetArrayValueDouble(High, index); };
      double   GetClose(int index) { return GetArrayValueDouble(Close, index); };
      double   GetPrice(int index) { return GetArrayValueDouble(Price, index); };
      long     GetTick_volume(int index) { return GetArrayValueLong(Tick_volume, index); };
      long     GetReal_volume(int index) { return GetArrayValueLong(Real_volume, index); };
      double   GetBuy_volume(int index)  { return GetArrayValueDouble(Buy_volume, index); };
      double   GetSell_volume(int index) { return GetArrayValueDouble(Sell_volume, index); };
      double   GetBuySell_volume(int index) { return GetArrayValueDouble(BuySell_volume, index); };

      bool     IsNewBar;

               RangeBarIndicator();
               ~RangeBarIndicator();
               
      void     SetUseAppliedPriceFlag(ENUM_APPLIED_PRICE _applied_price) { this.useAppliedPrice = true; this.applied_price = _applied_price; };
      void     SetGetVolumesFlag() { this.getVolumes = true; };
      void     SetGetVolumeBreakdownFlag() { this.getVolumeBreakdown = true; };
      void     SetGetTimeFlag() { this.getTime = true; };
      
      bool     OnCalculate(const int _rates_total,const int _prev_calculated, const datetime &_Time[], const double &_Close[]);
      void     OnDeinit(const int reason);
      bool     BufferSynchronizationCheck(const double &buffer[]);      
      int      GetPrevCalculated() { return prev_calculated; };     
      int      GetRatesTotal() { return ArraySize(Open); };     
      void     BufferShiftLeft(double &buffer[]);
      
   private:
  
      bool  CheckStatus();
      bool  NeedsReload();
      int   GetOLHC(int start, int count);
      int   GetOLHCForIndicatorCalc(double &o[],double &l[],double &h[],double &c[],datetime &t[],long &tickVolume[],long &realVolume[], double &buyVolume[], double &sellVolume[], double &buySellVolume[], int start, int count);
      int   GetOLHCAndApplPriceForIndicatorCalc(double &o[],double &l[],double &h[],double &c[],datetime &t[],long &tickVolume[],long &realVolume[], double &buyVolume[], double &sellVolume[], double &buySellVolume[], double &price[],ENUM_APPLIED_PRICE applied_price, int start, int count);
      void  OLHCShiftRight();
      void  OLHCResize();
      
      bool  Canvas_IsNewBar(const datetime &_Time[]);
      bool  Canvas_IsRatesTotalChanged(int ratesTotalNow);      
      int   Canvas_RatesTotalChangedBy(int ratesTotalNow);
      
      double CalcAppliedPrice(const MqlRates &_rates, ENUM_APPLIED_PRICE applied_price);
      double CalcAppliedPrice(const double &o,const double &l,const double &h,const double &c,ENUM_APPLIED_PRICE applied_price);

      ENUM_TIMEFRAMES TFMigrate(int tf);
      datetime iTime(string symbol,int tf,int index);
      double GetArrayValueDouble(double &arr[], int index);
      long GetArrayValueLong(long &arr[], int index);
      datetime GetArrayValueDateTime(datetime &arr[], int index);
};

RangeBarIndicator::RangeBarIndicator(void)
{
   rangeBars = new RangeBars(UseOnRangeBarChart);
   if(rangeBars != NULL)
      rangeBars.Init();
      
   useAppliedPrice = false;
   getVolumes = false;
   getTime = false;
   
   dataReady = false;
   firstRun = true;
   prevTime = 0;
   prevRatesTotal = 0;
}

RangeBarIndicator::~RangeBarIndicator(void)
{
   if(rangeBars != NULL)
   {
      rangeBars.Deinit();
      delete rangeBars;
   }
}

bool RangeBarIndicator::CheckStatus(void)
{
   int handle = rangeBars.GetHandle();
   if(handle == INVALID_HANDLE)
      return false;
   
   return true;
}

bool RangeBarIndicator::NeedsReload(void)
{
  
   if(rangeBars.Reload())
   {
     Print("Chart settings changed - reloading indicator with new settings");
     return true;
   }
   
   return false;
}

bool RangeBarIndicator::OnCalculate(const int _rates_total,const int _prev_calculated, const datetime &_Time[], const double &_Close[])
{   
   if(firstRun)
   {
      Canvas_IsNewBar(_Time); 
      Canvas_RatesTotalChangedBy(_rates_total);   
      IsNewBar = rangeBars.IsNewBar();   
   }

   if(!CheckStatus())
   {
      if(rangeBars != NULL)
         delete rangeBars;
      
      rangeBars = new RangeBars(UseOnRangeBarChart);
      if(rangeBars != NULL)
         rangeBars.Init();
      
      Print("CheckStatus block failed");
            
      return false;
   }

   ArraySetAsSeries(this.Time,false);
   ArraySetAsSeries(this.Open,false);
   ArraySetAsSeries(this.High,false);
   ArraySetAsSeries(this.Low,false);
   ArraySetAsSeries(this.Close,false);   
   ArraySetAsSeries(this.Price,false);   
   ArraySetAsSeries(this.Tick_volume,false);   
   ArraySetAsSeries(this.Real_volume,false);   
   ArraySetAsSeries(this.Buy_volume,false);   
   ArraySetAsSeries(this.Sell_volume,false);   
   ArraySetAsSeries(this.BuySell_volume,false);   

   if(firstRun)
   {
      GetOLHC(0,_rates_total);
      firstRun = false;   
   }
           
   if(NeedsReload() || !this.dataReady)
   {
      GetOLHC(0,_rates_total);
      this.prev_calculated = 0;
      firstRun = true; 
      ChartSetSymbolPeriod(ChartID(), _Symbol, _Period); // try to force reload
      return false;
   }                                      

   bool change = Canvas_RatesTotalChangedBy(_rates_total);

   if(change != 0)
   {
      #ifdef DISPLAY_DEBUG_MSG
         Print("rates total changed to:"+_rates_total);
      #endif

      if(change == 1)
      {
         #ifdef DISPLAY_DEBUG_MSG
            Print("changed by 1 => Resize called");   
         #endif
         OLHCResize();
      }
      else      
      {
         #ifdef DISPLAY_DEBUG_MSG
            Print("changed by "+change+" => getting ALL");
         #endif
         GetOLHC(0,_rates_total);
      }

      this.prev_calculated = 0;
      Canvas_IsNewBar(_Time);
      return true;   
   }
   else if(Canvas_IsNewBar(_Time))
   {
      #ifdef DISPLAY_DEBUG_MSG
         Print("Got Canvas_IsNewBar");
      #endif

      if(ArraySize(this.Open) == 0)
      {
         GetOLHC(0,_rates_total);
         this.prev_calculated = 0;
         return true; 
      }

      OLHCShiftRight();
      this.prev_calculated = _prev_calculated; 
      return true;
   }

   IsNewBar = rangeBars.IsNewBar();
   if(IsNewBar)
   {
      GetOLHC(0,_rates_total);
      this.prev_calculated = 0;
      firstRun = true; 
      return true;
   } 
   
   //
   // Only recalculate last bar
   //
   
   GetOLHC(0,0);
   this.prev_calculated = _prev_calculated;

   return true;
}

bool RangeBarIndicator::BufferSynchronizationCheck(const double &buffer[])
{
   if(ArraySize(buffer) != ArraySize(Close))
   {
      #ifdef DEVELOPER_VERSION
         Print("### buffers out of synch - refreshing...");
      #endif
      return false;
   }
   
   return true;
}

int RangeBarIndicator::GetOLHC(int start, int count)
{
   if((start == 0) && (count == 0) && dataReady)
   {
     MqlRates tempRates[1];
     double b[1],s[1],bs[1];
     
     int last = ArraySize(Open)-1;
     
     if(last < 0)
      return 0;
     
     rangeBars.GetMqlRates(tempRates,0,1);
     this.Open[last] = tempRates[0].open;
     this.Low[last] = tempRates[0].low;
     this.High[last] = tempRates[0].high;
     this.Close[last] = tempRates[0].close;    
     
     if(getTime)
     {
         this.Time[last] = tempRates[0].time;
     }
     if(getVolumes)
     {
         this.Tick_volume[last] = tempRates[0].tick_volume;
         this.Real_volume[last] = tempRates[0].real_volume;
     }
     if(useAppliedPrice)
     {
         this.Price[last] = CalcAppliedPrice(tempRates[0],this.applied_price);
     }
     if(getVolumeBreakdown)
     {
        rangeBars.GetBuySellVolumeBreakdown(b,s,bs,0,1);
        this.Buy_volume[last] = b[0];
        this.Sell_volume[last] = s[0];
        this.BuySell_volume[last] = bs[0];        
     }

     return 1;
   }
   else
   {
      return  GetOLHCAndApplPriceForIndicatorCalc(this.Open,this.Low,this.High,this.Close,this.Time,this.Tick_volume,this.Real_volume, this.Buy_volume, this.Sell_volume, this.BuySell_volume, this.Price,this.applied_price,0,count);   
   }
}


void RangeBarIndicator::OLHCShiftRight()
{
   int count = ArraySize(this.Open);

   if(count <= 0)
      return;
   
   count--;
   
   for(int i=count; i>0; i--)
   {
      this.Open[i] = this.Open[i-1];
      this.High[i] = this.High[i-1];
      this.Low[i] = this.Low[i-1];
      this.Close[i] = this.Close[i-1];

      if(getTime)
         this.Time[i] = this.Time[i-1];

      if(useAppliedPrice)
         this.Price[i] = this.Price[i-1];

      if(getVolumes)
      {
         this.Tick_volume[i] = this.Tick_volume[i-1];
         this.Real_volume[i] = this.Real_volume[i-1];         
      }
      if(getVolumeBreakdown)
      {
         this.Buy_volume[i] = this.Buy_volume[i-1];
         this.Sell_volume[i] = this.Sell_volume[i-1];
         this.BuySell_volume[i] = this.BuySell_volume[i-1];
      }
   }
   
   this.Open[0] = 0.0;
   this.High[0] = 0.0;
   this.Low[0] = 0.0;
   this.Close[0] = 0.0;
   
   if(getTime)
      this.Time[0] = 0;

   if(useAppliedPrice)
      this.Price[0] = 0.0;

   if(getVolumes)
   {
      this.Tick_volume[0] = 0.0;
      this.Real_volume[0] = 0.0;         
   }
   if(getVolumeBreakdown)
   {
      this.Buy_volume[0] = 0;
      this.Sell_volume[0] = 0;
      this.BuySell_volume[0] = 0;
   }
}

void RangeBarIndicator::OLHCResize()
{
   int count = ArraySize(this.Open);
   
   if(count <= 0)
      return;
   
   ArrayResize(this.Open,count+1);
   ArrayResize(this.Low,count+1);
   ArrayResize(this.High,count+1);
   ArrayResize(this.Close,count+1);
   
   if(getTime)
      ArrayResize(this.Time,count+1);

   if(useAppliedPrice)
      ArrayResize(this.Price,count+1);

   if(getVolumes)
   {
      ArrayResize(this.Tick_volume,count+1);
      ArrayResize(this.Real_volume,count+1);
   }
   if(getVolumeBreakdown)
   {
      ArrayResize(this.Buy_volume,count+1);
      ArrayResize(this.Sell_volume,count+1);
      ArrayResize(this.BuySell_volume,count+1);
   }
   
   OLHCShiftRight();
}

bool RangeBarIndicator::Canvas_IsNewBar(const datetime &_Time[])
{
   ArraySetAsSeries(_Time,true);
   datetime now = _Time[0]; 
   ArraySetAsSeries(_Time,false);    
   
   if(prevTime != now)
   {
      prevTime = now;
      return true;
   }
   
   return false;
}

bool RangeBarIndicator::Canvas_IsRatesTotalChanged(int ratesTotalNow)
{
   if(prevRatesTotal == 0)
      prevRatesTotal = ratesTotalNow;
         
   if(prevRatesTotal != ratesTotalNow)
   {
      prevRatesTotal = ratesTotalNow;
      return true;
   }
   
   return false;
}

int RangeBarIndicator::Canvas_RatesTotalChangedBy(int ratesTotalNow)
{
   int changedBy = 0;
   
   if(prevRatesTotal == 0)
      prevRatesTotal = ratesTotalNow;
         
   if(prevRatesTotal != ratesTotalNow)
   {
      changedBy = (ratesTotalNow - prevRatesTotal);
      prevRatesTotal = ratesTotalNow;
      return changedBy;
   }
   
   return 0;
}

int RangeBarIndicator::GetOLHCForIndicatorCalc(double &o[],double &l[],double &h[],double &c[],datetime &t[], long &tickVolume[],long &realVolume[], double &buyVolume[], double &sellVolume[], double &buySellVolume[], int start, int count)
{
   int handle;
   double temp[];
   
   if(ArrayResize(temp,count) == -1)
      return -1;
   if(ArrayResize(o,count) == -1)
      return -1;
   if(ArrayResize(l,count) == -1)
      return -1;
   if(ArrayResize(h,count) == -1)
      return -1;
   if(ArrayResize(c,count) == -1)
      return -1;
   
   if(getVolumes)
   {
      if(ArrayResize(tickVolume,count) == -1)
         return -1;
      if(ArrayResize(realVolume,count) == -1)
         return -1;
   }
   
   if(getTime)
   {
      if(ArrayResize(t,count) == -1)
         return -1;
   }
   
   if(getVolumeBreakdown)
   {
      if(ArrayResize(buyVolume,count) == -1)
         return -1;
      if(ArrayResize(sellVolume,count) == -1)
         return -1;
      if(ArrayResize(buySellVolume,count) == -1)
         return -1; 
   }
   
   handle = rangeBars.GetHandle();
   if(handle == INVALID_HANDLE)
      return -1;

   int __count = CopyBuffer(handle,RANGEBAR_OPEN,start,count,temp);
   if(__count == -1)
   {
      if(GetLastError() == ERR_INDICATOR_DATA_NOT_FOUND)
      {
         Print("Waiting for buffers ready flag");
         return -2;
      }
      else
         return -1;
   }   
      
   if(__count < count)
   {
      #ifdef DISPLAY_DEBUG_MSG
         Print("Fixing offset (req:"+count+" res:"+__count+")");
      #endif
      
      ArrayInitialize(o,0x0);
      ArrayInitialize(l,0x0);
      ArrayInitialize(h,0x0);
      ArrayInitialize(c,0x0);
      
      if(getTime)
         ArrayInitialize(t,0x0);
         
      if(getVolumes)
      {
         ArrayInitialize(tickVolume,0x0);
         ArrayInitialize(realVolume,0x0);
      }
      
      if(getVolumeBreakdown)
      {
         ArrayInitialize(buyVolume,0x0);
         ArrayInitialize(sellVolume,0x0);
         ArrayInitialize(buySellVolume,0x0);
      }
      
      // less data - indicator requres more
      
      ArrayCopy(o,temp,(count-__count),0);

      if(CopyBuffer(handle,RANGEBAR_LOW,start,__count,temp) == -1)
         return -1;
      ArrayCopy(l,temp,(count-__count),0);
         
      if(CopyBuffer(handle,RANGEBAR_HIGH,start,__count,temp) == -1)
         return -1;
      ArrayCopy(h,temp,(count-__count),0);

      if(CopyBuffer(handle,RANGEBAR_CLOSE,start,__count,temp) == -1)
         return -1;

      ArrayCopy(c,temp,(count-__count),0);
      
      if(getTime)
      {
         if(CopyBuffer(handle,RANGEBAR_BAR_OPEN_TIME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(t,temp,(count-__count),0);      
      }
      
      if(getVolumes)
      {
         if(CopyBuffer(handle,RANGEBAR_TICK_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(tickVolume,temp,(count-__count),0);
   
         if(CopyBuffer(handle,RANGEBAR_REAL_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(realVolume,temp,(count-__count),0);      
      }
      
#ifdef P_RANGEBAR_BR
   #ifdef P_RANGEBAR_BR_PRO
      if(getVolumeBreakdown)
      {      
         if(CopyBuffer(handle,RANGEBAR_BUY_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(buyVolume,temp,(count-__count),0);

         if(CopyBuffer(handle,RANGEBAR_SELL_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(sellVolume,temp,(count-__count),0);

         if(CopyBuffer(handle,RANGEBAR_BUYSELL_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(buySellVolume,temp,(count-__count),0);
      }
   #else
   #endif
#else      
      if(getVolumeBreakdown)
      {      
         if(CopyBuffer(handle,RANGEBAR_BUY_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(buyVolume,temp,(count-__count),0);

         if(CopyBuffer(handle,RANGEBAR_SELL_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(sellVolume,temp,(count-__count),0);

         if(CopyBuffer(handle,RANGEBAR_BUYSELL_VOLUME,start,__count,temp) == -1)
            return -1;
            
         ArrayCopy(buySellVolume,temp,(count-__count),0);
      }
#endif

   }
   else
   {
      if(CopyBuffer(handle,RANGEBAR_OPEN,start,count,o) == -1)
         return -1;
         
      if(CopyBuffer(handle,RANGEBAR_LOW,start,count,l) == -1)
         return -1;
         
      if(CopyBuffer(handle,RANGEBAR_HIGH,start,count,h) == -1)
         return -1;
         
      if(CopyBuffer(handle,RANGEBAR_CLOSE,start,count,c) == -1)
         return -1;
      
      if(getTime)
      {
         if(CopyBuffer(handle,RANGEBAR_BAR_OPEN_TIME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(t,temp);
      }   
      
      if(getVolumes)
      {
         if(CopyBuffer(handle,RANGEBAR_TICK_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(tickVolume,temp);
         
         if(CopyBuffer(handle,RANGEBAR_REAL_VOLUME,start,count,temp) == -1)
            return -1;   
         
         ArrayCopy(realVolume,temp);
      }
      
#ifdef P_RANGEBAR_BR
   #ifdef P_RANGEBAR_BR_PRO
      if(getVolumeBreakdown)
      {      
         if(CopyBuffer(handle,RANGEBAR_BUY_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(buyVolume,temp);

         if(CopyBuffer(handle,RANGEBAR_SELL_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(sellVolume,temp);

         if(CopyBuffer(handle,RANGEBAR_BUYSELL_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(buySellVolume,temp);
      }
   #else
   #endif
#else
      if(getVolumeBreakdown)
      {      
         if(CopyBuffer(handle,RANGEBAR_BUY_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(buyVolume,temp);

         if(CopyBuffer(handle,RANGEBAR_SELL_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(sellVolume,temp);

         if(CopyBuffer(handle,RANGEBAR_BUYSELL_VOLUME,start,count,temp) == -1)
            return -1;
            
         ArrayCopy(buySellVolume,temp);
      }
#endif      
   }
     
   return count;
}

//
// Get "count" Renko MqlRates into "ratesInfoArray[]" array starting from "start" bar  
//

int RangeBarIndicator::GetOLHCAndApplPriceForIndicatorCalc(double &o[],double &l[],double &h[],double &c[],datetime &t[],long &tickVolume[],long &realVolume[],double &buyVolume[], double &sellVolume[], double &buySellVolume[],double &price[],ENUM_APPLIED_PRICE _applied_price, int start, int count)
{
   dataReady = true;
   
   int __count = GetOLHCForIndicatorCalc(o,l,h,c,t,tickVolume,realVolume,buyVolume,sellVolume,buySellVolume,start,count);
   if(__count < 0)
   {
      dataReady = false;
      return __count;
   }
   if(applied_price == PRICE_CLOSE) 
   {
      return ArrayCopy(price,c);
   }
   else if(applied_price == PRICE_OPEN) 
   {
      return ArrayCopy(price,o);
   }
   else if(applied_price == PRICE_HIGH) 
   {
      return ArrayCopy(price,h);
   }
   else if(applied_price == PRICE_LOW) 
   {
      return ArrayCopy(price,l);
   }
   else
   {       
      if(ArrayResize(price,__count) == -1)
         return -1;      

      for(int i=0; i<__count; i++)
      {
         price[i] = CalcAppliedPrice(o[i],l[i],h[i],c[i],_applied_price);
      }
   }
      
   return __count;
}

// TFMigrate: 
// https://www.mql5.com/en/forum/2842#comment_39496
//
ENUM_TIMEFRAMES RangeBarIndicator::TFMigrate(int tf)
{
   switch(tf)
   {
      case 0: return(PERIOD_CURRENT);
      case 1: return(PERIOD_M1);
      case 5: return(PERIOD_M5);
      case 15: return(PERIOD_M15);
      case 30: return(PERIOD_M30);
      case 60: return(PERIOD_H1);
      case 240: return(PERIOD_H4);
      case 1440: return(PERIOD_D1);
      case 10080: return(PERIOD_W1);
      case 43200: return(PERIOD_MN1);
      
      case 2: return(PERIOD_M2);
      case 3: return(PERIOD_M3);
      case 4: return(PERIOD_M4);      
      case 6: return(PERIOD_M6);
      case 10: return(PERIOD_M10);
      case 12: return(PERIOD_M12);
      case 16385: return(PERIOD_H1);
      case 16386: return(PERIOD_H2);
      case 16387: return(PERIOD_H3);
      case 16388: return(PERIOD_H4);
      case 16390: return(PERIOD_H6);
      case 16392: return(PERIOD_H8);
      case 16396: return(PERIOD_H12);
      case 16408: return(PERIOD_D1);
      case 32769: return(PERIOD_W1);
      case 49153: return(PERIOD_MN1);      

      default: return(PERIOD_CURRENT);
   }
}

datetime RangeBarIndicator::iTime(string symbol,int tf,int index)
{
   if(index < 0)
   {
      return(-1);
   }
   
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   
   datetime Arr[];
   
   if(CopyTime(symbol, timeframe, index, 1, Arr) > 0)
   {
      return(Arr[0]);
   }
   else 
   {
      return(-1);
   }
}

//
//  Function used for calculating the Apllied Price based on Renko OLHC values
//

double RangeBarIndicator::CalcAppliedPrice(const MqlRates &_rates, ENUM_APPLIED_PRICE _applied_price)
{
      if(_applied_price == PRICE_CLOSE)
         return _rates.close;
      else if (_applied_price == PRICE_OPEN)
         return _rates.open;
      else if (_applied_price == PRICE_HIGH)
         return _rates.high;
      else if (_applied_price == PRICE_LOW)
         return _rates.low;
      else if (_applied_price == PRICE_MEDIAN)
         return (_rates.high + _rates.low) / 2;
      else if (_applied_price == PRICE_TYPICAL)
         return (_rates.high + _rates.low + _rates.close) / 3;
      else if (_applied_price == PRICE_WEIGHTED)
         return (_rates.high + _rates.low + _rates.close + _rates.close) / 4;
         
      return 0.0;
}

double RangeBarIndicator::CalcAppliedPrice(const double &o,const double &l,const double &h,const double &c, ENUM_APPLIED_PRICE _applied_price)
{
      if(_applied_price == PRICE_CLOSE)
         return c;
      else if (_applied_price == PRICE_OPEN)
         return o;
      else if (_applied_price == PRICE_HIGH)
         return h;
      else if (_applied_price == PRICE_LOW)
         return l;
      else if (_applied_price == PRICE_MEDIAN)
         return (h + l) / 2;
      else if (_applied_price == PRICE_TYPICAL)
         return (h + l + c) / 3;
      else if (_applied_price == PRICE_WEIGHTED)
         return (h + l + c +c) / 4;
      
      return 0.0;
}

void RangeBarIndicator::BufferShiftLeft(double &buffer[])
{
   int size = ArraySize(buffer);
   
   for(int i=1; i<size; i++)
      buffer[i-1] = buffer[i];

}

long RangeBarIndicator::GetArrayValueLong(long &arr[], int index)
{
   int size  = ArraySize(arr);
   if(index < size)
   {
       return(arr[index]);
   } 
   else    
   {
       return(false); 
   }
}

double RangeBarIndicator::GetArrayValueDouble(double &arr[], int index)
{
   int size  = ArraySize(arr);
   if(index < size)
   {
       return(arr[index]);
   } 
   else    
   {
       return(false); 
   }
}

datetime RangeBarIndicator::GetArrayValueDateTime(datetime &arr[], int index)
{
   int size  = ArraySize(arr);
   if(index < size)
   {
       return(arr[index]);
   } 
   else    
   {
       return(false); 
   }
}

