Coverage for src / cvx / risk / model.py: 100%
15 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-09 03:39 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-09 03:39 +0000
1# Copyright 2023 Stanford University Convex Optimization Group
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Abstract risk model.
16This module provides the abstract base class for all risk models in cvxrisk.
17Risk models are used to estimate portfolio risk and provide constraints for
18portfolio optimization problems.
20Example:
21 All risk models inherit from the Model class and must implement
22 the estimate, update, and constraints methods:
24 >>> import cvxpy as cp
25 >>> import numpy as np
26 >>> from cvx.risk.sample import SampleCovariance
27 >>> # Create a sample covariance risk model
28 >>> model = SampleCovariance(num=3)
29 >>> # Update the model with a covariance matrix
30 >>> cov = np.array([[1.0, 0.5, 0.0], [0.5, 1.0, 0.5], [0.0, 0.5, 1.0]])
31 >>> model.update(cov=cov, lower_assets=np.zeros(3), upper_assets=np.ones(3))
32 >>> # Create a weights variable and estimate risk
33 >>> weights = cp.Variable(3)
34 >>> risk_expr = model.estimate(weights)
35 >>> isinstance(risk_expr, cp.Expression)
36 True
38"""
40from __future__ import annotations
42from abc import ABC, abstractmethod
43from dataclasses import dataclass, field
44from typing import Any
46import cvxpy as cp
49@dataclass
50class Model(ABC):
51 """Abstract base class for risk models.
53 This class defines the interface that all risk models must implement.
54 Risk models are used in portfolio optimization to estimate portfolio risk
55 and provide constraints for the optimization problem.
57 Attributes:
58 parameter: Dictionary mapping parameter names to CVXPY Parameter objects.
59 These parameters can be updated without reconstructing the optimization problem.
61 Example:
62 Subclasses must implement the abstract methods:
64 >>> import cvxpy as cp
65 >>> import numpy as np
66 >>> from cvx.risk.sample import SampleCovariance
67 >>> model = SampleCovariance(num=2)
68 >>> model.update(
69 ... cov=np.array([[1.0, 0.5], [0.5, 2.0]]),
70 ... lower_assets=np.zeros(2),
71 ... upper_assets=np.ones(2)
72 ... )
73 >>> # Access model parameters
74 >>> 'chol' in model.parameter
75 True
77 The parameter dictionary holds CVXPY Parameter objects that can be
78 updated without reconstructing the optimization problem:
80 >>> param = model.parameter['chol']
81 >>> isinstance(param, cp.Parameter)
82 True
83 >>> param.shape
84 (2, 2)
86 Multiple risk models can be composed by combining their constraints:
88 >>> from cvx.risk.bounds import Bounds
89 >>> extra_bounds = Bounds(m=2, name="extra")
90 >>> extra_bounds.update(
91 ... lower_extra=np.array([0.2, 0.2]),
92 ... upper_extra=np.array([0.8, 0.8])
93 ... )
94 >>> weights = cp.Variable(2)
95 >>> all_constraints = model.constraints(weights) + extra_bounds.constraints(weights)
96 >>> len(all_constraints)
97 4
99 """
101 parameter: dict[str, cp.Parameter] = field(default_factory=dict)
102 """Dictionary of CVXPY parameters for the risk model."""
104 @abstractmethod
105 def estimate(self, weights: cp.Variable, **kwargs: Any) -> cp.Expression:
106 """Estimate the variance given the portfolio weights.
108 This method returns a CVXPY expression representing the risk measure
109 for the given portfolio weights. The expression can be used as an
110 objective function in a convex optimization problem.
112 Args:
113 weights: CVXPY variable representing portfolio weights.
114 **kwargs: Additional keyword arguments specific to the risk model.
116 Returns:
117 CVXPY expression representing the estimated risk (e.g., standard deviation).
119 Example:
120 >>> import cvxpy as cp
121 >>> import numpy as np
122 >>> from cvx.risk.sample import SampleCovariance
123 >>> model = SampleCovariance(num=2)
124 >>> model.update(
125 ... cov=np.array([[1.0, 0.0], [0.0, 1.0]]),
126 ... lower_assets=np.zeros(2),
127 ... upper_assets=np.ones(2)
128 ... )
129 >>> weights = cp.Variable(2)
130 >>> risk = model.estimate(weights)
131 >>> isinstance(risk, cp.Expression)
132 True
134 """
136 @abstractmethod
137 def update(self, **kwargs: Any) -> None:
138 """Update the data in the risk model.
140 This method updates the CVXPY parameters in the model with new data.
141 Because CVXPY supports parametric optimization, updating parameters
142 allows solving new problem instances without reconstructing the problem.
144 Args:
145 **kwargs: Keyword arguments containing data to update the model.
146 The specific arguments depend on the risk model implementation.
148 Example:
149 >>> import numpy as np
150 >>> from cvx.risk.sample import SampleCovariance
151 >>> model = SampleCovariance(num=3)
152 >>> # Update with new covariance data
153 >>> cov = np.array([[1.0, 0.5, 0.0], [0.5, 1.0, 0.5], [0.0, 0.5, 1.0]])
154 >>> model.update(
155 ... cov=cov,
156 ... lower_assets=np.zeros(3),
157 ... upper_assets=np.ones(3)
158 ... )
160 """
162 @abstractmethod
163 def constraints(self, weights: cp.Variable, **kwargs: Any) -> list[cp.Constraint]:
164 """Return the constraints for the risk model.
166 This method returns a list of CVXPY constraints that should be included
167 in the portfolio optimization problem. Common constraints include bounds
168 on asset weights.
170 Args:
171 weights: CVXPY variable representing portfolio weights.
172 **kwargs: Additional keyword arguments specific to the risk model.
174 Returns:
175 List of CVXPY constraints for the risk model.
177 Example:
178 >>> import cvxpy as cp
179 >>> import numpy as np
180 >>> from cvx.risk.sample import SampleCovariance
181 >>> model = SampleCovariance(num=2)
182 >>> model.update(
183 ... cov=np.array([[1.0, 0.0], [0.0, 1.0]]),
184 ... lower_assets=np.zeros(2),
185 ... upper_assets=np.ones(2)
186 ... )
187 >>> weights = cp.Variable(2)
188 >>> constraints = model.constraints(weights)
189 >>> len(constraints) == 2 # Lower and upper bounds
190 True
192 """