Efficient Frontier of Funds and Allocation Systems
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
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 |
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") |
R-bloggers.com 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.