Open KarlTrader opened 2 years ago
To further illustrate, consider this example:
date_idx = pd.bdate_range(start='2020-01-01', end='2021-12-28') # %4 !
uptrend_data = pd.DataFrame({
'up1': np.linspace(0, 50, len(date_idx)),
'up2': np.linspace(0, 100, len(date_idx)),
}, index=date_idx)
sma_fast = uptrend_data.rolling(5).mean()
sma_slow = uptrend_data.rolling(10).mean()
strat = bt.Strategy('pick_one_but_get_two', [
bt.algos.RunMonthly(),
bt.algos.SelectAll(),
bt.algos.SelectMomentum(n=1),
bt.algos.SelectWhere(sma_fast > sma_slow),
bt.algos.WeighEqually(),
bt.algos.Rebalance(),
])
res = bt.run(bt.Backtest(strat, uptrend_data))
res.get_security_weights().tail().to_csv(sys.stdout)
I'd expect to see 0.5 allocated to up2 and the rest in cash. However:
,up1,up2
2021-12-22,0.49999998278989316,0.49999998278989316
2021-12-23,0.4999999828232461,0.4999999828232461
2021-12-24,0.49999998285647,0.49999998285647
2021-12-27,0.4999999828895656,0.4999999828895656
2021-12-28,0.49999998292253367,0.49999998292253367
/home/matt/anaconda3/envs/an39/lib/python3.9/site-packages/ffn/core.py:2299: RuntimeWarning: divide by zero encountered in true_divide
res = np.divide(er.mean(), std)
/home/matt/anaconda3/envs/an39/lib/python3.9/site-packages/ffn/core.py:258: RuntimeWarning: divide by zero encountered in true_divide
self.calmar = np.divide(self.cagr, np.abs(self.max_drawdown))
/home/matt/anaconda3/envs/an39/lib/python3.9/site-packages/ffn/core.py:2299: RuntimeWarning: divide by zero encountered in true_divide
res = np.divide(er.mean(), std)
/home/matt/anaconda3/envs/an39/lib/python3.9/site-packages/ffn/core.py:258: RuntimeWarning: divide by zero encountered in true_divide
self.calmar = np.divide(self.cagr, np.abs(self.max_drawdown))
If I reverse the Select algos, I get problems in a downtrend
date_idx = pd.bdate_range(start='2020-01-01', end='2021-12-28') # %4 !
downtrend_data = pd.DataFrame({
'down1': np.concatenate([
np.linspace(0, 50, len(date_idx)//2),
np.linspace(50, 0, len(date_idx)//2),
]),
'down2': np.concatenate([
np.linspace(0, 100, len(date_idx)//2),
np.linspace(100, 0, len(date_idx)//2),
]),
}, index=date_idx)
sma_fast = downtrend_data.rolling(5).mean()
sma_slow = downtrend_data.rolling(10).mean()
strat = bt.Strategy('filter_all_but_get_one', [
bt.algos.RunMonthly(),
bt.algos.SelectAll(),
bt.algos.SelectWhere(sma_fast > sma_slow),
bt.algos.SelectMomentum(n=1),
bt.algos.WeighEqually(),
bt.algos.Rebalance(),
])
res = bt.run(bt.Backtest(strat, downtrend_data))
res.get_security_weights().tail().to_csv(sys.stdout)
Not allocating anything at the end is correct, but 'down2' is completely ignored. And that has the stronger trend and should be selected in phase, shouldn't it?
,down1
2021-12-22,0.0
2021-12-23,0.0
2021-12-24,0.0
2021-12-27,0.0
2021-12-28,0.0
Hi, I am trying to implement Faber's QTAA paper in the bt framework. For reference: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=962461 The main challenge I have run into is the additional trend filter from the aggressive approach:
I have tried the following:
Do I need more sophisticated techniques to get such an extra filter? Or should I try and incorporate the filter into the momentum score, maybe by explicitly adding cash instruments to the selection?