Renko bars calculated with ATR?
-
On TradingView, the Renko bars comes with 2 settings for the brick size, ATR or Traditional. This https://www.backtrader.com/blog/posts/2017-06-26-renko-bricks/renko-bricks/ works well for Traditional where you can just set the size, but I am having trouble getting the ATR calculation into the Filter. I'm not sure how to get an Indicator for use in a Filter and have it update those values on the fly, and also wait enough periods for them to be calculated. I attempted to convert it to a Pandas dataframe and go from there, but it's just a mess and doesn't work properly as it creates gaps between all the bars, haha.
def bt_to_pd(btdata, len): get = lambda mydata: mydata.get(ago=0, size=len) fields = { 'open': get(btdata.open), 'high': get(btdata.high), 'low': get(btdata.low), 'close': get(btdata.close), 'volume': get(btdata.volume) } time = [btdata.num2date(x) for x in get(btdata.datetime)] return pd.DataFrame(data=fields, index=time) class Renko(bt.Filter): '''Modify the data stream to draw Renko bars (or bricks) Params: - ``hilo`` (default: *False*) Use high and low instead of close to decide if a new brick is needed - ``size`` (default: *None*) The size to consider for each brick - ``autosize`` (default: *20.0*) If *size* is *None*, this will be used to autocalculate the size of the bricks (simply dividing the current price by the given value) - ``dynamic`` (default: *False*) If *True* and using *autosize*, the size of the bricks will be recalculated when moving to a new brick. This will of course eliminate the perfect alignment of Renko bricks. - ``align`` (default: *1.0*) Factor use to align the price boundaries of the bricks. If the price is for example *3563.25* and *align* is *10.0*, the resulting aligned price will be *3560*. The calculation: - 3563.25 / 10.0 = 356.325 - round it and remove the decimals -> 356 - 356 * 10.0 -> 3560 See: - http://stockcharts.com/school/doku.php?id=chart_school:chart_analysis:renko ''' params = ( ('hilo', False), ('size', None), ('autosize', 20.0), ('dynamic', False), ('align', 1.0), ('atr', 0) ) def nextstart(self, data): if not self.p.atr: o = data.open[0] self.o = round(o / self.p.align, 0) * self.p.align # aligned self._size = self.p.size or float(o // self.p.autosize) self._top = int(o) + self._size self._bot = int(o) - self._size else: self._size = self.p.size def next(self, data): if not self._size and self.p.atr: self.df = bt_to_pd(data, len(data)) self.df['ATR'] = ATR(self.df, n=self.p.atr) ATR_val = self.df['ATR'][-1] if not np.isnan(ATR_val): self._size = self.init_ATR = ATR_val print('init_ATR', self._size) if not self._size: return False if self.p.atr: o = data.open[0] self.o = round(o / self.p.align, 0) * self.p.align # aligned self._top = int(self.o) + self._size self._bot = int(self.o) - self._size c = data.close[0] h = data.high[0] l = data.low[0] if self.p.hilo: hiprice = h loprice = l else: hiprice = loprice = c if hiprice >= self._top: # deliver a renko brick from top -> top + size self._bot = bot = self._top if self.p.size is None and self.p.dynamic: self._size = float(c // self.p.autosize) top = bot + self._size top = round(top / self.p.align, 0) * self.p.align # aligned else: top = bot + self._size self._top = top data.open[0] = bot data.low[0] = bot data.high[0] = top data.close[0] = top data.volume[0] = 0.0 data.openinterest[0] = 0.0 return False # length of data stream is unaltered elif loprice <= self._bot: # deliver a renko brick from bot -> bot - size self._top = top = self._bot if self.p.size is None and self.p.dynamic: self._size = float(c // self.p.autosize) bot = top - self._size bot = round(bot / self.p.align, 0) * self.p.align # aligned else: bot = top - self._size self._bot = bot data.open[0] = top data.low[0] = top data.high[0] = bot data.close[0] = bot data.volume[0] = 0.0 data.openinterest[0] = 0.0 return False # length of data stream is unaltered data.backwards() return True # length of stream was changed, get new bar
I hope there is an easier way and any help is appreciated! Thanks!
-
Used this ATR function btw because it didn't seem like bt.ind.ATR() would take the data in next(self, data).
def ATR(DF, n=14): df = DF.copy() df['H-L'] = abs(df['high'] - df['low']) df['H-PC'] = abs(df['high'] - df['close'].shift(1)) df['L-PC'] = abs(df['low'] - df['close'].shift(1)) df['TR'] = df[['H-L', 'H-PC', 'L-PC']].max(axis=1, skipna=False) df['ATR'] = df['TR'].ewm(com=n,min_periods=n).mean() return df['ATR']