Computing the minimum-variance portfolio

This tutorial appeared originally on the COMISEF Wiki.

1 The problem

The global minimum-variance (MV) portfolio is the leftmost point of the mean–variance efficient frontier. It is found by choosing portfolio weights that minimise overall variance subject only to the constraint that the sum of the weights \(w\) is one. Formally,

\begin{equation} \min_{w}\ \ w' \Sigma w\\ w'\iota = 1 \end{equation}

where \(w\) is the vector of portfolio weights, \(\Sigma\) is the variance-covariance matrix of the assets, and \(\iota\) is an appropriately-sized vector of ones. We do not put any further constraints on the problem; short sales, in particular, are allowed here.

As an example dataset, we create artificial returns with mean zero (the default with rnorm) and volatility 5%. (This order of magnitude would be reasonable for monthly equity returns.) These returns are stored in a matrix mData.

nO <- 100L  ## number of observations
nA <- 10L   ## number of assets
mData <- array(rnorm(nO * nA, sd = 0.05), 
               dim = c(nO, nA))

2 A quadratic-programming solution

We first use the solve.QP function of package quadprog (see the references below), so we load/attach the package.

library("quadprog")

The aim will be to minimise w %*% cov(mData) %*% w, subject to the constraint that sum(w) equals one.

aMat <- array(1, dim = c(1,nA))
bVec <- 1
zeros <- array(0, dim = c(nA,1))
solQP <- solve.QP(cov(mData), zeros, t(aMat), bVec, meq = 1)
solQP$solution
[1] 0.07853404 0.12853164 0.14526751 0.09211099 0.11893845
[6] 0.15176486 0.12447023 0.02945822 0.06584904 0.06507502

The quadprod package will minimise \(\frac{1}{2} w' \Sigma w\), i.e. one-half of the portfolio variance (see ?solve.QP). Hence the returned minimum-value will be one-half of the portfolio's variance. Check:

all.equal(drop(var(mData %*% solQP$solution)),  ## the solution, evaluated at the data
          2*solQP$value)                        ## the solution's objective fun value times 2
[1] TRUE

This computation is also included as function minvar in the NMOF package.

require("NMOF")
minvar(cov(mData), wmin = -Inf, wmax = Inf)
 [1] 0.07853404 0.12853164 0.14526751 0.09211099 0.11893845
 [6] 0.15176486 0.12447023 0.02945822 0.06584904 0.06507502
attr(,"variance")
[1] 0.0002269999

Note that NMOF's minvar also returns the portfolio variance.

drop(var(mData %*% solQP$solution))
[1] 0.0002269999

3 A regression solution

The problem can also be solved as a linear regression; see Kempf and Memmel (2006). The authors show that regressing the negative excess returns of \(r_{\mathrm{n_A}} - 1\) assets on the returns of the remaining asset results in coefficients that equal the \(r_{\mathrm{n_A}} - 1\) respective portfolio weights; the remaining asset's weight is determined by the budget constraint. (Excess return here means with respect to the remaining asset.)

We run the regression

\begin{equation} r_{\mathrm{n_A}} = \alpha + w_1 (r_{\mathrm{n_A}} - r_1) + w_2 (r_{\mathrm{n_A}} - r_2) + \cdots + w_{\mathrm{n_A}-1}(r_{\mathrm{n_A}}-r_{\mathrm{n_A}-1}) + \epsilon\,, \end{equation}

in which \(r_i\) are the returns of the i-th asset, and \(\epsilon\) are the errors.

From this equation, we directly obtain the weights \(w_1\) to \(w_{\mathrm{n_A}-1}\). Since the weights need to sum to unity, we have

\begin{equation} w_{\mathrm{n_A}} = 1 - \sum_{i=1}^{\mathrm{n_A}-1} w_i\,. \end{equation}

Furthermore, the resulting portfolio's mean return will equal \(\alpha\) (the constant in the regression), and the portfolio's variance will equal the variance of the residuals.

## choose 1st asset as regressand
y <- mData[ , 1L]

## compute minus excess returns
X <- mData[ , 1L] - mData[ , 2:nA]

## run regression
solR <- lm(y ~ X)

The results should be the same as the results from quadprog:

cat("weights from qp\n")
as.vector(solQP$solution)

cat("\nweights from regression\n")
as.vector(c(1 - sum(coef(solR)[-1L]), coef(solR)[-1L]))

cat("\ncheck: variance of portfolio same as variance of residuals?\n")
all.equal(as.numeric(var(mData %*% solQP$solution)), var(solR$residuals))
weights from qp
 [1] 0.07853404 0.12853164 0.14526751 0.09211099 0.11893845
 [6] 0.15176486 0.12447023 0.02945822 0.06584904 0.06507502

weights from regression
 [1] 0.07853404 0.12853164 0.14526751 0.09211099 0.11893845
 [6] 0.15176486 0.12447023 0.02945822 0.06584904 0.06507502

check: variance of portfolio same as variance of residuals?
[1] TRUE

4 Solving a system of linear equations

The weights can also be found by solving a system of linear equations.

x <- solve(cov(mData), numeric(nA) + 1)
x <- x / sum(x) ## rescale so that sum(w) is 1
x
[1] 0.07853404 0.12853164 0.14526751 0.09211099 0.11893845
[6] 0.15176486 0.12447023 0.02945822 0.06584904 0.06507502

5 References

  • Gilli, M., Maringer, D., Schumann, E. (2011) . Numerical Methods and Optimization in Finance. Elsevier/Academic Press. http://nmof.net
  • Kempf, A. and C. Memmel (2006). Estimating the Global Minimum Variance Portfolio. Schmalenbach Business Review 58, 332-348.
  • R Development Core Team (2008). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing. http://www.r-project.org.
  • Turlach, B.A. [S original] and A. Weingessel [R port] (2007). quadprog: Functions to solve Quadratic Programming Problems. R package version 1.4-11. available from CRAN.
return to main page...