Rebalancing - Conservative Formula
-
Hi,
I am trying to replicate the strategy published in the article - 'Rebalancing - Conservative Formula' with my own data. However, ranks and final portfolio values are coming as nan . Appreciate any help!! below is the code for your referenceclass Strategy(bt.Strategy): 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=0.05 # 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) # 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 = (1.0 - 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] # 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: m / v for d, v, m in zip(self.datas, vs, ms)} def next(self): # sort data and current rank ranks = sorted( self.ranks.items(), # get the (d, rank), pair key=lambda x: x[1][0], # use rank (elem 1) and current time "0" reverse=True, # highest ranked 1st ... please ) # put top ranked in dict with data as key to test for presence rtop = dict(ranks[:self.selnum]) # For logging purposes of stocks leaving the portfolio rbot = dict(ranks[self.selnum:]) # prepare quick lookup list of stocks currently holding a position posdata = [d for d, pos in self.getpositions().items() if pos] # remove those no longer top ranked # do this first to issue sell orders and free cash for d in (d for d in posdata if d not in rtop): self.log('Leave {} - Rank {:.2f}'.format(d._name, rbot[d][0])) self.order_target_percent(d, target=0.0) # rebalance those already top ranked and still there for d in (d for d in posdata if d in rtop): self.log('Rebal {} - Rank {:.2f}'.format(d._name, rtop[d][0])) self.order_target_percent(d, target=self.perctarget) del rtop[d] # remove it, to simplify next iteration # issue a target order for the newly top ranked stocks # do this last, as this will generate buy orders consuming cash for d in rtop: self.log('Enter {} - Rank {:.2f}'.format(d._name, rtop[d][0])) self.order_target_percent(d, target=self.perctarget)
cerebro = bt.Cerebro() cerebro.broker.setcash(300000.0) cerebro.broker.setcommission(commission=0.001) # Add securities as datas: for ticker, data in prices.groupby(level=0): if ticker in TICKERS: print(f"Adding ticker: {ticker}") data = bt.feeds.PandasData(dataname=data.droplevel(level=0), name=ticker, plot=False) cerebro.adddata(data)
Adding ticker: ACC.NS
Adding ticker: ADANIPORTS.NS
Adding ticker: AMBUJACEM.NS
Adding ticker: ASHOKLEY.NS
Adding ticker: ASIANPAINT.NS
Adding ticker: AUROPHARMA.NS
Adding ticker: AXISBANK.NS
Adding ticker: BAJAJ-AUTO.NS
Adding ticker: BAJAJFINSV.NS
.....cerebro.addstrategy(Strategy, selcperc=0.20, vperiod=36, mperiod=6) cerebro.addobserver(bt.observers.BuySell) cerebro.addobserver(bt.observers.Value) cerebro.addanalyzer(bt.analyzers.Returns, _name='returns') cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='time_return') cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio') print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) # Run the strategy. Results will be output from stop. results = cerebro.run(stdstats=True, tradehistory=False) # # Run over everything # results = cerebro.run(maxcpus=1) # Print out the final result print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
Starting Portfolio Value: 300000.00
2017-01-31 Enter ACC.NS - Rank nan
2017-01-31 Enter ADANIPORTS.NS - Rank nan
2017-01-31 Enter AMBUJACEM.NS - Rank nan
2017-01-31 Enter ASHOKLEY.NS - Rank nan
2017-01-31 Enter ASIANPAINT.NS - Rank nan
2017-01-31 Enter AUROPHARMA.NS - Rank nan
2017-01-31 Enter AXISBANK.NS - Rank nan
2017-01-31 Enter BAJAJ-AUTO.NS - Rank nan
2017-01-31 Enter BAJAJFINSV.NS - Rank nan
2017-01-31 Enter BAJAJHLDNG.NS - Rank nan
2017-01-31 Enter BAJFINANCE.NS - Rank nan
2017-01-31 Enter BANKBARODA.NS - Rank nan
2017-01-31 Enter BERGEPAINT.NS - Rank nan
2017-01-31 Enter BHARTIARTL.NS - Rank nan
2017-01-31 Enter BIOCON.NS - Rank nan
2017-01-31 Enter BOSCHLTD.NS - Rank nan
2017-01-31 Enter BPCL.NS - Rank nan
2017-01-31 Enter BRITANNIA.NS - Rank nan
2017-02-28 Rebal ACC.NS - Rank nan
2017-02-28 Rebal ADANIPORTS.NS - Rank nan
2017-02-28 Rebal AMBUJACEM.NS - Rank nan
2017-02-28 Rebal ASHOKLEY.NS - Rank nan
2017-02-28 Rebal ASIANPAINT.NS - Rank nan
......
Final Portfolio Value: nan -
It looks like a data issue, I get this when I change the frequency from daily to monthly.
-
@Sabir-Jana I'm curious if someone has an answer because I couldn't see error in your code.
-
I did not went through the code, but check maybe if the analyser use a timeframe value.
-
There is no issue with the code. Data wasn't clean hence causing the issue. Once I run the code with clean data, it worked.
Thanks