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

1"""Minimum risk portfolio optimization. 

2 

3This module provides functions for creating and solving minimum risk portfolio 

4optimization problems using various risk models. 

5 

6Example: 

7 Create and solve a minimum risk portfolio problem: 

8 

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 

28 

29""" 

30 

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 

45 

46import cvxpy as cp 

47 

48from cvx.risk import Model 

49 

50 

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. 

59 

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. 

64 

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. 

78 

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 

87 

88 Example: 

89 Basic minimum risk portfolio: 

90 

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 

107 

108 With tracking error (minimize deviation from benchmark): 

109 

110 >>> benchmark = np.array([0.5, 0.5]) 

111 >>> problem = minrisk_problem(model, weights, base=benchmark) 

112 >>> _ = problem.solve(solver="CLARABEL") 

113 

114 With custom constraints: 

115 

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 

121 

122 """ 

123 # if no constraints are specified 

124 constraints = constraints or [] 

125 

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 ) 

130 

131 return problem