Logo Icon

Correlations Among Us Stocks: Is It Really Time to Fire Your Adviser?

Note: This post is NOT financial advice! This is just a fun way to explore some of the capabilities R has for importing and manipulating data.

The Financial Times says it’s time to “Fire your Adviser” because correlations among US stocks are at their highest levels since the financial crisis. Unfortunately, they only provide data going back 3 months and it’s in a boring table, rather than an awesome chart. After reading this article, I immediately pulled out quantmod and PerformanceAnalytics in R. I used quantmod to download the daily data (from yahoo finance) for each index listed in the article, and then used PerformanceAnalytics to analyze and graph it. After and hour or so of fiddling around, I ended up with this chart:

library(quantmod)
library(PerformanceAnalytics)
set.seed(42)

symbols <- c('XLE','XLV','XLI','XLU','XLP','IYZ','XLK','XLY','XLF','XLB','GLD','SLV','EFA','EEM','FXA','FXE','FXY','HYG','LQD')

getSymbols(symbols,from='2007-01-01')
#>  [1] "XLE" "XLV" "XLI" "XLU" "XLP" "IYZ" "XLK" "XLY" "XLF" "XLB" "GLD" "SLV" "EFA" "EEM" "FXA" "FXE" "FXY" "HYG" "LQD"
getSymbols('SPY',from='2007-01-01')
#> [1] "SPY"

SP500 <- Cl(SPY)
colnames(SP500)[1] <- 'SPY'

#Function to build a dataframe form a list of symbols
symbolFrame <- function(symbolList) {
    Data <- lapply(symbolList, function(S) Cl(get(S)))
  Data <- do.call(cbind, Data)
    colnames(Data) <- symbolList
    return(Data)
}

#Make a color palette for the graphj
library(fBasics)
colorset <- qualiPalette(length(symbols), name="Set1")

#Chart Correlations
Data <- symbolFrame(symbols)
Data <- Data['2010-01-01::2012-12-31']
print(chart.RollingCorrelation(Data, SP500, legend.loc="bottomleft",colorset=colorset, main = "Rolling 3-month Correlation",width=90))
A multi-line plot showing the rolling 3-month correlation of various assets and sector ETFs with SPY (S&P 500 ETF) from January 2010 to December 2012. Each line represents the correlation of a different asset with SPY, with assets like XLE, XLV, and XLI showing high correlations, typically above 0.5. The plot reveals significant drops in correlation during market downturns, such as the mid-2011 period, where correlations for some assets turned negative, indicating a decoupling from the broader market.

Each line on this chart represents the rolling correlation between a major asset class and the S&P 500 (represented by SPY). I used a 90-day window to calculate the correlations, so each point on each line is looking backwards at a 90-day timeframe. As you can see, correlations have indeed gone up in the last few months, but there have been other periods in 2010 and 2011 with similarly high correlations.

This next chart looks at just the major US sectors, and takes the average of each of their correlations with SPY. This shows the overall correlation of the US stock market since 2007. Again, things are highly correlated right now, but we’ve been here before.

table.RollingCorrelation <- function (Ra, Rb, width = 12, na.pad = FALSE, ...) {
    Ra = checkData(Ra)
    Rb = checkData(Rb)
    columns.a = ncol(Ra)
    columns.b = ncol(Rb)
    columnnames.a = colnames(Ra)
    columnnames.b = colnames(Rb)
    for (column.a in 1:columns.a) {
        for (column.b in 1:columns.b) {
            merged.assets = merge(Ra[, column.a, drop = FALSE],
                Rb[, column.b, drop = FALSE])
            column.calc = rollapply(na.omit(merged.assets[, ,
                drop = FALSE]), width = width, FUN = function(x) cor(x[,
                1, drop = FALSE], x[, 2, drop = FALSE]), by = 1,
                by.column = FALSE, na.pad = na.pad, align = "right")
            column.calc.tmp = xts(column.calc)
            colnames(column.calc.tmp) = paste(columnnames.a[column.a],
                columnnames.b[column.b], sep = " to ")
            column.calc = xts(column.calc.tmp, order.by = time(column.calc))
            if (column.a == 1 & column.b == 1)
                Result.calc = column.calc
            else Result.calc = merge(Result.calc, column.calc)
        }
    }
    return(Result.calc)
}

#Calculate mean correlation among US Sectors & plot
sectors <- c('XLE','XLV','XLI','XLU','XLP','IYZ','XLK','XLY','XLF','XLB')
corrs <- table.RollingCorrelation(symbolFrame(sectors), SP500, width=90)
meancorr <- apply(corrs,1,mean)
meancorr <- xts(meancorr,order.by=index(corrs))
plot(meancorr, main = "Mean Rolling 3-month Correlation\nAmong Major US Sectors")
A line plot showing the mean rolling 3-month correlation among major US sectors from May 2007 to August 2024. The plot depicts fluctuations in correlation over time, with periods of high correlation (above 0.8) indicating that sectors were moving more in tandem, particularly during and after the 2008 financial crisis, as well as during other significant market events. There are also notable periods of lower correlation, such as in 2018 and 2022, reflecting more divergence in sector performance.

Here is the code I used to generate these charts. Feel free to comment on my implementation, and I’ll be happy to make any improvements and update the charts.

stay in touch