Application of Horizon Plots

[This article was first published on Timely Portfolio, 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.

for background please see prior posts Horizon Plot Already Available and Cubism Horizon Charts in R

Good visualization simplifies, and stories are better told with effective and pretty visualizations.

Although horizon plots are not immediately intuitive, I have embraced them as an extremely effective method of analyzing more than four series.  I hope they become much more popular, so I can use them with much more confidence.  If we look at a traditional cumulative growth chart on the managers dataset provided by PerformanceAnalytics, I get confused by too many lines and colors since there are 10 different series.  While this chart works, it can be better.

From TimelyPortfolio

We could panel the data, but I think this makes comparison even more difficult.

From TimelyPortfolio

In this case and many others, horizon plots provide what I feel to be both a more attractive and effective visualization.  Here is an example using latticeExtra’s horizonplot function with very little adjustment.  You can detect both comovement or seasonality and can compare the amplitude simultaneously.

From TimelyPortfolio

With a little additional formatting, we can get an ideal visualization-pretty and effective.  The ability to scale well beyond 10 series offers power that we cannot obtain with a traditional line chart.

From TimelyPortfolio

As another example, let’s look at how we can use horizon plots to monitor a moving average system similar to the Mebane Faber’s timing model.  If you follow the link, you can see a decent visualization of the price and moving average.  A horizon plot could accomplish this much more efficiently.

From TimelyPortfolio

I personally like the mirrored horizon plot even better.  Let’s incorporate that.

From TimelyPortfolio

Please help me popularize these extremely powerful charts.

R code from GIST (do raw for copy/paste):

require(lattice)
require(latticeExtra)
require(directlabels)
require(reshape2)
require(quantmod)
require(PerformanceAnalytics)
data(managers)
managers[which(is.na(managers),arr.ind=TRUE)[,1],
unique(which(is.na(managers),arr.ind=TRUE)[,2])] = 0
testprice <- cumprod(1+managers)-1
testdf <- as.data.frame(cbind(index(testprice),coredata(testprice)),stringsAsFactors=FALSE)
testmelt <- melt(testdf,id.vars=1)
colnames(testmelt) <- c("date","series","growth")
testmelt[,"date"] <- as.Date(testmelt[,"date"])
#just plain old xyplot from xts package examples
direct.label(
xyplot(testprice,
lwd=2,
screens=1,
col = c(brewer.pal(n=8,"Dark2")[1:6],brewer.pal(n=9,"PuBu")[5:9]),
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
},
scales = list(tck = c(1,0), y = list(draw = TRUE,relation = "same", alternating = FALSE)),
xlab = NULL,
main="Performance Since 1996 or Inception"),
list(last.bumpup,hjust=0.75, cex=0.8))
#get panel in row 1 and column 1, since only one panel because screens = 1
trellis.focus("panel", 1, 1, highlight = FALSE)
panel.refline(h = pretty(coredata(testprice)), col = "gray70", lty = 3)
#does not even qualify but here as another example
xyplot(testprice,
scales = list(tck = c(1,0), y = list(draw = TRUE,relation = "same", alternating = FALSE)),
panel = function(x, y, ...) {
panel.grid(col = "grey", lty = 3)
panel.xyplot(x, y, ...)
},
layout= c(1,NCOL(testprice)))
xyplot(testprice,
col = c(brewer.pal(n=8,"Dark2")[1:6],brewer.pal(n=9,"PuBu")[5:9]),
screens = colnames(testprice),
lwd = 3,
strip = FALSE, strip.left = TRUE,
scales = list(x = list(tck = c(1,0), alternating = FALSE),
y = list(tck = c(0,1), draw = TRUE, relation = "same", alternating = 2)),
panel = function(x, y, ...) {
panel.refline(h = pretty(coredata(testprice)), col = "gray70", lty = 3)
panel.xyplot(x, y, ...)
},
main = "Performance Since 1996 or Inception")
#first horizonplot with little adjustment
horizonplot(testprice, horizonscale = 1,
#turn off ticks on top and do not draw y ticks or axis
scales = list(tck = c(1,0), y = list(draw = FALSE,relation = "same")),
#draw strip on top
strip=TRUE,
#do not draw strip to left since we have strip = TRUE above
strip.left=FALSE,
#do standard horizon but also add horizontal white grid lines
panel = function(x, ...) {
panel.horizonplot(x, ...)
#here we draw white horizontal grid
#h = 3 means 3 lines so will divide into fourths
#v = 0 will not draw any vertical grid lines
panel.grid(h=3, v=0,col = "white", lwd=1,lty = 1)
},
layout=c(1,ncol(testprice)),
main = "Performance Since 1996 or Inception")
## amended from horizonplot example given in documentation
horizonplot(testprice,
scales = list(tck = c(1,0), y = list(draw = FALSE,relation = "same")),
origin = 0,
horizonscale = 1,
colorkey = FALSE,
panel = function(x, ...) {
panel.horizonplot(x, ...)
panel.grid(h=3, v=0,col = "white", lwd=1,lty = 3)
},
ylab = list(rev(colnames(testprice)), rot = 0, cex = 0.8, pos = 3),
xlab = NULL,
par.settings=theEconomist.theme(box = "gray70"),
strip.left = FALSE,
layout = c(1,ncol(testprice)),
main = "Performance Since 1996 or Inception")
#horizon plot version of http://www.mebanefaber.com/timing-model/
#do horizon of percent above or below 10 month or 200 day moving average
tckrs <- c("VTI","VEU","IEF","VNQ","DBC")
getSymbols(tckrs, from = "2010-12-31")
#do horizon of percent above or below 10 month or 200 day moving average
prices <- get(tckrs[1])[,4]
for (i in 2:length(tckrs)) {
prices <- merge(prices,get(tckrs[i])[,4])
}
colnames(prices) <- tckrs
n=200
#get percent above or below
pctdiff <- (prices / apply(prices, MARGIN = 2, FUN = runMean, n = n) - 1)[n:NROW(prices),]
horizonplot(pctdiff,
scales = list(tck = c(1,0), y = list(draw = FALSE,relation = "same")),
origin = 0,
horizonscale = 0.05,
colorkey = FALSE,
panel = function(x, ...) {
panel.horizonplot(x, ...)
panel.grid(h=3, v=0,col = "white", lwd=1,lty = 3)
},
ylab = list(rev(colnames(prices)), rot = 0, cex = 0.8, pos = 3),
xlab = NULL,
par.settings=theEconomist.theme(box = "gray70"),
strip.left = FALSE,
layout = c(1,ncol(prices)),
main = "Percent Above or Below 200 Days Moving Average")
# for one more example, let's do a mirror horizon plot
horizonplot.offset <- function(x,horizon.type="offset",horizonscale=0.05,title=NA,alpha=0.4){
#get the positive and negative plots for the offset chart
#very similar to the mirror chart above
#except the negative values will be moved to the top of y range
#and inverted
ppos<-
xyplot(x,ylim=c(0,horizonscale),origin=0,
par.settings=theEconomist.theme(box="transparent"),
lattice.options=theEconomist.opts(),
xlab=NULL,ylab=NULL,
panel = function(x,y,...){
#
for (i in 0:round(max(y)/horizonscale,0))
panel.xyarea(x,y=ifelse(y>0,y,NA)-(horizonscale * i),col="green",border="green",col.line="green",alpha=alpha,lwd=2,
scales = list(y=list(draw=FALSE)),...)
},
main=title)
pneg <-
xyplot(x,ylim=c(0,horizonscale),origin=horizonscale,
panel=function(x, y ,...){
for (i in 0:round(min(y)/-horizonscale,0)) {
panel.xyarea(x,y=horizonscale+ifelse(y<0,y,NA)+(horizonscale*i),col.line="red",border="red",col="red",lwd=2,alpha=alpha,...)
}
})
return(ppos+pneg)
}
horizonplot.offset(pctdiff, title = "Percent Difference from 200 Day Moving Average")

To leave a comment for the author, please follow the link and comment on their blog: Timely Portfolio.

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.

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)