{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Portfolio optimization " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Portfolio allocation vector\n", "\n", "In this example we show how to do portfolio optimization using CVXPY.\n", "We begin with the basic definitions.\n", "In portfolio optimization we have some amount of money to invest in any of $n$ different assets. \n", "We choose what fraction $w_i$ of our money to invest in each asset $i$, $i=1, \\ldots, n$.\n", "\n", "We call $w\\in {\\bf R}^n$ the *portfolio allocation vector*.\n", "We of course have the constraint that ${\\mathbf 1}^T w =1$.\n", "The allocation $w_i<0$ means a *short position* in asset $i$, or that we borrow shares to sell now that we must replace later.\n", "The allocation $w \\geq 0$ is a *long only* portfolio.\n", "The quantity\n", "$$\\|w \\|_1 = {\\mathbf 1}^T w_+ + {\\mathbf 1}^T w_-$$\n", "is known as *leverage*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Asset returns\n", "\n", "We will only model investments held for one period. The initial prices are $p_i > 0$. The end of period prices are $p_i^+ >0$. The asset (fractional) returns are $r_i = (p_i^+-p_i)/p_i$. The porfolio (fractional) return is $R = r^Tw$.\n", "\n", "A common model is that $r$ is a random variable with mean ${\\bf E}r = \\mu$ and covariance ${\\bf E{(r-\\mu)(r-\\mu)^T}} = \\Sigma$.\n", "It follows that $R$ is a random variable with ${\\bf E}R = \\mu^T w$ and ${\\bf var}(R) = w^T\\Sigma w$.\n", "${\\bf E}R$ is the (mean) *return* of the portfolio. ${\\bf var}(R)$ is the *risk* of the portfolio.\n", "(Risk is also sometimes given as ${\\bf std}(R) = \\sqrt{{\\bf var}(R)}$.)\n", "\n", "Portfolio optimization has two competing objectives: high return and low risk." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Classical (Markowitz) portfolio optimization\n", "\n", "Classical (Markowitz) portfolio optimization solves the optimization problem\n", "\n", "\\begin{array}{ll} \\mbox{maximize} & \\mu^T w - \\gamma w^T\\Sigma w\\\\\n", "\\mbox{subject to} & {\\bf 1}^T w = 1, \\quad w \\in {\\cal W},\n", "\\end{array}\n", "\n", "where $w \\in {\\bf R}^n$ is the optimization variable, $\\cal W$ is a set of allowed portfolios (e.g., ${\\cal W} = {\\bf R}_+^n$ for a long only portfolio), and $\\gamma >0$ is the *risk aversion parameter*.\n", "\n", "The objective $\\mu^Tw - \\gamma w^T\\Sigma w$ is the *risk-adjusted return*. Varying $\\gamma$ gives the optimal *risk-return trade-off*. \n", "We can get the same risk-return trade-off by fixing return and minimizing risk." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example\n", "\n", "In the following code we compute and plot the optimal risk-return trade-off for $10$ assets, restricting ourselves to a long only portfolio." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Generate data for long only portfolio optimization.\n", "import numpy as np\n", "import scipy.sparse as sp\n", "np.random.seed(1)\n", "n = 10\n", "mu = np.abs(np.random.randn(n, 1))\n", "Sigma = np.random.randn(n, n)\n", "Sigma = Sigma.T.dot(Sigma)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Long only portfolio optimization.\n", "import cvxpy as cp\n", "\n", "\n", "w = cp.Variable(n)\n", "gamma = cp.Parameter(nonneg=True)\n", "ret = mu.T@w \n", "risk = cp.quad_form(w, Sigma)\n", "prob = cp.Problem(cp.Maximize(ret - gamma*risk), \n", " [cp.sum(w) == 1, \n", " w >= 0])" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Compute trade-off curve.\n", "SAMPLES = 100\n", "risk_data = np.zeros(SAMPLES)\n", "ret_data = np.zeros(SAMPLES)\n", "gamma_vals = np.logspace(-2, 3, num=SAMPLES)\n", "for i in range(SAMPLES):\n", " gamma.value = gamma_vals[i]\n", " prob.solve()\n", " risk_data[i] = cp.sqrt(risk).value\n", " ret_data[i] = ret.value" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n" ], "text/plain": [ "