Home > Cluster, R > Cluster Portfolio Allocation

Cluster Portfolio Allocation

Today, I want to continue with clustering theme and show how the portfolio weights are determined in the Cluster Portfolio Allocation method. One example of the Cluster Portfolio Allocation method is Cluster Risk Parity (Varadi, Kapler, 2012).

The Cluster Portfolio Allocation method has 3 steps:

  • Create Clusters
  • Allocate funds within each Cluster
  • Allocate funds across all Clusters

I will illustrate below all 3 steps using “Equal Weight” and “Risk Parity” portfolio allocation methiods. Let’s start by loading historical prices for the 10 major asset classes.

###############################################################################
# Load Systematic Investor Toolbox (SIT)
# https://systematicinvestor.wordpress.com/systematic-investor-toolbox/
###############################################################################
setInternet2(TRUE)
con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb'))
    source(con)
close(con)
	#*****************************************************************
	# Load historical data for ETFs
	#****************************************************************** 
	load.packages('quantmod')

	tickers = spl('GLD,UUP,SPY,QQQ,IWM,EEM,EFA,IYR,USO,TLT')

	data <- new.env()
	getSymbols(tickers, src = 'yahoo', from = '1900-01-01', env = data, auto.assign = T)
		for(i in ls(data)) data[[i]] = adjustOHLC(data[[i]], use.Adjusted=T)
		
	bt.prep(data, align='remove.na')

	#*****************************************************************
	# Setup
	#****************************************************************** 
	# compute returns
	ret = data$prices / mlag(data$prices) - 1

	# setup period
	dates = '2012::2012'
	ret = ret[dates]

Next, let’s compute “Plain” portfolio allocation (i.e. no Clustering)

	fn.name = 'equal.weight.portfolio'				
	fn = match.fun(fn.name)

	# create input assumptions
	ia = create.historical.ia(ret, 252) 
	
	# compute allocation without cluster, for comparison
	weight = fn(ia)

Next, let’s create clusters and compute portfolio allocation within each Cluster

	# create clusters
	group = cluster.group.kmeans.90(ia)
	ngroups = max(group)

	weight0 = rep(NA, ia$n)
			
	# store returns for each cluster
	hist.g = NA * ia$hist.returns[,1:ngroups]
			
	# compute weights within each group	
	for(g in 1:ngroups) {
		if( sum(group == g) == 1 ) {
			weight0[group == g] = 1
			hist.g[,g] = ia$hist.returns[, group == g, drop=F]
		} else {
			# create input assumptions for the assets in this cluster
			ia.temp = create.historical.ia(ia$hist.returns[, group == g, drop=F], 252) 

			# compute allocation within cluster
			w0 = fn(ia.temp)
			
			# set appropriate weights
			weight0[group == g] = w0
			
			# compute historical returns for this cluster
			hist.g[,g] = ia.temp$hist.returns %*% w0
		}
	}

Next, let’s compute portfolio allocation across all Clusters and compute final portfolio weights

	# create GROUP input assumptions
	ia.g = create.historical.ia(hist.g, 252) 
			
	# compute allocation across clusters
	group.weights = fn(ia.g)
				
	# mutliply out group.weights by within group weights
	for(g in 1:ngroups)
		weight0[group == g] = weight0[group == g] * group.weights[g]

Finally, let’s create reports and compare portfolio allocations

	#*****************************************************************
	# Create Report
	#****************************************************************** 			
	load.packages('RColorBrewer')
	col = colorRampPalette(brewer.pal(9,'Set1'))(ia$n)

	layout(matrix(1:2,nr=2,nc=1))
	par(mar = c(0,0,2,0))
	index = order(group)

	pie(weight[index], labels = paste(colnames(ret), round(100*weight,1),'%')[index], col=col, main=fn.name)

	pie(weight0[index], labels = paste(colnames(ret), round(100*weight0,1),'%')[index], col=col, main=paste('Cluster',fn.name))	

equal.weight.portfolio.plot.png.small

The difference is most striking in the “Equal Weight” portfolio allocation method. The Cluster version allocates 25% to each cluster first, and then allocates equally within each cluster. The Plain version allocates equally among all assets. The “Risk Parity” version below works in similar way, but instead of having equal weights, the focus is on the equal risk allocations. I.e. UUP gets a much bigger allocation because it is far less risky than any other asset.

risk.parity.portfolio.plot.png.small

Next week, I will show how to back-test Cluster Portfolio Allocation methods.

To view the complete source code for this example, please have a look at the bt.cluster.portfolio.allocation.test() function in bt.test.r at github.

Categories: Cluster, R
  1. Alex
    February 12, 2013 at 7:39 am

    I’m still not understanding why a clustering approach is preferred to a minimum variance approach that considers the entire co-variance matrix of each asset, optimizing allocations to minimize total risk. Or, perhaps another way, applying risk-parity to orthogonal risk factors based on principle component analysis on the assets.

    • February 16, 2013 at 1:32 am

      The main focus of clustering portfolio allocation is diversification. So if you want to get a portfolio with minimum variance, you should use minimum variance approach. The clustering portfolio allocation method will not produce the portfolio with minimum variance.

      On the other hand, if you want to create a portfolio that distributes risk equally within clusters and across clusters (i.e. diversify you risk bets) than you should use clustering portfolio allocation method.

      One way to think about clustering portfolio allocation – is that one of the methods you can use to create portfolios. For example, if you want minimum risk, please use minimum variance approach. If you want to diversify risk allocations, please use clustering portfolio allocation method.

      • Alex
        February 16, 2013 at 2:38 am

        Thanks for the reply

  2. Andreas
    February 12, 2013 at 8:36 am

    Well done! Thanks for sharing.

  3. August 3, 2014 at 5:46 am

    Is it possible (and if so, do you think it would create any value) to create a framework where a minimum variance allocation is applied to clusters?

    In this use case, the primary objective is to diversify factor risks by clustering similar strategies together (hence reducing the potential for overweighting a to any subset of highly coorellated strategies). The secondary objective is to then achieve the lowest risk allocation combination to each “strategy cluster”. This could be done by treating each clustered sub portfolio as an asset and running the MVO to determine cluster weight.

    For example:

    Consider a fund of funds or proprietary trading group with many individual traders. They may have many correlated strategies trading the same underlying index (or even equities in the same sector). You would not know the specific approach but could identify correlation.

    It may be desirable to first diversify factor risk by clustering the most correlated returns together. Next, the master account could properly allocate % of buying power to each “strategy cluster” (group of traders or family of funds) to achieve Min Variance. Lastly, target volatility could be used to set leverage targets at the master account level.

    Damian

  1. No trackbacks yet.

Leave a comment