#property copyright "Copyright 2017, AZ-iNVEST" #property link "http://www.az-invest.eu" #property version "2.02" input bool UseOnRangeBarChart = true; // Use this indicator on RangeBar chart #include 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 dataReady; 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[]; 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[]); int GetPrevCalculated() { return prev_calculated; }; 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); }; RangeBarIndicator::RangeBarIndicator(void) { rangeBars = new RangeBars(UseOnRangeBarChart); if(rangeBars != NULL) rangeBars.Init(); useAppliedPrice = false; getVolumes = false; getTime = false; dataReady = false; } 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[]) { static bool firstRun = true; 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; NeedsReload(); } if(NeedsReload() || !this.dataReady) { GetOLHC(0,_rates_total); this.prev_calculated = 0; if(NeedsReload() || !this.dataReady) { Print("NeedsReload/DataReady block failed"); return false; } } /* if(needsReload || IsNewBar || canvasIsNewTime || (change != 0)) { Print("reload="+needsReload+", renkoisnewbar="+IsNewBar+", canvasIsNewTime="+canvasIsNewTime+", change="+change); GetOLHC(0,_rates_total); this.prev_calculated = ArraySize(this.Open); return true; } */ 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;//_prev_calculated; 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; ///////// false } OLHCShiftRight(); this.prev_calculated = _prev_calculated; return true; } IsNewBar = rangeBars.IsNewBar(); if(IsNewBar) { GetOLHC(0,_rates_total); this.prev_calculated = 0; return true; } // // Only recalculate last bar // GetOLHC(0,0); this.prev_calculated = _prev_calculated; 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); static datetime prevTime = 0; if(prevTime != now) { prevTime = now; return true; } return false; } bool RangeBarIndicator::Canvas_IsRatesTotalChanged(int ratesTotalNow) { static int prevRatesTotal = 0; if(prevRatesTotal == 0) prevRatesTotal = ratesTotalNow; if(prevRatesTotal != ratesTotalNow) { prevRatesTotal = ratesTotalNow; return true; } return false; } int RangeBarIndicator::Canvas_RatesTotalChangedBy(int ratesTotalNow) { int changedBy = 0; static int prevRatesTotal = 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) { int errorCode = GetLastError(); if(errorCode == 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; } 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