# Effective Number of Bets of Risk Parity Strategies and Tactical Risk Parity by ENB

This post will aim to explore the effective number of bets (https://kylebalkissoon.wordpress.com/2014/04/15/effective-number-of-bets/) of various risk parity strategies (https://kylebalkissoon.wordpress.com/2014/04/14/risk-parity-using-various-risk-measures-volatility-expected-shortfall-semi-deviation-maximum-draw-down/).

Why?

Let’s see if it explains why some strategies outperform and others underperformed. If it is because we are not betting efficiently (ENB<Assets) relative to the other strategies it may indicate that a certain method or type is better or worse than another.

So I took the most recent portfolio weights of the previous risk parity strategy and plugged them into ENB and plotted them. As you can clearly see vol/semi dev have the highest ENB, if we were to compare with performance: This is generally in line with the performance.

If you have been following along you’re probably thinking, that’s great that we know which one is the best backwards looking, but what happens if I wanted to rotate my risk metric by which one gave me the greatest ENB at a point in time?

So let’s rerun the previous risk parity experiment and switch to the most diversified one by enb at any point in time.

Professional Note: I would highly advise against doing this in practice as ENB did not exist for all the time (Meucci’s first paper was in 2009 iirc) and the analytical framework that made me choose ENB as a risk metric incorporates my views on the past, which would essentially be cheating. To properly do this you would need a model to select an optimal risk metric out of a potential candidate set. As you can see the technique clearly outperforms. Managing risk and diversifying can be an effective tool in portfolio construction.

Code for ENB and evaluation

```library(PerformanceAnalytics)
library(quantmod)

Effective_Number_of_bets = function(R,w){
num_assets = ncol(R)
##Calculate covariance matrix
sigma = cov(R)
###Calculate eign vectors E and eigenvalues
eigen_vectors = eigen(sigma)\$vectors
eigen_values = eigen(sigma)\$values

principal_variances = NULL
for( i in 1:ncol(R)){
principal_variances[i] = var(as.numeric(as.vector(eigen_vectors[,i])%*%t(R)))

}
principal_portfolio_return = 1/(eigen_vectors)%*%t(R)
principal_weights = eigen_vectors^-1%*%w

##Calculated Weighted Ret, Rw in Meucci's slides
weighted_ret = t(principal_weights)%*%principal_portfolio_return

##Variance Concentration
var_concentration = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(var_concentration) = c('assets','variance')
for( i in 1:num_assets){
var_concentration[i,]\$assets = i

var_concentration[i,]\$variance = (weighted_ret[i]^2)*principal_variances[i]

}

var_concentration\$variance =  var_concentration\$variance/sum( var_concentration\$variance)

##volatility concentration
volatility_concentration = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(volatility_concentration) = c('assets','volatility')
for( i in 1:num_assets){
volatility_concentration[i,]\$assets = i
volatility_concentration[i,]\$volatility = var_concentration\$variance[i]/sd(weighted_ret)

}

volatility_concentration\$volatility = volatility_concentration\$volatility/sum(volatility_concentration\$volatility)

##Diversification distribtuion "probability mass"
diversification_distribution = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(diversification_distribution) = c('assets','mass')
for( i in 1:num_assets){
diversification_distribution[i,]\$assets = i

diversification_distribution[i,]\$mass = var_concentration\$variance[i]/var(as.numeric(weighted_ret))

}
diversification_distribution\$mass = diversification_distribution\$mass/sum(diversification_distribution\$mass)
###Effective number of bets
effective_number_of_bets = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(effective_number_of_bets) = c('assets','enb')
for( i in 1:num_assets){
effective_number_of_bets\$assets[i] = i

effective_number_of_bets\$enb[i] = exp((-1)*sum(diversification_distribution[1:i,2]*log(diversification_distribution[1:i,2])))

}

ans = list(var_concentration,volatility_concentration,diversification_distribution,effective_number_of_bets)
names(ans) = c('var_con','vol_con','div_dist','enb')
return(ans)}

###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){

}

##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))

colnames(my_mat) = c('expected shortfall','volatility','semi deviation','max drawdown','sp500')

###How efficient are our allocations

es_enb = Effective_Number_of_bets(securities_matrix,as.numeric(last(weight_matrix_es)))
vol_enb = Effective_Number_of_bets(securities_matrix,as.numeric(last(weight_matrix_vol)))
sd_enb = Effective_Number_of_bets(securities_matrix,as.numeric(last(weight_matrix_sd)))
mdd_enb = Effective_Number_of_bets(securities_matrix,as.numeric(last(weight_matrix_mdd)))

matrix_for_step_plots = data.frame(es_enb\$enb[,1],es_enb\$enb[,2],vol_enb\$enb[,2],sd_enb\$enb[,2],mdd_enb\$enb[,2])
colnames(matrix_for_step_plots) = c('assets','ES','Vol','SemiDev','MaxDD')

matplot(x=matrix_for_step_plots\$assets,y=matrix_for_step_plots[,2:5],type='s',xlab='Assets',ylab='effective number of bets')
legend('topleft',legend=c('ES','Vol','SemiDev','MaxDD'),col=1:4,pch=1)
plot(matrix_for_step_plots[,1],matrix_for_step_plots[,2:5])```

Created by Pretty R at inside-R.org

Code for Tactical ENB

```library(PerformanceAnalytics)
library(quantmod)

Effective_Number_of_bets = function(R,w){
num_assets = ncol(R)
##Calculate covariance matrix
sigma = cov(R)
###Calculate eign vectors E and eigenvalues
eigen_vectors = eigen(sigma)\$vectors
eigen_values = eigen(sigma)\$values

principal_variances = NULL
for( i in 1:ncol(R)){
principal_variances[i] = var(as.numeric(as.vector(eigen_vectors[,i])%*%t(R)))

}
principal_portfolio_return = 1/(eigen_vectors)%*%t(R)
principal_weights = eigen_vectors^-1%*%w

##Calculated Weighted Ret, Rw in Meucci's slides
weighted_ret = t(principal_weights)%*%principal_portfolio_return

##Variance Concentration
var_concentration = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(var_concentration) = c('assets','variance')
for( i in 1:num_assets){
var_concentration[i,]\$assets = i

var_concentration[i,]\$variance = (weighted_ret[i]^2)*principal_variances[i]

}

var_concentration\$variance =  var_concentration\$variance/sum( var_concentration\$variance)

##volatility concentration
volatility_concentration = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(volatility_concentration) = c('assets','volatility')
for( i in 1:num_assets){
volatility_concentration[i,]\$assets = i
volatility_concentration[i,]\$volatility = var_concentration\$variance[i]/sd(weighted_ret)

}

volatility_concentration\$volatility = volatility_concentration\$volatility/sum(volatility_concentration\$volatility)

##Diversification distribtuion "probability mass"
diversification_distribution = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(diversification_distribution) = c('assets','mass')
for( i in 1:num_assets){
diversification_distribution[i,]\$assets = i

diversification_distribution[i,]\$mass = var_concentration\$variance[i]/var(as.numeric(weighted_ret))

}
diversification_distribution\$mass = diversification_distribution\$mass/sum(diversification_distribution\$mass)
###Effective number of bets
effective_number_of_bets = data.frame(matrix(ncol=2,nrow=num_assets))
colnames(effective_number_of_bets) = c('assets','enb')
for( i in 1:num_assets){
effective_number_of_bets\$assets[i] = i

effective_number_of_bets\$enb[i] = exp((-1)*sum(diversification_distribution[1:i,2]*log(diversification_distribution[1:i,2])))

}

ans = list(var_concentration,volatility_concentration,diversification_distribution,effective_number_of_bets)
names(ans) = c('var_con','vol_con','div_dist','enb')
return(ans)}

###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){

}

##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))
weight_matrix_enb= xts(matrix(nrow=nrow(securities_matrix),ncol=ncol(securities_matrix)),order.by=index(securities_matrix))
enb_ranks= xts(matrix(nrow=nrow(securities_matrix),ncol=4),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 = last(first(securities_matrix,i-1),252)
##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

##Apply ENB to rank the strategies
##Calculate enb
enb_es = Effective_Number_of_bets(info_set,as.numeric(es_w))\$enb\$enb
enb_vol = Effective_Number_of_bets(info_set,as.numeric(vol_w))\$enb\$enb
enb_sd = Effective_Number_of_bets(info_set,as.numeric(sd_w))\$enb\$enb
enb_mdd = Effective_Number_of_bets(info_set,as.numeric(mdd_w))\$enb\$enb
enb_vec = c(enb_es,enb_vol,enb_sd,enb_mdd)
##Who is the best?
best_enb = which.max(enb_vec)
enb_ranks[i,] = enb_vec
weight_matrix_enb[i,] = get(c('es_w','vol_w','sd_w','mdd_w')[best_enb])

}
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,])

weight_matrix_enb[i,] = weight_matrix_enb[i-1,]*(1+securities_matrix[i-1,])
weight_matrix_enb[i,] = weight_matrix_enb[i,]/sum(weight_matrix_enb[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))
port_enb = xts(rowSums(weight_matrix_enb*securities_matrix),order.by=index(securities_matrix))

colnames(my_mat) = c('expected shortfall','volatility','semi deviation','max drawdown','enb','sp500')
chart.CumReturns(my_mat,legend.loc='topleft',wealth.index=TRUE,main='Performance of various risk parity strategies')```

Created by Pretty R at inside-R.org

1. 