PerformanceAnalytics — An Indespensible Quant Tool for any Investor

I’m in the thick of our annual portfolio investment due diligence at work. Basically, this involves surveying all the potential investments we could hold in our portfolios, and then deciding whether we should continue to hold what we own or switch to something else. It is a big job, involving lots of data analysis.

I forget how much I rely on the PerformanceAnalytics package to help me tell the data’s story. It really is an all-in-one package for any quantitative analysis. I’d like to give you a rough-and-tumble of just some of its features.

To start, let’s load two libraries, and gather some data:


# List some ETFs to run our mock-analysis on.
symbols=c("IVV", "GLD", "VBR", "VWO", "VTI", "DBC")
getSymbols( symbols )

I’ve pulled several different symbols for two main reasons. (1) We need multiple data sets to really understand what this puppy can do, and (2) I’d like to demonstrate how to run analysis en masse.

The code block above will pull OHLC+Volume+Adjusted Close data for each symbol. We need to convert that structure to monthly returns. So,

for(i in 1:length(symbols)){ # Iterate through each symbol
  etf = get( symbols[i] ) # Set up temp variable to store data from symbol
  etf = etf[xts:::startof( etf, "months" )] # Convert to monthly structure
  chg = Delt( etf[,6], k=1 ) # Convert to monthly return
  assign( # Assign a unique variable name to this data set: <ticker>.r
    paste( symbols[i], ".r", sep="" ),

Note that for loops are somewhat frowned upon in R. For my purposes, I haven’t had any trouble with them, so until I do, I keep using them! Okay, we now have return data for each symbol. We will call these variables later!

Now that we have data in the format we want, let’s move on to our first demonstration.

Correlation Visualizations
Portfolio management is, at heart, understanding how assets relate to one another. Understanding these relationships can be made easier through visualizations, of which there are few better than the chart.Correlation() function. First, we need our return data in a tidy data frame structure. I do this with the merge() command. Also, you can see how I’ve used the get() and paste() commands to call my custom variables. = merge( # Set up the data.frame
# I've already included symbols[1] & [2], so this iterates through the rest
for(i in 3:length(symbols)){ = merge(, get( paste( symbols[i], ".r", sep="" ) ) )
colnames( = symbols # Make column names the ticker

And we are ready to use the correlation viz tool!

chart.Correlation( )

Which yields the very sexy plot below.

Other than its sexiness, this plot is very helpful. The diagonal gives you the return distribution of each ETF. I like to see how the returns are distributed—are the concentrated in the center? are there lots of outliers? are they heavily skewed? is something just weird? Indeed, this plot recently helped me immediately throw out a position under consideration because the distribution was barbelled (but more heavy left-tailed).

On the lower half, we have the returns of the column-row ETFs plotted against each other. This helps us understand how and when the two positions move with respect to each other. Some positions I like to have a strong linear relationship—say IVV (large cap) and VBR (small cap). Others should serve to offset losses and be strong diversifiers in a portfolio—say IVV (large cap) and GLD (gold). Sometimes, you can find a good replacement position, VTI, for example, which moves in lock-step with IVV. If we find in later analysis that VTI offers a better risk/return profile, we could consider swapping IVV for VTI.

On the top half, we have the traditional correlation metric with its significance level (the asterisks). Honestly, this is the least helpful part of the plot though it can help sum up the other two data points we just reviewed. Ultimately, this is typically the number that feeds into a portfolio optimizer, so it is important. I’d argue, however, that one should use our other observations as primary filters.

Upside/Downside Capture
Another helpful plot is the upside/downside capture. This visualizes the percentage of upside and downside captured by a position relative to a benchmark. Let’s analyze these relative to IVV, our mock core position.

Let’s have R open a new window, then arrange the plots in a 2×3 grid. We will fill the grid with a loop, iterating through each ETF. It looks like this: # Open new plot window
par(mfrow=c(2,3)) # Arrange plots in a 2-row, 3-column grid
for(i in 1:length(symbols)){ # Iterate through each ETF
  etf = get( paste( symbols[i], ".r", sep="" ) )
  bmark = IVV.r # Ratio relative to IVV's return
  colnames( bmark ) = "IVV" # Header on data as IVV
  colnames( etf ) = symbols[i] # Header on data as the ticker
  chart.CaptureRatios(etf, bmark, colorset="dodgerblue4", 

This block yields the Upside/Downside capture ratios for our ETFs:

Note that we could (and should) use a benchmark return as a comparison, but I’m just showing you the ropes here.

Return Distributions
Also helpful are the return distribution visualizations available. Let’s say, for example, I want to understand how non-normal a given asset’s returns are, and I’d like to get a sense of it’s Value at Risk (VaR). We can quickly build some visualizations with the following code: # new Plot
par(mfrow=c(2,3)) # arrange plots in a 2x3 grid
for(i in 1:length(symbols)){ # iterate through each symbol
  etf = get( paste( symbols[i], ".r", sep="" ) ) # load data into temp variable
  colnames( etf ) = symbols[i] # data header as the ticker
  chart.Histogram( etf, main=paste(symbols[i], " Return Distribution"), 
    breaks=15, methods=c("add.normal", "add.risk"), # Add the normal curve, and the VaR levels
    colorset=c("steelblue", "darkgreen", "navy") # colors for each (middle color not used)

In this block, we have iterated through each ticker, then plotted the return histogram. In addition, I’ve told R to include a normal curve for comparison (it will auto-calculate the mean and standard deviation of the distribution) and the VaR levels. All of that yields this plot:

We can see, quite clearly, that none of these returns are statistically normal, but that shouldn’t be too big a surprise. Also, the vertical dashed lines show us the VaR levels. This is a good way to filter out core strategies from more binary strategies.

Finally, let’s look at the drawdown plot. This is helpful to get a sense of a fund’s…you guessed it…drawdowns. Just to clarify, a drawdown plot looks at only downward movement relative to a high; it is not a view of the full performance. These visualizations are helpful to compare funds through stressful times, how they perform, and how the recover.

We follow a similar coding pattern as before.
for(i in 1:length(symbols)){
  etf = get( paste( symbols[i], ".r", sep="" ) )
  colnames( etf ) = symbols[i]
  chart.Drawdown( etf, geometric=TRUE, colorset="firebrick4" )

Which yields

These are just a few of the capabilities of the PerformanceAnalytics package. Indeed, this is just a few of the visualization tools, there are many more functions which are helpful. The documentation is 200+ pages of quantitative awesomeness.

After quantmod, this is my favorite quantitative package. It can easily serve as the quantitative toolbox for an investor.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s