3 min read

Computing SPDR sector returns

Introduction

The idea of this post is to evaluate the performance of different sectors of the S&P 500 after the 2020 market crash. What I find is the sectors that were most hit had a strong recovery from the march lows. Currently the market had a small drop therefore it’s interesting to monitor if the same pattern would repeat.

Computing sector returns

I start by getting the sector ETFs data starting from 1990 to compute the average returns for each sector.

rm(list = ls(all = TRUE))

options("getSymbols.warning4.0"=FALSE)

library(data.table)
library(quantmod)
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## Attaching package: 'xts'
## The following objects are masked from 'package:data.table':
## 
##     first, last
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## Version 0.4-0 included new data defaults. See ?getSymbols.
library(xts)
library(PerformanceAnalytics)
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
sectors = data.table(
  Sector = c("Energy", "Basic Materials", "Industrials",
             "Consumer Cyclical", "Consumer Defensive", "Healthcare", "Financial Services",
             "Technology", "Communication Services", "Utilities", "Real Estate"),
  ticker = c("XLE", "XLB", "XLI", "XLY", "XLP", "XLV", "XLF", "XLK", "XTL", "XLU", "VNQ"))

prices = lapply(sectors$ticker, function(tk)
  getSymbols(tk, src='yahoo', auto.assign=FALSE, from='1990-01-01'))
names(prices) = sectors$ticker

compute_returns <- function(prices){
  R = lapply(prices, function(df) Ad(df)/Lag(Ad(df), k=1) - 1)
  R = do.call(cbind, R)
  R = R[-1, ]
  names(R) = gsub("\\.Adjusted", "", names(R))
  R
}

period_returns <- function(df){
  Ad(tail(df, 1))[[1]] / Ad(head(df, 1))[[1]] - 1
}


ret_all = compute_returns(prices)
average = table.AnnualizedReturns(ret_all)
ret_average = as.data.table(average[1, ])

average
##                              XLE    XLB    XLI    XLY    XLP    XLV    XLF
## Annualized Return         0.0450 0.0707 0.0707 0.0904 0.0611 0.0812 0.0416
## Annualized Std Dev        0.2872 0.2466 0.2195 0.2256 0.1574 0.1861 0.3109
## Annualized Sharpe (Rf=0%) 0.1568 0.2866 0.3221 0.4007 0.3885 0.4360 0.1339
##                              XLK    XTL    XLU    VNQ
## Annualized Return         0.0681 0.0462 0.0672 0.0760
## Annualized Std Dev        0.2626 0.2123 0.1979 0.3130
## Annualized Sharpe (Rf=0%) 0.2593 0.2175 0.3395 0.2429

Then I compute the returns for the periods I’m interesed. I’ve named these periods as:

  • Crash: 2020-02-24 to 2020-03-23
  • Recovery: 2020-03-23 to 2020-06-08
  • Recent-drop: 2020-06-08 to 2020-06-29
price_crash = lapply(prices, function(df) df["2020-02-20/2020-03-23"])
ret_crash = lapply(price_crash, period_returns)
ret_crash = as.data.table(ret_crash)

price_recovery = lapply(prices, function(df) df["2020-03-23/2020-06-08"])
ret_recovery = lapply(price_recovery, period_returns)
ret_recovery = as.data.table(ret_recovery)

price_drop = lapply(prices, function(df) df["2020-06-08/2020-06-29"])
ret_drop = lapply(price_drop, period_returns)
ret_drop = as.data.table(ret_drop)

rets = rbind(ret_average, ret_crash, ret_recovery, ret_drop)
rets = cbind(names(rets), transpose(rets))
names(rets) = c("ticker", "average", "crash", "recovery", "Recent-drop")

rets = merge(sectors, rets, by='ticker')
rets = rets[, .(Sector, ticker, average, crash, recovery, `Recent-drop`)]
rets[order(crash)]
##                     Sector ticker average      crash  recovery  Recent-drop
##  1:                 Energy    XLE  0.0450 -0.5601858 0.9881205 -0.200288778
##  2:     Financial Services    XLF  0.0416 -0.4267864 0.5141564 -0.140776587
##  3:            Real Estate    VNQ  0.0760 -0.4222992 0.5250395 -0.096917843
##  4:            Industrials    XLI  0.0707 -0.4149666 0.5642813 -0.100625643
##  5:        Basic Materials    XLB  0.0707 -0.3631436 0.5621904 -0.067681147
##  6:              Utilities    XLU  0.0672 -0.3559493 0.3983975 -0.097734035
##  7:      Consumer Cyclical    XLY  0.0904 -0.3352880 0.5223352 -0.051454317
##  8:             Technology    XLK  0.0681 -0.3042901 0.4477272  0.009938275
##  9: Communication Services    XTL  0.0462 -0.2919865 0.4237548 -0.071398441
## 10:             Healthcare    XLV  0.0812 -0.2746236 0.3926561 -0.049203217
## 11:     Consumer Defensive    XLP  0.0611 -0.2421984 0.2558091 -0.039403865

Energy and Financials had the largest “recent-drop”, therefore if the same pattern repeats itself I would expect those sectors to outperform the rest.