Coverage for cvx/risk/cvar/cvar.py: 100%

27 statements  

« prev     ^ index     » next       coverage.py v7.6.8, created at 2025-01-09 10:59 +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. 

14from __future__ import annotations 

15 

16from dataclasses import dataclass 

17 

18import cvxpy as cvx 

19import numpy as np 

20 

21from ..bounds import Bounds 

22from ..model import Model 

23 

24 

25@dataclass 

26class CVar(Model): 

27 """Conditional value at risk model""" 

28 

29 alpha: float = 0.95 

30 """alpha parameter to determine the size of the tail""" 

31 

32 n: int = 0 

33 """number of samples""" 

34 

35 m: int = 0 

36 """number of assets""" 

37 

38 def __post_init__(self): 

39 self.k = int(self.n * (1 - self.alpha)) 

40 self.parameter["R"] = cvx.Parameter(shape=(self.n, self.m), name="returns", value=np.zeros((self.n, self.m))) 

41 self.bounds = Bounds(m=self.m, name="assets") 

42 

43 def estimate(self, weights, **kwargs): 

44 """Estimate the risk by computing the Cholesky decomposition of self.cov""" 

45 # R is a matrix of returns, n is the number of rows in R 

46 # n = self.R.shape[0] 

47 # k is the number of returns in the left tail 

48 # k = int(n * (1 - self.alpha)) 

49 # average value of the k elements in the left tail 

50 return -cvx.sum_smallest(self.parameter["R"] @ weights, k=self.k) / self.k 

51 

52 def update(self, **kwargs): 

53 ret = kwargs["returns"] 

54 m = ret.shape[1] 

55 

56 self.parameter["R"].value[:, :m] = kwargs["returns"] 

57 self.bounds.update(**kwargs) 

58 

59 def constraints(self, weights, **kwargs): 

60 return self.bounds.constraints(weights)