August 7, 2013

Markowitz Optimization with CVXOPT

Let $\mu$ be the expected return vector and $\Sigma$ be the return covariance matrix, then Markowitz seeks to minimize the portfolio variance while achieving a given portfolio return $\mu^*$
$$
\begin{align}
\text{minimize}& w^\T \Sigma w \\
\text{s.t.}& w_i \geq 0 \\
& \sum w_i = 1 \\
& \sum \mu_i w_i = \mu^*
\end{align}
$$
This is a quadratic programming (QP) problem which can easily be solved in Python using the cvxopt library.
def markowitz(mu, sigma, mu_p):
 def iif(cond, iftrue=1.0, iffalse=0.0):
  if cond:
   return iftrue
  else:
   return iffalse

 from cvxopt import matrix, solvers
 #solvers.options['show_progress'] = False

 d = len(sigma)

 # P and q determine the objective function to minimize
 # which in cvxopt is defined as $.5 x^T P x + q^T x$
 P = matrix(sigma)
 q = matrix([0.0 for i in range(d)])

 # G and h determine the inequality constraints in the
 # form $G x \leq h$. We write $w_i \geq 0$ as $-1 \times x_i \leq 0$
 # and also add a (superfluous) $x_i \leq 1$ constraint
 G = matrix([
  [ (-1.0)**(1+j%2) * iif(i == j/2) for i in range(d) ]
  for j in range(2*d)
 ]).trans()
 h = matrix([ iif(j % 2) for j in range(2*d) ])

 # A and b determine the equality constraints defined
        # as A x = b
 A = matrix([
  [ 1.0 for i in range(d) ],
  mu
 ]).trans()
 b = matrix([ 1.0, float(mu_p) ])

 sol = solvers.qp(P, q, G, h, A, b)

 if sol['status'] != 'optimal':
  raise Exception("Could not solve problem.")

 w = list(sol['x'])
 f = 2.0*sol['primal objective']

 return {'w': w, 'f': f, 'args': (P, q, G, h, A, b), 'result': sol }
Using my ExcelPython library to call the function from a spreadsheet, I tried calculating the portfolio weights and efficient frontier with parameters
$$
\begin{align}
\mu &= \paren{ \begin{matrix} 4\% \\ 5\% \\ 6\%  \end{matrix} } &
\Sigma &= \paren{ \begin{matrix}
20\% & -9\% & 12\% \\
-9\% & 15\% & -11\% \\
12\% & -11\% & 30\%
\end{matrix} }
\end{align}
$$
with $\mu^*$ varying from 4% to 6%. Here's the result