Risk Parity using various risk measures (volatility, expected shortfall, semi deviation, maximum draw down)

A lot of people talk about equal risk contribution or balancing risk. This post aim’s to explore how a risk parity portfolio performs depending on the risk metric used in it’s calculation.

I explore using Expected Shortfall. Volatility, Semi deviation and maximum drawdown as risk factors.

The strategy is rebalanced monthly.

Mathematically.

Choose W such that Rf_i * W_i = 1/NumberofAssets

What’s the rationale here?

This means mathematically that the investor has a risk budget and wants to naively diversify the risk budget across the investable universe.

Some potential drawbacks to this approach is the fact it does not take into consideration the correlation/covariance structure of the market. This can also be a positive as it implies that the parameter does not need to be invested however the allocation is universe dependent. It might be worth a future blog post for me to identify least correlated securities from a universe then use risk-parity on that uncorrelated bunch.

 

Performance of risk-parity strategies

 

  • Sharpe Ratio for ES strategy: 0.51
  • Sharpe Ratio for vol strategy: 0.56
  • Sharpe Ratio for semidev strategy: 0.56
  • Sharpe Ratio for max dd strategy: 0.39

 

 

Will update later more performance stats.

library(PerformanceAnalytics)
library(quantmod)
 
###Symlist
symbol_list = c('SPY','XLF','XLE','XLU','XLK','XLB','XLP','XLY','XLI','XLV','TLT','GLD')
 
getSymbols(symbol_list, from = '1990-01-01')
 
 
securities_matrix = NULL
for( sym in symbol_list){
  securities_matrix = merge.xts(securities_matrix,ROC(Ad(get(paste(sym))),type='discrete'))
 
 
}
 
##Start in 2005, as GLD has inception of 2004-11
securities_matrix = securities_matrix['2005/2015-01-01']
 
###Risk Metrics
 
 
 
weight_matrix_es = xts(matrix(nrow=nrow(securities_matrix),ncol=ncol(securities_matrix)),order.by=index(securities_matrix))
weight_matrix_vol= xts(matrix(nrow=nrow(securities_matrix),ncol=ncol(securities_matrix)),order.by=index(securities_matrix))
weight_matrix_sd= xts(matrix(nrow=nrow(securities_matrix),ncol=ncol(securities_matrix)),order.by=index(securities_matrix))
weight_matrix_mdd= xts(matrix(nrow=nrow(securities_matrix),ncol=ncol(securities_matrix)),order.by=index(securities_matrix))
 
monthly_dates_for_rebal = index(weight_matrix_es[endpoints(weight_matrix_es)])
 
for(i in 253:nrow(securities_matrix)){
  info_set = first(securities_matrix,i-1)
  ##Estimate ES
 
  if(index(securities_matrix[i])%in%monthly_dates_for_rebal){
  es_est = ES(info_set)
  es_w = 1/(es_est*ncol(es_est))  
  es_w = es_w/sum(es_w)
  weight_matrix_es[i,] = es_w
 
  ##VOL est
  vol_est = StdDev(info_set)
  vol_w = 1/(vol_est*ncol(vol_est))
  vol_w = vol_w/sum(vol_w)
  weight_matrix_vol[i,] = vol_w
 
  sd_est = SemiDeviation(info_set)
  sd_w = 1/sd_est*ncol(sd_est)
  sd_w = sd_w/sum(sd_w)
  weight_matrix_sd[i,] = sd_w
 
  mdd_est = maxDrawdown(info_set)
  mdd_w = 1/mdd_est*ncol(mdd_est)
  mdd_w = mdd_est/sum(mdd_w)
  weight_matrix_mdd[i,] = mdd_w}
  else{
    weight_matrix_es[i,] = weight_matrix_es[i-1,]*(1+securities_matrix[i-1,])
    weight_matrix_es[i,] = weight_matrix_es[i,]/sum(weight_matrix_es[i,])
 
    weight_matrix_vol[i,] = weight_matrix_vol[i-1,]*(1+securities_matrix[i-1,])
    weight_matrix_vol[i,] = weight_matrix_vol[i,]/sum(weight_matrix_vol[i,])
 
    weight_matrix_sd[i,] = weight_matrix_sd[i-1,]*(1+securities_matrix[i-1,])
    weight_matrix_sd[i,] = weight_matrix_sd[i,]/sum(weight_matrix_sd[i,])
 
 
    weight_matrix_mdd[i,] = weight_matrix_mdd[i-1,]*(1+securities_matrix[i-1,])
    weight_matrix_mdd[i,] = weight_matrix_mdd[i,]/sum(weight_matrix_mdd[i,])
 
 
  }
}
 
port_es = xts(rowSums(weight_matrix_es*securities_matrix),order.by=index(securities_matrix))
port_vol = xts(rowSums(weight_matrix_vol*securities_matrix),order.by=index(securities_matrix))
port_sd = xts(rowSums(weight_matrix_sd*securities_matrix),order.by=index(securities_matrix))
port_mdd = xts(rowSums(weight_matrix_mdd*securities_matrix),order.by=index(securities_matrix))
 
my_mat = merge.xts(port_es,port_vol,port_sd,port_mdd,securities_matrix$SPY.Adjusted)
colnames(my_mat) = c('expected shortfall','volatility','semi deviation','max drawdown','sp500')

Created by Pretty R at inside-R.org

Advertisements

4 comments

  1. […] ← Risk Parity using various risk measures (volatility, expected shortfall, semi deviation, maximum dra… […]

  2. Wouldn’t you want to omit SPY from the securities list if you’re comparing the results to SPY?

    1. Good question, the answer no as the goal is to manage risk as opposed to beat a benchmark. Taking tactical exposure in SPY is also valuable from a risk to return standpoint.

Leave a Reply

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

WordPress.com Logo

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

Google photo

You are commenting using your Google 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

%d bloggers like this: