Wednesday, April 18, 2018

optimization - 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?



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

technique - How credible is wikipedia?

I understand that this question relates more to wikipedia than it does writing but... If I was going to use wikipedia for a source for a res...