How to compute the tangency portfolio

Enrico Schumann

## Keywords

portfolio optimisation

draft

# Introduction

The tangency portfolio is the portfolio that maximises the Sharpe ratio, ie, it is obtained from

(1)
\begin{split} \max_{w} \frac{\mu'w-r_f}{\sqrt{w' \Sigma w}}\\ w'\iota = 1 \end{split}

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. The vector $\mu$ holds the assets' means, $$r_f$$ is the riskfree rate (assumed to be a constant). There are no further constraints on the problem, in particular, short sales are allowed here.

The optimisation makes sense only if there exists at least ones portfolio for which $\mu'w$ is larger than $r_f$; otherwise, excess return is negative and no one would want to hold risky assets.

# Using a QP solver

## R

We use the quadprog package for R (see the references below).

require(quadprog)

# create artifical data
nO     <- 100     # number of observations
nA     <- 10      # number of assets
mData  <- array(rnorm(nO * nA, mean = 0.001, sd = 0.01), dim = c(nO, nA))
rf     <- 0.0001     # riskfree rate (2.5% pa)
mu     <- apply(mData, 2, mean)    # means
mu2    <- mu - rf                  # excess means

# qp
aMat  <- as.matrix(mu2)
bVec  <- 1
zeros <- array(0, dim = c(nA,1))
solQP <- solve.QP(cov(mData), zeros, aMat, bVec, meq = 1)

# rescale variables to obtain weights
w <- as.matrix(solQP$solution/sum(solQP$solution))

# compute sharpe ratio
SR <- t(w) %*% mu2 / sqrt(t(w) %*% cov(mData) %*% w)


# A regression representation

Britten-Jones [1] showed that the same results can be obtained by running the following regression

(2)
\begin{align} 1 = Xw + \varepsilon\,. \end{align}

Here $1$ is a vector of ones, $X$ holds in its columns the excess returns of the assets (each row is one observation), and $\varepsilon$ holds the remaining errors.

The obtained regression weights need to be rescaled, ie,

(3)
\begin{align} w_i = \frac{w_i}{\sum w} \quad \text{for all $i$}\,. \end{align}

## R

# (...continued)

##
# regression

# compute excess returns
X     <- mData - rf
ones     <- array(1, dim = c(100,1))

# run regression
solR     <- lm(ones~-1 + X)

# rescale
w2 <- coef(solR)
w2 <- w2/sum(w2)


# Solving a system of linear equations

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

## R

# (...continued)

# solve
w3 <- solve(cov(mData),mu2)

# rescale
w3 <- w3/sum(w3)


All three methods should give the same results:

# (...continued)

#qp
as.vector(w)

# regression
as.vector(w2)

# linear equations
as.vector(w3)

# check
all.equal(as.vector(w),as.vector(w2))
all.equal(as.vector(w),as.vector(w3))
all.equal(as.vector(w2),as.vector(w3))