Coverage for src / cvx / risk / model.py: 100%

14 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-15 12:21 +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. 

15 

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. 

19 

20Example: 

21 All risk models inherit from the Model class and must implement 

22 the estimate, update, and constraints methods: 

23 

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 

37 

38""" 

39 

40from __future__ import annotations 

41 

42from abc import ABC, abstractmethod 

43from dataclasses import dataclass, field 

44 

45import cvxpy as cp 

46 

47 

48@dataclass 

49class Model(ABC): 

50 """Abstract base class for risk models. 

51 

52 This class defines the interface that all risk models must implement. 

53 Risk models are used in portfolio optimization to estimate portfolio risk 

54 and provide constraints for the optimization problem. 

55 

56 Attributes: 

57 parameter: Dictionary mapping parameter names to CVXPY Parameter objects. 

58 These parameters can be updated without reconstructing the optimization problem. 

59 

60 Example: 

61 Subclasses must implement the abstract methods: 

62 

63 >>> import cvxpy as cp 

64 >>> import numpy as np 

65 >>> from cvx.risk.sample import SampleCovariance 

66 >>> model = SampleCovariance(num=2) 

67 >>> model.update( 

68 ... cov=np.array([[1.0, 0.5], [0.5, 2.0]]), 

69 ... lower_assets=np.zeros(2), 

70 ... upper_assets=np.ones(2) 

71 ... ) 

72 >>> # Access model parameters 

73 >>> 'chol' in model.parameter 

74 True 

75 

76 """ 

77 

78 parameter: dict[str, cp.Parameter] = field(default_factory=dict) 

79 """Dictionary of CVXPY parameters for the risk model.""" 

80 

81 @abstractmethod 

82 def estimate(self, weights: cp.Variable, **kwargs) -> cp.Expression: 

83 """Estimate the variance given the portfolio weights. 

84 

85 This method returns a CVXPY expression representing the risk measure 

86 for the given portfolio weights. The expression can be used as an 

87 objective function in a convex optimization problem. 

88 

89 Args: 

90 weights: CVXPY variable representing portfolio weights. 

91 **kwargs: Additional keyword arguments specific to the risk model. 

92 

93 Returns: 

94 CVXPY expression representing the estimated risk (e.g., standard deviation). 

95 

96 Example: 

97 >>> import cvxpy as cp 

98 >>> import numpy as np 

99 >>> from cvx.risk.sample import SampleCovariance 

100 >>> model = SampleCovariance(num=2) 

101 >>> model.update( 

102 ... cov=np.array([[1.0, 0.0], [0.0, 1.0]]), 

103 ... lower_assets=np.zeros(2), 

104 ... upper_assets=np.ones(2) 

105 ... ) 

106 >>> weights = cp.Variable(2) 

107 >>> risk = model.estimate(weights) 

108 >>> isinstance(risk, cp.Expression) 

109 True 

110 

111 """ 

112 

113 @abstractmethod 

114 def update(self, **kwargs) -> None: 

115 """Update the data in the risk model. 

116 

117 This method updates the CVXPY parameters in the model with new data. 

118 Because CVXPY supports parametric optimization, updating parameters 

119 allows solving new problem instances without reconstructing the problem. 

120 

121 Args: 

122 **kwargs: Keyword arguments containing data to update the model. 

123 The specific arguments depend on the risk model implementation. 

124 

125 Example: 

126 >>> import numpy as np 

127 >>> from cvx.risk.sample import SampleCovariance 

128 >>> model = SampleCovariance(num=3) 

129 >>> # Update with new covariance data 

130 >>> cov = np.array([[1.0, 0.5, 0.0], [0.5, 1.0, 0.5], [0.0, 0.5, 1.0]]) 

131 >>> model.update( 

132 ... cov=cov, 

133 ... lower_assets=np.zeros(3), 

134 ... upper_assets=np.ones(3) 

135 ... ) 

136 

137 """ 

138 

139 @abstractmethod 

140 def constraints(self, weights: cp.Variable, **kwargs) -> list[cp.Constraint]: 

141 """Return the constraints for the risk model. 

142 

143 This method returns a list of CVXPY constraints that should be included 

144 in the portfolio optimization problem. Common constraints include bounds 

145 on asset weights. 

146 

147 Args: 

148 weights: CVXPY variable representing portfolio weights. 

149 **kwargs: Additional keyword arguments specific to the risk model. 

150 

151 Returns: 

152 List of CVXPY constraints for the risk model. 

153 

154 Example: 

155 >>> import cvxpy as cp 

156 >>> import numpy as np 

157 >>> from cvx.risk.sample import SampleCovariance 

158 >>> model = SampleCovariance(num=2) 

159 >>> model.update( 

160 ... cov=np.array([[1.0, 0.0], [0.0, 1.0]]), 

161 ... lower_assets=np.zeros(2), 

162 ... upper_assets=np.ones(2) 

163 ... ) 

164 >>> weights = cp.Variable(2) 

165 >>> constraints = model.constraints(weights) 

166 >>> len(constraints) == 2 # Lower and upper bounds 

167 True 

168 

169 """