Wednesday, April 18, 2012

Efficient Frontier of Funds and Allocation Systems

I did a very basic experiment in Efficient Frontier of Buy-Hold and Tactical System where I determined the efficient frontier of the S&P 500 with itself transformed by a Mebane Faber 10-month moving average tactical allocation.

The result was interesting, but I did not pursue further.  Now with some inspiration and tools by Systematic Investor, I thought I would extend the post. This time around we will use both the Vanguard U.S. Total Bond Market (VBMFX) and Vanguard U.S. S&P 500 (VFINX) combined with both portfolios determined by tactical methods (moving average, RSI, and omega) and those funds transformed individually by the same tactical methods.  I will as always warn you that this is not advice and large losses are almost guaranteed.  Also, I would like to note that I have checked the 10-month moving average every way possible (even manually in Excel), and it has been incredible on the VFINX since 1990.  Prior to 1990, results were good but nowhere near as amazing.  If I messed up, please let me know.

From TimelyPortfolio
From TimelyPortfolio
From TimelyPortfolio

R code from GIST:

require(quantmod)
require(PerformanceAnalytics)
getSymbols("VFINX",from="1990-01-01",adjust=TRUE)
getSymbols("VBMFX",from="1990-01-01",adjust=TRUE)
perf <- na.omit(merge(monthlyReturn(VBMFX[,4]),monthlyReturn(VFINX[,4])))
colnames(perf) <- c("VBMFX","VFINX")
#get 8 month RSI; randomly picked 8; no optimization
rsi<- lag(merge(RSI(perf[,1],n=8),RSI(perf[,2],n=8)),k=1)
#allocate between vbmfx and vfinx based on highest RSI
rsi.perf <- ifelse(rsi[,1]>rsi[,2],perf[,1],perf[,2])
rsi.each <- as.xts(as.matrix(rsi>50) * as.matrix(perf),
order.by=index(perf))
#get cumulative returns for moving average
cumul <- as.xts(apply(perf+1,MARGIN=2,cumprod),order.by=index(perf))
#do 10 month Mebane Faber style system
ma <- lag(merge(runMean(cumul[,1],n=10),runMean(cumul[,2],n=10)),k=1)
#apply 50% allocation to each fund if they are > 10 month moving average
ma.perf <- as.xts(apply(as.matrix(cumul>ma) * as.matrix(perf)/2,
MARGIN=1,sum),
order.by=index(perf))
ma.each <- as.xts(as.matrix(cumul>ma) * as.matrix(perf),
order.by=index(perf))
#add omega as another allocation method
omega <- lag(merge(apply.rolling(perf[,1],width=6,by=1,FUN=Omega),
apply.rolling(perf[,2],width=6,by=1,FUN=Omega)),
k=1)
#if omega >= 1 then apply 50% allocation
omega.perf <- as.xts(apply(as.matrix(omega>=1) * as.matrix(perf)/2,
MARGIN=1,sum),
order.by=index(perf))
omega.each <- as.xts(as.matrix(omega>=1) * as.matrix(perf),
order.by=index(perf))
perf.all <- merge(perf,rsi.perf,rsi.each,ma.perf,ma.each,omega.perf,omega.each)
perf.all[is.na(perf.all)]<-0
colnames(perf.all) <- c(colnames(perf),paste(c(rep("rsi",3),rep("ma",3),rep("omega",3)),
c("",".VBMFX",".VFINX"),sep=""))
#now let's add two very basic systems
#and explore on Systematic Investor's efficient frontier
########################################################
#continue to highlight the very fine work of
#http://systematicinvestor.wordpress.com/
#adapted some of his code to provide
#a not-so-novel additional example for
#those that might be interested
#######################################################
# Load Systematic Investor Toolbox (SIT)
con = gzcon(url('https://github.com/systematicinvestor/SIT/raw/master/sit.gz', 'rb'))
source(con)
close(con)
#--------------------------------------------------------------------------
# Create Efficient Frontier
#--------------------------------------------------------------------------
ia = list()
#amend to use the funds and basic systems
ia$symbols = colnames(perf.all)
ia$n = len(ia$symbols)
#use PerformanceAnalytics tables to get return (geometric) and risk
#for the entire period
ia$expected.return = as.matrix(t(table.Stats(perf.all)[7,]))
ia$risk = as.matrix(t(table.Stats(perf.all)[14,]))
ia$correlation = cor(perf.all)
ia$cov = cov(perf.all)
n = ia$n
# 0 <= x.i <= 1
constraints = new.constraints(n, lb = 0, ub = 1)
# SUM x.i = 1
constraints = add.constraints(rep(1, n), 1, type = '=', constraints)
# create efficient frontier
ef.risk = portopt(ia, constraints, 50)
#I am getting an error here
#plot.ef(ia, ef.risk), transition.map=TRUE)
#know what is happening but not motivated to fix
#"Error in x$weight : $ operator is invalid for atomic vectors"
#will do manually plot
colors <- c("purple","indianred3","steelblue2","steelblue3","steelblue4",
"darkolivegreen2","darkolivegreen3","darkolivegreen4",
"chocolate2","chocolate3","chocolate4")
plot(ef.risk$return~ef.risk$risk,col="grey60",lwd=3,type="l",
xlim=c(min(ia$risk),max(ia$risk)+.01),
ylim=c(min(ia$expected.return),max(ia$expected.return)))
points(x=as.numeric(ia$risk),y=as.numeric(ia$expected.return),pch=19,
col=colors,cex=1.5)
text(x=as.numeric(ia$risk),y=as.numeric(ia$expected.return),
labels=ia$symbols,pos=4,col=colors)
title(main="Efficient Frontier of VBMFX and VFINX and Systematic Allocation",
adj=0,outer=TRUE,line=-1)
plot.transition.map(ef.risk,col=colors)
chart.CumReturns(perf.all,colorset=colors,
main="Growth of VBMFX and VFINX and Systematic Allocations",
legend.loc="topleft")

3 comments:

  1. Great post, I really liked the last chart to show the relative performance of each strategy. I just ran a quick test of the SMA10 strategy on VFINX in quantstrat. My equity curve looks similar to yours, but doesn't go quite so parabolic... could just be the scale that gives that appearance.

    Again, great posts recently. I especially like how you are demonstrating the use of knitr to generate reports.

    Ross

    ReplyDelete
  2. Nice post, one question about the combined portfolios (ma): what is the rule there regarding rebalancing and allocation?
    Cheers
    Andreas

    ReplyDelete
  3. thanks so much to rbresearch and Andreas for the comments. Rule for this simple example is monthly rebalancing. Hope to use PortfolioAnalytics or the SIT (Systematic Investor Toolkit) to try other methods of rebalancing soon. Great question with very big implications.

    ReplyDelete