Volume in multidata
-
Hi All,
I am facing issue to access for multi-data, please refer the code below:
params = dict( selcperc=0.10, # percentage of stocks to select from the universe rperiod=1, # period for the returns calculation, default 1 period vperiod=36, # lookback period for volatility - default 36 periods mperiod=12, # lookback period for momentum - default 12 periods reserve=5, # 5% reserve capital ) def log(self, arg): print('{} {}'.format(self.datetime.date(), arg)) def __init__(self): # calculate 1st the amount of stocks that will be selected self.selnum = int(len(self.datas) * self.p.selcperc) print(int(len(self.datas) * self.p.selcperc)) # allocation perc per stock # reserve kept to make sure orders are not rejected due to # margin. Prices are calculated when known (close), but orders can only # be executed next day (opening price). Price can gap upwards self.perctarget = (100 - self.p.reserve) / self.selnum print((100 - self.p.reserve) / self.selnum) # returns, volatilities and momentums rs = [bt.ind.PctChange(d, period=self.p.rperiod) for d in self.datas] vs = [bt.ind.StdDev(ret, period=self.p.vperiod) for ret in rs] ms = [bt.ind.ROC(d, period=self.p.mperiod) for d in self.datas] volume = [bt.indicators.MovingAverageSimple(d, period = 2) for d in self.datas[0].volume] # simple rank formula: (momentum * net payout) / volatility # the highest ranked: low vol, large momentum, large payout # self.ranks = {d: d.npy * m / v for d, v, m in zip(self.datas, vs, ms)} self.ranks = {d: bt.DivByZero(m, v) for d, v, m, volu in zip(self.datas, vs, ms, volume) if volu > 100000}
Above is giving error:
self.ranks = {d: bt.DivByZero(m, v) for d, v, m, volu in zip(self.datas, vs, ms, volume) if volu > 100000} TypeError: __bool__ should return bool, returned LineOwnOperation
I am not an expert, any pointers to fix the issue?
-
volu refers to volume which is I think list comprehenion of SMA indicator objects. You won't be able to compare each value with a scalar.
-
thanks @run-out , is there any other way? I am trying to filter out stocks whose average volume is greater than certain value.
-
@gten Yes, you create an dictionary to keep track of your indicators for each datas you have put into the backtest.
Then in init you loop through the datas to create your indicator. If you use > you will have a line of binary 1 or 0. You can then call these values at each 'next' to determine the truth value an any bar of your data volume being greater than the threshold. Here is the sample code:
import backtrader as bt import yfinance as yf class Strategy(bt.Strategy): def log(self, txt, dt=None): """ Logging function fot this strategy""" dt = dt or self.data.datetime[0] if isinstance(dt, float): dt = bt.num2date(dt) print("%s, %s" % (dt, txt)) def __init__(self): self.sma_dict = {} self.p.volume_threshold = 15000000 # Average volume over threshold for d in self.datas: self.sma_dict[d] = bt.ind.SMA(d.volume, period=30) > self.p.volume_threshold def next(self): for d in self.datas: self.log(f"{d._name},\tvolume: {int(d.volume[0]):10,d},\tave_volume_gt: {int(self.sma_dict[d][0]):2d}") print("\n") return def runstrategy(): cerebro = bt.Cerebro() fromdate = "2020-01-15" todate = "2020-06-19" interval = "1d" data0 = bt.feeds.PandasData( dataname=yf.download( "AAPL", start=fromdate, stop=todate, interval=interval ), name="AAPL", ) cerebro.adddata(data0) data1 = bt.feeds.PandasData( dataname=yf.download( "MSFT", start=fromdate, stop=todate, interval=interval ), name="MSFT", ) cerebro.adddata(data1) data2 = bt.feeds.PandasData( dataname=yf.download( "TSLA", start=fromdate, stop=todate, interval=interval ), name="TSLA", ) cerebro.adddata(data2) cerebro.addstrategy(Strategy) cerebro.run() if __name__ == "__main__": runstrategy()
-
Thank you @run-out, makes sense.