Source code for yabte.utilities.portopt.minimum_variance

r"""Calculate portfolio weights by minimizing variance.

That is to minimize the following expression subject to various constraints.

.. math::

   \frac{1}{2} w' \Sigma w

Typical constraints are achieving a target return, i.e. :math:`w' \mu = r`, and that
all weights sum to one, i.e. :math:`\Sigma_i w_i = 1`.
"""

import numpy as np
import numpy.linalg as la

from ..lagrangian import Lagrangian


[docs]def minimum_variance(Sigma: np.ndarray, mu: np.ndarray, r: float) -> np.ndarray: """Calculate weights using Lagrangian multipliers and algebraic closed form solution.""" m = len(mu) ones = np.ones(m) SigmaInv = la.inv(Sigma) A = mu.T @ SigmaInv @ ones B = mu.T @ SigmaInv @ mu C = ones.T @ SigmaInv @ ones D = B * C - A * A l1 = (C * r - A) / D l2 = (B - A * r) / D return SigmaInv @ (l1 * mu + l2 * ones)
[docs]def minimum_variance_numeric(Sigma: np.ndarray, mu: np.ndarray, r: float) -> np.ndarray: """Calculate weights using Lagrangian multipliers and numeric solution (using scipy's root function).""" m = len(mu) ones = np.ones(m) L = Lagrangian( objective=lambda x: x.T @ Sigma @ x / 2, constraints=[ lambda x: r - x.T @ mu, lambda x: 1 - x.T @ ones, ], x0=ones / m, ) return L.fit()
[docs]def minimum_variance_numeric_slsqp( Sigma: np.ndarray, mu: np.ndarray, r: float ) -> np.ndarray: """Calculate weights using Lagrangian multipliers and numeric solution (using scipy's minimize function).""" from scipy.optimize import minimize m = len(mu) ones = np.ones(m) res = minimize( lambda x: x.T @ Sigma @ x / 2, ones / m, method="SLSQP", constraints=( {"type": "eq", "fun": lambda x: r - x.T @ mu, "jac": lambda x: -mu.values}, {"type": "eq", "fun": lambda x: 1 - x.T @ ones, "jac": lambda x: -ones}, ), tol=1e-15, ) return res.x