Portfolio optimization ====================== Portfolio optimization seeks to allocate assets in a way that maximizes the risk adjusted return, .. math:: \begin{array}{ll} \mbox{maximize} & \mu^T x - \gamma \left( x^T \Sigma x \right) \\ \mbox{subject to} & \boldsymbol{1}^T x = 1 \\ & x \ge 0 \end{array} where :math:`x \in \mathbf{R}^{n}` represents the portfolio, :math:`\mu \in \mathbf{R}^{n}` the vector of expected returns, :math:`\gamma > 0` the risk aversion parameter, and :math:`\Sigma \in \mathbf{S}^{n}_{+}` the risk model covariance matrix. The risk model is usually assumed to be the sum of a diagonal and a rank :math:`k < n` matrix, .. math:: \Sigma = F F^T + D, where :math:`F \in \mathbf{R}^{n \times k}` is the factor loading matrix and :math:`D \in \mathbf{S}^{n}_{+}` is a diagonal matrix describing the asset-specific risk. The resulting problem has the following equivalent form, .. math:: \begin{array}{ll} \mbox{minimize} & \frac{1}{2} x^T D x + \frac{1}{2} y^T y - \frac{1}{2\gamma}\mu^T x \\ \mbox{subject to} & y = F^T x \\ & \boldsymbol{1}^T x = 1 \\ & x \ge 0 \end{array} Python ------ .. code:: python import osqp import numpy as np import scipy as sp from scipy import sparse # Generate problem data sp.random.seed(1) n = 100 k = 10 F = sparse.random(n, k, density=0.7, format='csc') D = sparse.diags(np.random.rand(n) * np.sqrt(k), format='csc') mu = np.random.randn(n) gamma = 1 # OSQP data P = sparse.block_diag([D, sparse.eye(k)], format='csc') q = np.hstack([-mu / (2*gamma), np.zeros(k)]) A = sparse.vstack([ sparse.hstack([F.T, -sparse.eye(k)]), sparse.hstack([sparse.csc_matrix(np.ones((1, n))), sparse.csc_matrix((1, k))]), sparse.hstack((sparse.eye(n), sparse.csc_matrix((n, k)))) ], format='csc') l = np.hstack([np.zeros(k), 1., np.zeros(n)]) u = np.hstack([np.zeros(k), 1., np.ones(n)]) # Create an OSQP object prob = osqp.OSQP() # Setup workspace prob.setup(P, q, A, l, u) # Solve problem res = prob.solve() Matlab ------ .. code:: matlab % Generate problem data rng(1) n = 100; k = 10; F = sprandn(n, k, 0.7); D = sparse(diag( sqrt(k)*rand(n,1) )); mu = randn(n, 1); gamma = 1; % OSQP data P = blkdiag(D, speye(k)); q = [-mu/(2*gamma); zeros(k, 1)]; A = [F', -speye(k); ones(1, n), zeros(1, k); speye(n), sparse(n, k)]; l = [zeros(k, 1); 1; zeros(n, 1)]; u = [zeros(k, 1); 1; ones(n, 1)]; % Create an OSQP object prob = osqp; % Setup workspace prob.setup(P, q, A, l, u); % Solve problem res = prob.solve(); CVXPY ----- .. code:: python from cvxpy import * import numpy as np import scipy as sp from scipy import sparse # Generate problem data sp.random.seed(1) n = 100 k = 10 F = sparse.random(n, k, density=0.7, format='csc') D = sparse.diags(np.random.rand(n) * np.sqrt(k), format='csc') mu = np.random.randn(n) gamma = 1 Sigma = F*F.T + D # Define problem x = Variable(n) objective = mu.T*x - gamma*quad_form(x, Sigma) constraints = [sum(x) == 1, x >= 0] # Solve with OSQP Problem(Maximize(objective), constraints).solve(solver=OSQP) YALMIP ------ .. code:: matlab % Generate problem data rng(1) n = 100; k = 10; F = sprandn(n, k, 0.7); D = sparse(diag( sqrt(k)*rand(n,1) )); mu = randn(n, 1); gamma = 1; Sigma = F*F' + D; % Define problem x = sdpvar(n, 1); objective = gamma * (x'*Sigma*x) - mu'*x; constraints = [sum(x) == 1, x >= 0]; % Solve with OSQP options = sdpsettings('solver', 'osqp'); optimize(constraints, objective, options);