The New 60/40

August 6, 2012
By

(This article was first published on Systematic Investor » R, and kindly contributed to R-bloggers)

I want to share a brilliant idea and a great example from the You’re Looking at the Wrong Number post at the GestaltU blog. Today, I will focus on the section of this post that outlines simple steps to improve a typical 60/40 stock/bond portfolio by using risk allocation instead of dollar allocation, and targeting constant portfolio volatility.

I will use SPY (S&P 500) as a proxy for a stock allocation and TLT (20 Year Treasuries) as a proxy for a bond allocation. Let’s start by loading historical prices for SPY and a few fixed income ETF’s using the Systematic Investor Toolbox:

###############################################################################
# Load Systematic Investor Toolbox (SIT)
# http://systematicinvestor.wordpress.com/systematic-investor-toolbox/
###############################################################################
setInternet2(TRUE)
con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb'))
    source(con)
close(con)

	#*****************************************************************
	# Load historical data
	#****************************************************************** 
	load.packages('quantmod')	
	tickers = spl('SHY,IEF,TLT,SPY')

	data.all <- new.env()
	getSymbols(tickers, src = 'yahoo', from = '1990-01-01', env = data.all, auto.assign = T)	
	for(i in ls(data.all)) data.all[[i]] = adjustOHLC(data.all[[i]], use.Adjusted=T)		
	bt.prep(data.all, align='remove.na')
	
	prices = data.all$prices
		n = ncol(prices)
		nperiods = nrow(prices)

	# normalize all prices and plot them
	prices = prices/ matrix(first(prices), nr=nperiods, nc=n, byrow=T)
	plota.matplot(prices)

The fixed income ETFs are not as safe as you might have been expected. The SHY (Barclays 1-3 Year Treasury Bond Fund) is consistently up with out large draw-downs. However, both IEF (Barclays 7-10 Year Treasury Bond Fund) and TLT (Barclays 20 Year Treasury Bond Fund) do go through periods of loosing money.

Next let’s build a traditional dollar weighted 60/40 stock/bond portfolio and risk weighted version:

	#*****************************************************************
	# Load historical data
	#****************************************************************** 		
	data <- new.env()
		data$stock = data.all$SPY
		data$bond = data.all$TLT	
	bt.prep(data, align='remove.na')

	#*****************************************************************
	# Code Strategies
	#****************************************************************** 
	# all bonds began trading at 2002-07-31
	prices = data$prices
		n = ncol(prices)
		nperiods = nrow(prices)
	
	models = list()
	
	period.ends = endpoints(prices, 'months')
		period.ends = period.ends[period.ends > 0]
	
	#*****************************************************************
	# Traditional, Dollar Weighted 40% Bonds & 60% Stock
	#****************************************************************** 			
	weight.dollar = matrix(c(0.4, 0.6), nr=nperiods, nc=n, byrow=T)
	
	data$weight[] = NA
		data$weight[period.ends,] = weight.dollar[period.ends,]
	models$dollar.w.60.40 = bt.run.share(data, clean.signal=F)

	#*****************************************************************
	# Risk Weighted 40% Bonds & 60% Stock
	#****************************************************************** 				
	ret.log = bt.apply.matrix(prices, ROC, type='continuous')
	hist.vol = sqrt(252) * bt.apply.matrix(ret.log, runSD, n = 21)	
	weight.risk = weight.dollar / hist.vol
		weight.risk = weight.risk / rowSums(weight.risk)
		
	data$weight[] = NA
		data$weight[period.ends,] = weight.risk[period.ends,]
	models$risk.w.60.40 = bt.run.share(data, clean.signal=F)

Next let’s create a risk weighted portfolio with 6% volatility. I will adjust portfolio leverage up/down based on the one month historical volatility to target 6% annual volatility.

	#*****************************************************************
	# Helper function to adjust portfolio leverage to target given volatility
	#****************************************************************** 				
	target.vol.strategy <- function(model, weight, 
		target = 10/100, 
		lookback.len = 21,
		max.portfolio.leverage = 100/100) 
	{	
		ret.log.model = ROC(model$equity, type='continuous')
		hist.vol.model = sqrt(252) * runSD(ret.log.model, n = lookback.len)	
			hist.vol.model = as.vector(hist.vol.model)
		
		weight.target = weight * (target / hist.vol.model)
	
		# limit total leverage		
		rs = rowSums(abs(weight.target))
		weight.target = weight.target / iif(rs > max.portfolio.leverage, rs/max.portfolio.leverage, 1)		
		
		return(weight.target)	
	}
				
	#*****************************************************************
	# Scale Risk Weighted 40% Bonds & 60% Stock strategy to have 6% volatility
	#****************************************************************** 				
	data$weight[] = NA
		data$weight[period.ends,] = target.vol.strategy(models$risk.w.60.40,
						weight.risk, 6/100, 21, 100/100)[period.ends,]
	models$risk.w.60.40.target6 = bt.run.share(data, clean.signal=T)

In the last step, I want to invest portfolio cash position into short-term treasuries (SHY) to improve overall performance and create summary reports:

	#*****************************************************************
	# Same, plus invest cash into SHY
	#****************************************************************** 					
	weight = target.vol.strategy(models$risk.w.60.40, weight.risk, 6/100, 21, 100/100)
	data.all$weight[] = NA
		data.all$weight$SPY[period.ends,] = weight$stock[period.ends,]
		data.all$weight$TLT[period.ends,] = weight$bond[period.ends,]
		
		cash = 1-rowSums(weight)
		data.all$weight$SHY[period.ends,] = cash[period.ends]
	models$risk.w.60.40.target6.cash = bt.run.share(data.all, clean.signal=T)

	#*****************************************************************
	# Create Report
	#****************************************************************** 	
	plotbt.strategy.sidebyside(models)
	
	plotbt.custom.report.part1(models)
		
	plotbt.custom.report.part2(models$risk.w.60.40.target6)		
	plotbt.custom.report.part2(models$risk.w.60.40.target6.cash)		

I’m very happy with results: the returns went down a bit, but portfolio risk adjusted performance is up and draw-downs went down from 30% to 12%. The consistent returns are especially important for a retirement portfolio because it allows you to better plan how much money you can withdraw and sustain your fund for a longer term.

To view the complete source code for this example, please have a look at the bt.new.60.40.test() function in bt.test.r at github.


To leave a comment for the author, please follow the link and comment on his blog: Systematic Investor » R.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Tags: , , ,

Comments are closed.