Using R to model the classic 60/40 investing rule

[This article was first published on quandl blog » R quandl blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Treelife by Timothy Poulton

Image by Timothy Poulton

A long-standing paradigm among savers and investors is to favor a mixture of 40% bonds and 60% equities. The simple rationale is that stocks will provide greater returns while bonds will serve as a diversifier when if equities fall. If you are saving for your pension, you probably heard this story before, but do you believe it?

At least in part, this makes sense. Stocks are more volatile and thus should yield more as compensation. Regarding diversification, we can take a stab at it and try to model the correlation between stocks and bonds, but for now let’s assume it holds that bonds will ‘defend’ us during crisis. Today we zoom in on the pain this 60/40 mixture can cause you over the years, and compare it to other alternatives. We use numbers from the last two decades to show that you may want to reconsider this common paradigm.

In general we use volatility as a measure for risk, and we use the Sharpe Ratio (mean return over standard deviation of returns) as a measure for performance of the portfolio. The Sharpe Ratio (SR) is basically how much return you get for one unit of risk you endure, the higher the SR the better. Note that you can get high SR with very low mean returns. Some savers will consciously prefer lower SR in order to get higher returns so it is also about risk preferences. Still, it is common to consider both mean (return) and standard deviation (risk) when allocating money.

For the analysis, we shall use total return indices. Just using the S&P returns does not account for dividends, and this has a real impact when we look at a long time-span. I would like to use the R-software for the analysis. Total return indices are not easily available from usual sources like the quantmod package, so I use Quandl for it.

In the ‘search’ box, type for example: “bond total return index” and choose the series you want to work with. In order to load the chosen series to your software using the API, you can use the token (which is just a serial number) attached to your Quandl account, you can find yours here if you’re signed in:

library(Quandl)                 # Quandl package
Quandl.auth('here the token copied from the tab')
AAA <- Quandl("ML/AAATRI",start_date="1990-01-01",end_date="2012-12-31",
TR <- Quandl("SANDP/MONRETS",start_date="1990-01-01",end_date="2012-12-31" )
# [1] "data.frame"
# [1] "numeric"
# [1] "Date"

As you can see, the data comes in the correct format. Columns which are quotes are class numeric and the date vector is class Date. You have the "collapse" argument for getting your preferred frequency and some other arguments, like "sort" which will reverse the chronological order. To view the full list of options type '?Quandl' in the console.

What is loaded is the AAA index managed by Merrill Lynch and the total returns on the S&P. Total return means that we invest back any payments (such as dividends) we get from the companies. Let's look at the series:

Time <- rev(TR[,1]) 
TR <- apply(TR[-1],2,rev) 
# We now pretend as if we invested 1 dollar in 1990.
bond <- rev(AAA[,2])/tail(AAA[,2],1) 
stock <- NULL
for (i in 1:NROW(TR)){
stock[i] <- cumsum(prod((1+TR[1:i,11])))
# Graphical Parameters:
stockframe = data.frame(value=stock,Date=Time)
bondframe = data.frame(value=bond,Date=Time)
line.plot <- ggplot() +
geom_line(data=stockframe, aes(x=Date, y=value, colour="Stocks")) +
geom_line(data=bondframe, aes(x=Date, y=value, colour="Bonds")) +
scale_colour_manual("", breaks = c("Stocks", "Bonds"), values = c("#29779f","#d8593b")) +
theme(panel.background = element_rect(fill='#FFFFFF'), panel.grid.major.x = element_blank(), panel.grid.major.y = element_line(colour='#3a4047', size=0.1), panel.grid.minor = element_line(colour='#3a4047', size=0.1)) +
xlab("Date") + ylab("Returns") + ggtitle("Stocks and Bonds")


We see that over time stocks deliver higher returns than bonds, which is what we expect given their higher volatility. However, note that there are 'snapshots' in time where investing the one dollar would yield the almost the same return whether it is invested in stocks or bonds. Specifically, investing one dollar in 1990 would pay back 3.5 dollar in 2008.

Let us now dissect this graph and have a look at the yearly performance. We can easily compute yearly returns if we convert the series to another class called ts (time series).

# We convert to time series so later we can compute yearly returns.
AAAA <- ts(rev(AAA[,2]),start=c(1990,1),end=c(2012,1),frequency=12)
stockk <- xts(stock,
yretb <- yearlyReturn(AAAA)
yrets <- yearlyReturn(stockk)
namarg <- substr(index(yrets),1,4)
df = data.frame(Date=namarg, value=as.numeric(yrets)*100)
df2 = data.frame(Date=namarg, value=as.numeric(yretb)*100)
bar.plot <- ggplot() +
geom_bar(stat='identity', data=df, aes(x=Date, y=value, fill="Stocks")) +
geom_bar(stat='identity', data=df2, aes(x=Date, y=value, fill="Bonds")) + 
scale_fill_manual("", breaks = c("Stocks", "Bonds"), values = c("#29779f","#d8593b")) +
coord_flip() +
theme(panel.background = element_rect(fill='#FFFFFF'), panel.grid.major.x = element_blank(), panel.grid.major.y = element_line(colour='#3a4047', size=0.1), panel.grid.minor = element_line(colour='#3a4047', size=0.1)) +
xlab("Date") + ylab("%") + ggtitle("Yearly Returns")


These are the yearly return of stocks (blue) and bonds (orange). We see that bonds will never realize those returns sometimes achieved by equities but they are more stable, they are more stable by a large margin. This stability in bond's returns translates to higher sharp ratio.

Let us now check on a yearly basis, what is the SR for different portfolio decomposition, 20-80, 40-60 etc.:

TT <- length(yrets)
bondweight <- c(.2,.4,.6,.8)
stockweight <- 1-bondweight
Sharp <- m <- v <- NULL
st = 1 # 19 = 2008
for (j in 1:l){
PortRet <- as.numeric(bondweight[j]*yretb[st:TT]) + as.numeric(stockweight[j]*yrets[st:TT])
# you can play around with removing bad equity years.
#PortRet <- as.numeric(bondweight[j]*yretb[-st]) + as.numeric(stockweight[j]*yrets[-st])
m[j] <- mean(PortRet)
v[j] <-sd(PortRet)
Sharp[j] <- m[j]/v[j]
# 0.666 0.804 1.014 1.260

So, the higher the bond weighting (defined here by the AAA), the higher the SR. Of course, lower absolute returns, but higher returns per unit of variance. SR for 100% bonds is 1.26 which is the highest among the split considered. According to this, if you very much dislike the variance, you should invest fully in bonds and forget about equities. Apart from that, given the first figure it is also very important to realize that you can encounter these 'snapshots' when you basically get nothing for the extra risk you take. You can only hope you are not close to one of these 'snapshots' right before you need your savings back. 60-40 split? perhaps in the far past, not entirely convincing according to recent decades.

Using Quandl with Matlab is also very easy. You simply need to go here: and take the folder named Q+. In it, there are a couple of functions you need. Place the folder (or create a new folder but keep the same name) in your working Matlab directory and you are done:

Quandl.auth('here the token copied from the tab')
AAA = Quandl.get('ML/AAATRI','start_date','1990-01-01','end_date','2012-12-31','collapse','monthly');
TR = Quandl.get('SANDP/MONRETS','start_date','1990-01-01','end_date','2012-12-31');

Performance of bonds have been relatively strong over the last couple of decades. The good performance is directly linked to the interest rate set by the Fed. This rate has been declining over the years and is now very low (so can only move up or stay very low). Because of it, we should not expect a repeat of the strong bond performance we've observed so far.

To leave a comment for the author, please follow the link and comment on their blog: quandl blog » R quandl blog. offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)