Thursday, 10 September 2015

Expected Shortfall for a Canadian ETF Portfolio: September 2015

August was a volatile month for world equity markets. Broad equity markets in North America and Europe started August in a narrow trading range before taking steep drops in the middle of the month and then rebounding slightly. Historically, August can be a bad month for equities because many traders in North America and Europe take vacations. As a result, there is less volume which tends to magnify bad events.

In this post I look at the damage this volatility did to a Canadian ETF portfolio. The approach is to calculate expected shortfall (ES) for a balanced portfolio of Canadian ETFs. This is an update on an earlier post that I did. ES calculates the average expected loss over a particular time period for a given confidence interval.

I use a portfolio of five broad asset classes: Canadian equities, Canadian REITs, US equities, Europe and Far East (EAFE) equities, and Canadian bonds. I use daily data sourced from Yahoo Finance. The ticker symbols and asset classes are as follows.

  "XIU.TO", # Canadian equities,
  "XRE.TO", # Canadian REITS,
  "XSP.TO", # US equities (SP500),
  "XIN.TO", # EAFE equities,
  "XBB.TO"  # Canada bonds,


The portfolio weights are:
XIU 30%
XRE 10%
XSP 20%
XIN 10%
XBB 30%

Here is how each of the ETFs have performed over the past 100 trading days.



Even Canadian bonds (XBB.TO) got hit! For Canadian investors, there was no where to run and no where to hide.

Here are what the updated expected shortfall values look like. For the ES calculations I calculate one day ES at a 99% confidence interval.

To the right of the chart ES violations are evident for each of the three ES values. Lets take a closer look at the last 30 trading days.



               Portfolio  Historical    Gaussian    Modified hit_hist hit_mod
2015-07-28  0.0045591063 -0.02057457 -0.01388825 -0.02011961        0       0
2015-07-29  0.0072440550 -0.02057457 -0.01388926 -0.02011382        0       0
2015-07-30  0.0021083012 -0.01961036 -0.01372071 -0.01958061        0       0
2015-07-31  0.0049846131 -0.01961036 -0.01369486 -0.01959875        0       0
2015-08-04 -0.0001214745 -0.01727665 -0.01329163 -0.01802837        0       0
2015-08-05 -0.0002307325 -0.01727665 -0.01308706 -0.01716631        0       0
2015-08-06 -0.0046951243 -0.01727665 -0.01304780 -0.01708168        0       0
2015-08-07 -0.0013375814 -0.01727665 -0.01293413 -0.01674055        0       0
2015-08-10  0.0048423110 -0.01727665 -0.01293624 -0.01674833        0       0
2015-08-11 -0.0027925194 -0.01727665 -0.01292721 -0.01670386        0       0
2015-08-12 -0.0029878816 -0.01727665 -0.01290819 -0.01665256        0       0
2015-08-13 -0.0025982336 -0.01727665 -0.01291542 -0.01668199        0       0
2015-08-14  0.0019287368 -0.01621678 -0.01273006 -0.01731595        0       0
2015-08-17  0.0008568045 -0.01621678 -0.01268098 -0.01726896        0       0
2015-08-18 -0.0022049060 -0.01621678 -0.01268081 -0.01727367        0       0
2015-08-19 -0.0054502305 -0.01621678 -0.01260506 -0.01695646        0       0
2015-08-20 -0.0156635244 -0.01621678 -0.01262080 -0.01699182        0       0
2015-08-21 -0.0169691849 -0.01639901 -0.01267554 -0.01696590        1       1
2015-08-24 -0.0272985615 -0.01668715 -0.01276972 -0.01695727        1       1
2015-08-25  0.0007662231 -0.01797793 -0.01297351 -0.01555675        0       0
2015-08-26  0.0161247653 -0.01797793 -0.01297354 -0.01555796        0       0
2015-08-27  0.0173129171 -0.01797793 -0.01302212 -0.01568852        0       0
2015-08-28  0.0011453308 -0.01797793 -0.01307287 -0.01584518        0       0
2015-08-31 -0.0050855477 -0.01797793 -0.01303772 -0.01572148        0       0
2015-09-01 -0.0176022569 -0.01797793 -0.01304444 -0.01575105        0       1
2015-09-02  0.0068626662 -0.01822957 -0.01311513 -0.01578005        0       0
2015-09-03  0.0035217033 -0.01822957 -0.01311116 -0.01577722        0       0
2015-09-04 -0.0086586215 -0.01822957 -0.01303865 -0.01570228        0       0
2015-09-08  0.0099768929 -0.01822957 -0.01305171 -0.01570694        0       0
2015-09-09 -0.0055476529 -0.01822957 -0.01306676 -0.01569068        0       0



ES calculated using the historical method results in hits (portfolio return less than ES) on August 21 and 24. ES calculated using the modified approach results in hits on August 21,24, and September 1. The ES is calculated using a confidence interval of 99%. So 99% of the time the actual loss should be no larger than the ES value. There is a 1% chance of losing more than the ES value. For daily data this works out to a "hit" or exceedance once every 100 days. According to the modified ES calculation we have had 3 hits in the past 30 days. Poisson clumping? Perhaps, but certainly beyond what the models predicted.


Here is the R code.

#########################################################
#  Economic forecasting and analysis
#  Perry Sadorsky
#  ES for a Canadian portfolio
#  September 2015
##########################################################

rm(list=ls())
library(fpp)
library(quantmod)
library(PerformanceAnalytics)




symbols = c(
  "XIU.TO", # Canadian equities,
  "XRE.TO", # Canadian REITS,
  "XSP.TO", # US equities (SP500),
  "XIN.TO", # EAFE equities,
  "XBB.TO"  # Canada bonds,
)



getSymbols(symbols, from="1970-01-01")
m = length(symbols)
Y = Ad(XIU.TO)
for (i in 2:m) Y = cbind(Y, Ad(get(symbols[i])))
head(Y)
tail(Y)



par(mfrow = c(3, 2))
for (i in 1:m) plot(tail (Y[, i], 100),  main = symbols[i])
par(mfrow = c(1, 1))



## returns
y.ret <-  (na.omit(1 * diff(log(Y)) ))


par(mfrow = c(3, 2))
for (i in 1:m) {qqnorm(y.ret[, i],  main = c("QQ Normal Plot",symbols[i]))
                qqline(y.ret[, i])}
par(mfrow = c(1, 1))


head(y.ret)
tail(y.ret)
weights <- c(0.3, 0.1, 0.2, 0.1, 0.3)
##########################################################


w.e = 1000
p.v = 0.99
port.ret = weights[1]*y.ret[,1] + weights[2]*y.ret[,2] + weights[3]*y.ret[,3]+ weights[4]*y.ret[,4] + weights[5]*y.ret[,5]
names(port.ret) = c("Portfolio")



## create custom function
bt_p_ES <- function(x, p, w) {
  modified.ES = as.numeric  ( ES(x, p=p, method="modified", portfolio_method = "component", weights = w)$MES)
  historical.ES = as.numeric  (ES(x, p=p, method="historical", portfolio_method="component", weights = w)$`-r_exceed/c_exceed`)
  gaussian.ES = as.numeric  ( ES(x, p=p, method="gaussian",  portfolio_method = "component", weights = w)$ES)
  ans = c(historical.ES, gaussian.ES, modified.ES)
  names(ans) = c("Historical", "Gaussian", "Modified")
  return(ans)
}


## rolling analysis
tic <- Sys.time()
ret.es_1 <- rollapply(y.ret, width = w.e, FUN = bt_p_ES, p=p.v, w=weights, by.column = FALSE,
                      align = "right"  )
toc <- Sys.time()
toc - tic

View(ret.es_1)
tail(ret.es_1)


## all three together with actual portfolio returns
ES.results = lag(ret.es_1, k=1)
chart.TimeSeries(merge(port.ret, -ES.results), legend.loc="topright")
tail(-ES.results)

# identify exceedances
hit_hist = port.ret < -ES.results[,1]
hit_mod  = port.ret < -ES.results[,3]

colnames(hit_hist) = "hit_hist"
colnames(hit_mod)  = "hit_mod"

tail(merge(port.ret, -ES.results, hit_hist, hit_mod),30)