Data

We use the following assets:

library(tidyverse)
library(lubridate)
library(scales)
library(ggrepel)
library(tidyquant)
library(jsonlite)
theme_set(theme_minimal())
invisible(Sys.setlocale("LC_TIME", "en_US.UTF-8"))

library(ggcorrplot)
library(patchwork)
# stocks (free long term sources: K.French) ok
# real estate (free long term sources: ) ok
# bonds 
# gold ok
# alternatives

# stocks K.French ----
library(frenchdata)
ff_3f <- download_french_data('Fama/French 3 Factors')

stocks <- ff_3f$subsets$data[[1]] |> 
  mutate(date = ymd(paste0(date, "01"))) |> 
  mutate_if(is.numeric, function(x) x/100) |> 
  transmute(date, RF, Mkt = `Mkt-RF` + RF)

# real estate (REIT) ----
library(readxl)
url <- "https://www.reit.com/sites/default/files/returns/MonthlyHistoricalReturns.xls"
# download as temp file then read it
temp <- tempfile(fileext = ".xls")
download.file(url, temp, mode = "wb")
reit <- read_excel(temp,
                   range = "A8:B1000",
                   sheet = "Index Data") |> 
  transmute(date = floor_date(as.Date(Date), "month"),
            reit = Return/100) |> 
  drop_na()

# gold ----
# source: https://www.investing.com/currencies/xau-usd-historical-data
gold <- read_csv("data/128-ltcorrs/gold.csv") |> 
  transmute(date = mdy(Date), price = Price) |> 
  arrange(date) |> 
  mutate(gold = price/lag(price)-1) |> 
  select(date, gold) |> 
  drop_na()

# bonds ----
t20 <- tq_get("DGS20", get = "economic.data", from = as.Date("1960-01-01")) |> 
  mutate(price = price/100)
  
corporate <- tq_get("DAAA", get = "economic.data", from = as.Date("1960-01-01")) |> 
  mutate(price = price/100)


library(treasuryTR)

# t20 |> 
corporate <- corporate |> 
  arrange(date) |> 
  transmute(date, r = total_return(price, 20)) |> 
  drop_na() |> 
  group_by(date = floor_date(date, "month")) |>
  summarise(r = prod(1+r)-1) |> 
  ungroup()
t20 <- t20 |> 
  arrange(date) |> 
  transmute(date, r = total_return(price, 20)) |> 
  drop_na() |> 
  group_by(date = floor_date(date, "month")) |>
  summarise(r = prod(1+r)-1) |>
  ungroup()


assets_returns <- bind_rows(stocks |> 
                              pivot_longer(-date, names_to = "asset", values_to = "r") |> 
                              arrange(asset, date) |> 
                              mutate(asset = recode(asset, 
                                                    Mkt = "Stocks", RF = "T-Bills")),
                            reit |>
                              transmute(date, r = reit, asset = "REITs"),
                            gold |>
                              transmute(date, r = gold, asset = "Gold"),
                            corporate |>
                              transmute(date, r = r, asset = "Bonds"),
                            t20 |>
                              transmute(date, r = r, asset = "Treasuries"))

assets_returns |> saveRDS("data/128-ltcorrs/assets_returns.RDS")
assets_returns <- readRDS("data/128-ltcorrs/assets_returns.RDS")

date_minmax <- assets_returns |> 
  group_by(asset) |> 
  summarise(min = min(date), max = max(date))

The data spans from 1983-01-01 to 2024-10-01 for Bonds, from 1970-03-01 to 2024-10-01 for Gold, from 1972-01-01 to 2024-09-01 for REITs, from 1926-07-01 to 2024-08-01 for Stocks, and from 1926-07-01 to 2024-08-01 for T-Bills.

Correlations

The following heatmaps show the correlations between the assets for each decade.

plots <- list()
for (decade in c("1980s", "1990s", "2000s", "2010s", "2020s")) {
  plots[[decade]] <- assets_returns |> 
    filter(between(year(date), as.numeric(str_sub(decade, 1, 4)), as.numeric(str_sub(decade, 1, 4))+9)) |>
    pivot_wider(names_from = asset, values_from = r) |> 
    drop_na() |> 
    select(-date) |>
    cor() |> 
    ggcorrplot(type = "lower", outline.color = "white", colors = c("blue", "white", "darkgreen"),
               lab = T) +
    labs(title = decade)
}


plot_a_list <- function(master_list_with_plots, no_of_rows, no_of_cols) {
  
  patchwork::wrap_plots(master_list_with_plots, 
                        nrow = no_of_rows, ncol = no_of_cols)
}

plot_a_list(plots, 3, 2)

On first sight, it becomes clear that the correlations have increased. While we see some purple up to the 2010s, the 2020s show a lot of green. The past four years have seen a lot of positive correlations between the asset classes.

Rolling Correlations

The following plots show the rolling 60-month correlations between the assets.

assets_returns_wide <- assets_returns |> 
  pivot_wider(names_from = asset, values_from = r)
  # janitor::clean_names()

correlations <- tibble()
for (asset1 in names(assets_returns_wide[-1])) {
  for (asset2 in names(assets_returns_wide[-1])) {
    if (asset1 != asset2) {
      correlations <- bind_rows(correlations,
                                assets_returns_wide |> 
                                  select(date, all_of(asset1), all_of(asset2)) |> 
                                  filter(!is.na(!!as.name(asset1)),
                                         !is.na(!!as.name(asset2))) |> 
                                  transmute(
                                    date,
                                    cor60 = rollapply(
                                      data = cbind(!!sym(asset1), !!sym(asset2)),
                                      width = 60,
                                      FUN = function(x) cor(x[, 1], x[, 2], use = "pairwise.complete.obs"),
                                      by.column = FALSE,
                                      align = "right",
                                      fill = NA
                                    ),
                                    asset1 = asset1,
                                    asset2 = asset2) |> 
                                  drop_na())
    }
  }
}

correlations |> 
  filter(date >= as.Date("1980-01-01")) |>
  ggplot(aes(x = date, y = cor60, color = cor60, group = 1)) +
  geom_line() +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red", size = 0.1) +
  facet_grid(asset1 ~ asset2, scales = "free") +
  scale_x_date(date_breaks = "10 years", date_labels = "'%y") +
  scale_y_continuous(limit = c(-1, 1)) +
  # "blue", "white", "darkgreen"
  scale_color_gradient2(low = "blue", mid = "gray", high = "darkgreen",
                        midpoint = 0) +
  labs(title = "Rolling 60-month correlations",
       y = "Correlation", color = NULL,
       x = NULL) +
  theme(legend.position = "bottom")

In the chat the follows we focus on the correlation of Stocks with the other asset classes.

correlations |>
  filter(date >= as.Date("1960-01-01")) |> 
  filter(asset1 == "Stocks") |>
  ggplot(aes(x = date, y = cor60, color = asset2)) +
  geom_line() +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red", size = 0.1) +
  scale_color_manual(values = c("REITs" = "red", "Gold" = "orange", "Bonds" = "blue", "Treasuries" = "purple",
                                "T-Bills" = "gray")) +
  scale_y_continuous(limit = c(-1, 1)) +
  scale_x_date(date_breaks = "10 years", date_labels = "%Y") +
  labs(title = "Rolling 60-month correlations with Stocks",
       y = "Correlation",
       x = NULL, color = NULL)

The chart shows the spike in correlations over the past four years. While correlations had been higher for several asset classes versus Stocks, the sudden increase is notable. Previously, we have seen increases around the Financial Crisis.

In conclusion, the correlations between asset classes have increased over the past four years. This is especially true for Stocks and REITs, Stocks and Gold, and Stocks and Bonds. The relationship between Stocks and Bonds is not stable over time. Bonds are not always a good hedge against stock market downturns. Gold has shown low correlations with the other asset classes, but these have increased recently. REITs have shown low to moderate correlations with the other asset classes, but the correlation with Stocks has increased over time.