
Usually tactical asset allocation refers to a group of strategies that switch assets depending on some conditions. Ideally you want to switch from asset A to asset B when you know B is going to outperform A.
It could be switching assets, groups of assets or even strategies.
I coded something inspired by the "Dual Momentum System" of Gary Antonacci. His set up is different than mine: he defines portfolios that should do well in different market conditions and within portfolio he switches between different assets or cash based on the momentum.
Compared to a general momentum strategy, he forces diversification and he can be completely in cash if nothing performs well in each of his groups. That's an interesting idea that I want to explore further but at the moment I just borrow the idea for our work on commodities.
My code for tactical switch looks like that:
def dualMomentum(price,ticks): port=price*0.0 portb=price*0.0 mom=price.pct_change(3.0*20)+price.pct_change(6*20)+price.pct_change(12*20) port=np.sign(mom-mom.max(axis=1))+1 for t in ticks: symbols=t.split() #print symbols port[symbols]=np.sign(mom[symbols]-mom[symbols].max(axis=1))+1 portb[symbols]=portb[symbols]+port[symbols] return portb
price is my usual data frame with prices, ticks is a list of lists, like the one I have for my tickers.
Each list in ticks is a list of instruments to form a given group.
Then the function chooses the one with highest momentum (calculated here as the average of 3, 6 and 12 months returns here but i make it flexible latter) within that group.
The decomposition with port and portb is to be able to have the same security in many groups (cash most likely). Say I have 4 groups, I have a weight of 1/4 on the "best" asset of group one, 1/4 on the best of group two... but if it is the same, it should have a weight of 1/2 up to 1 in cash if nothing in any groups get better returns than cash.
Since we play with futures, we have our collateral in cash in any case so if no instrument does better than 0 we do not take position. I modify it a little for that special case (the bit with sign just makes sure that all weights are 0 if mx is below or equal to 0 at that date:
def dualMomentumFuture(price,ticks): port=price*0.0 portb=price*0.0 mom=price.pct_change(3.0*20)+price.pct_change(6*20)+price.pct_change(12*20) port=np.sign(mom-mom.max(axis=1))+1 for t in ticks: symbols=t.split() mx=mom[symbols].max(axis=1) port[symbols]=(np.sign(mom[symbols]-mx)+1)*(np.sign(mx)*0.5+0.5)*np.sign(mx) portb[symbols]=portb[symbols]+port[symbols] return portb/len(ticks)
This is how I test the idea in my main program:
port=dualMomentumFuture(fprice,ticks) (port_rets,port_rets_d)=generatePL(port) results['Switcher']= port_rets results_d['Switcher']= port_rets_d
I just have to modify the way I read my data from my data base because when I read the ticker CL, I stored it in my dataframe as CL1. I changed that in the reading loop.
Results are not bad: I get a Sharpe of 0.64 using monthly data, compared to a uniform with a Sharpe of 0.16. My maximum drawdown over the period is 50% for the uniform and 33% for the switcher. Diversification is good but holding only the best performing commodities seems better.
What would it be if I put all the futures together and switch to the best using that system (no more subgroups)? I get a Sharpe of 0.37 and a max drawdown of 64%. So there is benefit in diversification after all. This would be similar to the momentum strategy we talked about in the previous post but my top group would contain only one future and I don't go short my bottom group.
We could apply this to strategies themselves: given a basket of strategy, switch to the best performing one. Either we use the same criteria we can switch based on a recent estimate of the Sharpe ratio.
I will test the later with the uniform weight strategy: given the past mean and variance of returns, stay in the strategy or switch to cash:
def dualMomentumTactic(price,symbols,lookback): port=price*0.0 portb=price*0.0 mom=price.pct_change(lookback) vol=pd.rolling_std(price,lookback) mom=mom/vol port=np.sign(mom-mom.max(axis=1))+1 mx=mom[symbols].max(axis=1) port[symbols]=(np.sign(mom[symbols]-mom[symbols].max(axis=1))+1)*(np.sign(mx)*0.5+0.5)*np.sign(mx) portb[symbols]=portb[symbols]+port[symbols] return portb
I need to modify a bit the generatePL function because now it uses the returns of the strategies themselves and not the futures:
def generatePLTactic(port): #when we trade portfolio of strategies #costs are assumed to be priced in the stretegies themselves, switch is costless port = port.fillna(0.0) port = port.shift(1).resample(freq,how='first') #make sure the sum of portfolio weights is 1, fully invested, with full collateral port = port/((np.abs(port)).sum(axis=1)) #there is no collateral here as it is already in the base strategies port_rets = (port * results).sum(axis=1) #generate daily weigths for m2m port2 = port.shift(-1).resample('D').shift().fillna(method='ffill').resample('B',how='first') port_rets2 = (port2*strat_cumrets_d).sum(axis=1) port_rets2 = port_rets2.fillna(0.0) port_rets_d = (1+port_rets2).pct_change() dr = port_rets_d.resample(freq).index[:-1]+BDay() port_rets_d[dr] = (port2.loc[dr]*results_d.loc[dr]).sum(axis=1)+collat_d.loc[dr] return(port_rets,port_rets_d)
I get the results of the back test by doing:
strat_cumrets_d=results_d.groupby( [TradeId_d]).apply(lambda x:(1.0+x).cumprod()-1.0) strats=['Uniform','Risk free'] prices=results_d.apply(lambda x:(1.0+x).cumprod()) port=dualMomentumTactic(prices,strats,12*20) (port_rets,port_rets_d)=generatePLTactic(port) results['Tactic']= port_rets results_d['Tactic']= port_rets_d
So the system switched between the uniform monthly rebalanced long only and cash based on the risk adjusted return of the past year (12*20 business days).
The results gives a Sharpe Ratio of 0.30 (instead of 0.16) and a max drawdown of 15% instead of 50%.
Just to be clear, all these metrics are not stellar. A SR of 0.30 is not very good but this show how a simple overlay that just decides if the strategy should be on or off can double the SR and divide by more than 3 the drawdown of a given strategy.
Now let see if I apply this to a group of three: cash, 30 day momentum and basis strategy.
-If I switch based on the Sharpe ratio, it stays in cash all the time.
-I can make it decide between only the two active strategies. It seems to stay always invested in the basis strategy. That makes sens it has the best Sharpe ratio overall, probably anytime as well.
-I can change and make it switch based on another metric, momentum for example. It gives a slight improvement, the switching strategy gets a SR of 0.77 compared to 0.73 using only the basis strategy.
That depends on the period I use to asses the momentum. Generally speaking switching in this case doesn't really boost the results. We might need for that more strategies with clearer regimes of good/bad performances.
Aucun commentaire:
Enregistrer un commentaire