# Optimizing a portfolio of ETFs

I am aware of how to do mean-variance or minimum-variance portfolio optimization with constraints like

• weights must add to 1.0
• no short sells
• max weight in any ticker

using basic quadratic programming techniques. However I am stumped by the following:

My universe of tickers consists of ETFs. Say I want constraints of the form:

• no more than 20% of the portfolio in bond funds (there are many possible bond funds)
• no more than 30% of the portfolio in funds of one asset class (e.g. real estate)

How can I run such a portfolio optimization?

• What software are you using for optimization? Mar 18, 2012 at 1:28
• I use R - some quadratic programming Mar 18, 2012 at 1:35
• I'm sorry but this is off-topic. This is the 101 example for any financial optimization class: protfolio optimization with linear constraints. Please see the FAQ.
– SRKX
Mar 18, 2012 at 10:00
• The question is about something a tad bit more complex than a simple linear constraint, see the reply below - adding a binary exposure vector is not so trival. Mar 19, 2012 at 5:40
• Although you're not using Matlab, the Mathworks website has a multitude of examples that cover portfolio optimization and constraint specification using the financial toolbox that you might find useful. Function parameters for quadprog differ slightly between the two platforms, but the concepts are essentially the same. Mar 19, 2012 at 14:29

Using solve.QP in R, a straightforward approach is to add a binary exposure vector as an inequality constraint to your Amat matrix for each group that you want to constrain.

The only catch is that values in the exposure and b_0 vectors should be negative, since the function is really satisfying the constraints: A^T b >= b_0.

For a simple mean-variance example with two groups that we want to constrain:

library(quadprog)
library(MASS)

# Generate some returns
set.seed(100)
n <- 100   # number of assets
m <- 200   # number of states of the world
rho <- 0.7
sigma <- 0.2
mu <- .1
Cov <- matrix(rho*sigma*sigma, ncol=n, nrow=n)
diag(Cov) <- rep(sigma*sigma, n)
S <- 1 + matrix(mvrnorm(m, rep(mu, n), Sigma=Cov), ncol=n)

# Calculate a covariance matrix
Cov <- var(S)

mu <- apply(S, 2, mean)
mu.target <- mean(mu)
bLo <- rep(0, n)

# Define group membership (arbitrary example)
group1 <- matrix(0,100)
group2 <- matrix(0,100)
group3 <- matrix(0,100)

group1[mu <= mean(mu) - .005] <- -1
group2[mu > (mean(mu) - .005) & mu <= (mean(mu) + .005)] <- -1
group3[mu > mean(mu) + .005] <- -1

Amat <- rbind(1, mu)
dim(bLo) <- c(n,1)
bvec <- t(rbind(1, mu.target, bLo))
zMat <- diag(n)
Amat <- t(rbind(Amat, zMat))
Dmat <- Cov
dvec <- rep(0, nrow(Amat))
meq <- 2  # the first two columns are equality constraints

sol <- solve.QP(Dmat=Dmat, dvec=dvec, Amat=Amat, bvec=bvec, meq)

cat(paste("Without group constraints:\n"))
data.frame(Group1=sum(sol$solution * -group1), Group2=sum(sol$solution * -group2), Group3=sum(sol$solution * -group3)) # Add group constraints: # Group1 <= 20% # Group2 <= 30% Amat <- rbind(1, mu, t(group1), t(group2)) dim(bLo) <- c(n,1) bvec <- t(rbind(1, mu.target, -.20, -.30, bLo)) zMat <- diag(n) Amat <- t(rbind(Amat, zMat)) Dmat <- Cov dvec <- rep(0, nrow(Amat)) sol <- solve.QP(Dmat=Dmat, dvec=dvec, Amat=Amat, bvec=bvec, meq) cat(paste("With group constraints:\n")) data.frame(Group1=sum(sol$solution * -group1), Group2=sum(sol$solution * -group2), Group3=sum(sol$solution * -group3))


Group weights:

                     1      2      3
Without constraints  26.4%  53.1%  20.4%
With constraints     20.0%  30.0%  50.0%

• This worked perfectly, thanks so much this was excellent help. Mar 23, 2012 at 7:07