Coverage for src / cvx / risk / portfolio / min_risk.py: 100%
7 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-15 12:21 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-15 12:21 +0000
1"""Minimum risk portfolio optimization.
3This module provides functions for creating and solving minimum risk portfolio
4optimization problems using various risk models.
6Example:
7 Create and solve a minimum risk portfolio problem:
9 >>> import cvxpy as cp
10 >>> import numpy as np
11 >>> from cvx.risk.sample import SampleCovariance
12 >>> from cvx.risk.portfolio import minrisk_problem
13 >>> # Create risk model
14 >>> model = SampleCovariance(num=3)
15 >>> model.update(
16 ... cov=np.array([[1.0, 0.5, 0.0], [0.5, 1.0, 0.5], [0.0, 0.5, 1.0]]),
17 ... lower_assets=np.zeros(3),
18 ... upper_assets=np.ones(3)
19 ... )
20 >>> # Create optimization problem
21 >>> weights = cp.Variable(3)
22 >>> problem = minrisk_problem(model, weights)
23 >>> # Solve the problem
24 >>> _ = problem.solve(solver="CLARABEL")
25 >>> # Optimal weights sum to 1
26 >>> bool(np.isclose(np.sum(weights.value), 1.0))
27 True
29"""
31# Copyright 2023 Stanford University Convex Optimization Group
32#
33# Licensed under the Apache License, Version 2.0 (the "License");
34# you may not use this file except in compliance with the License.
35# You may obtain a copy of the License at
36#
37# http://www.apache.org/licenses/LICENSE-2.0
38#
39# Unless required by applicable law or agreed to in writing, software
40# distributed under the License is distributed on an "AS IS" BASIS,
41# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42# See the License for the specific language governing permissions and
43# limitations under the License.
44from __future__ import annotations
46import cvxpy as cp
48from cvx.risk import Model
51def minrisk_problem(
52 riskmodel: Model,
53 weights: cp.Variable,
54 base: cp.Expression | float = 0.0,
55 constraints: list[cp.Constraint] | None = None,
56 **kwargs,
57) -> cp.Problem:
58 """Create a minimum-risk portfolio optimization problem.
60 This function creates a CVXPY optimization problem that minimizes portfolio
61 risk subject to constraints. The problem includes standard constraints
62 (weights sum to 1, weights are non-negative) plus any model-specific and
63 custom constraints.
65 Args:
66 riskmodel: A risk model implementing the `Model` interface, used to
67 compute portfolio risk. Can be SampleCovariance, FactorModel,
68 CVar, etc.
69 weights: CVXPY variable representing the portfolio weights. Should have
70 shape (n,) where n is the number of assets.
71 base: Expression representing the base portfolio (default 0.0). Use this
72 for tracking error minimization where you want to minimize the risk
73 of deviating from a benchmark.
74 constraints: Optional list of additional CVXPY constraints to apply to
75 the optimization problem.
76 **kwargs: Additional keyword arguments passed to the risk model's
77 estimate and constraints methods.
79 Returns:
80 A CVXPY Problem that minimizes portfolio risk subject to constraints.
81 The problem includes:
82 - Objective: minimize risk(weights - base)
83 - Constraint: sum(weights) == 1
84 - Constraint: weights >= 0
85 - Model-specific constraints from riskmodel.constraints()
86 - Any additional constraints passed in the constraints argument
88 Example:
89 Basic minimum risk portfolio:
91 >>> import cvxpy as cp
92 >>> import numpy as np
93 >>> from cvx.risk.sample import SampleCovariance
94 >>> from cvx.risk.portfolio import minrisk_problem
95 >>> model = SampleCovariance(num=2)
96 >>> model.update(
97 ... cov=np.array([[1.0, 0.5], [0.5, 2.0]]),
98 ... lower_assets=np.zeros(2),
99 ... upper_assets=np.ones(2)
100 ... )
101 >>> weights = cp.Variable(2)
102 >>> problem = minrisk_problem(model, weights)
103 >>> _ = problem.solve(solver="CLARABEL")
104 >>> # Lower variance asset gets higher weight
105 >>> bool(weights.value[0] > weights.value[1])
106 True
108 With tracking error (minimize deviation from benchmark):
110 >>> benchmark = np.array([0.5, 0.5])
111 >>> problem = minrisk_problem(model, weights, base=benchmark)
112 >>> _ = problem.solve(solver="CLARABEL")
114 With custom constraints:
116 >>> custom_constraints = [weights[0] >= 0.3] # At least 30% in first asset
117 >>> problem = minrisk_problem(model, weights, constraints=custom_constraints)
118 >>> _ = problem.solve(solver="CLARABEL")
119 >>> bool(weights.value[0] >= 0.3 - 1e-6)
120 True
122 """
123 # if no constraints are specified
124 constraints = constraints or []
126 problem = cp.Problem(
127 objective=cp.Minimize(riskmodel.estimate(weights - base, **kwargs)),
128 constraints=[cp.sum(weights) == 1.0, weights >= 0] + riskmodel.constraints(weights, **kwargs) + constraints,
129 )
131 return problem