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?
Answer
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)
# Setup quadratic problem
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%
No comments:
Post a Comment