riskfolio-lib 7.2.0__cp313-cp313-macosx_10_13_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- riskfolio/__init__.py +14 -0
- riskfolio/external/__init__.py +10 -0
- riskfolio/external/cppfunctions.py +376 -0
- riskfolio/external/functions.cpython-313-darwin.so +0 -0
- riskfolio/src/AuxFunctions.py +1488 -0
- riskfolio/src/ConstraintsFunctions.py +2210 -0
- riskfolio/src/DBHT.py +1089 -0
- riskfolio/src/GerberStatistic.py +240 -0
- riskfolio/src/HCPortfolio.py +1102 -0
- riskfolio/src/OwaWeights.py +433 -0
- riskfolio/src/ParamsEstimation.py +1989 -0
- riskfolio/src/PlotFunctions.py +5052 -0
- riskfolio/src/Portfolio.py +6164 -0
- riskfolio/src/Reports.py +692 -0
- riskfolio/src/RiskFunctions.py +3195 -0
- riskfolio/src/__init__.py +20 -0
- riskfolio/version.py +4 -0
- riskfolio_lib-7.2.0.dist-info/LICENSE.txt +27 -0
- riskfolio_lib-7.2.0.dist-info/METADATA +386 -0
- riskfolio_lib-7.2.0.dist-info/RECORD +22 -0
- riskfolio_lib-7.2.0.dist-info/WHEEL +6 -0
- riskfolio_lib-7.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""""" #
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Copyright (c) 2020-2026, Dany Cajas
|
|
5
|
+
All rights reserved.
|
|
6
|
+
This work is licensed under BSD 3-Clause "New" or "Revised" License.
|
|
7
|
+
License available at https://github.com/dcajasn/Riskfolio-Lib/blob/master/LICENSE.txt
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import cvxpy as cp
|
|
12
|
+
import math
|
|
13
|
+
from scipy.special import binom
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"owa_l_moment",
|
|
17
|
+
"owa_gmd",
|
|
18
|
+
"owa_cvar",
|
|
19
|
+
"owa_wcvar",
|
|
20
|
+
"owa_tg",
|
|
21
|
+
"owa_wr",
|
|
22
|
+
"owa_rg",
|
|
23
|
+
"owa_cvrg",
|
|
24
|
+
"owa_wcvrg",
|
|
25
|
+
"owa_tgrg",
|
|
26
|
+
"owa_l_moment_crm",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def owa_l_moment(T, k=2):
|
|
31
|
+
r"""
|
|
32
|
+
Calculate the OWA weights to calculate the kth linear moment (l-moment)
|
|
33
|
+
of a returns series as shown in :cite:`d-Cajas6`.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
T : int
|
|
38
|
+
Number of observations of the returns series.
|
|
39
|
+
k : int
|
|
40
|
+
Order of the l-moment. Must be an integer higher or equal than 1.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
value : 1d-array
|
|
45
|
+
An OWA weights vector of size Tx1.
|
|
46
|
+
"""
|
|
47
|
+
w = []
|
|
48
|
+
T_ = int(T)
|
|
49
|
+
for i in range(1, T_ + 1):
|
|
50
|
+
a = 0
|
|
51
|
+
for j in range(k):
|
|
52
|
+
a += (
|
|
53
|
+
(-1) ** j * binom(k - 1, j) * binom(i - 1, k - 1 - j) * binom(T_ - i, j)
|
|
54
|
+
)
|
|
55
|
+
a *= 1 / (k * binom(T_, k))
|
|
56
|
+
w.append(a)
|
|
57
|
+
return np.array(w).reshape(-1, 1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def owa_gmd(T):
|
|
61
|
+
r"""
|
|
62
|
+
Calculate the OWA weights to calculate the Gini mean difference (GMD)
|
|
63
|
+
of a returns series as shown in :cite:`d-Cajas3`.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
T : int
|
|
68
|
+
Number of observations of the returns series.
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
value : 1d-array
|
|
73
|
+
An OWA weights vector of size Tx1.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
w_ = []
|
|
77
|
+
T_ = int(T)
|
|
78
|
+
for i in range(1, T_ + 1):
|
|
79
|
+
w_.append(2 * i - 1 - T_)
|
|
80
|
+
w_ = 2 * np.array(w_) / (T_ * (T_ - 1))
|
|
81
|
+
w_ = w_.reshape(-1, 1)
|
|
82
|
+
|
|
83
|
+
return w_
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def owa_cvar(T, alpha=0.05):
|
|
87
|
+
r"""
|
|
88
|
+
Calculate the OWA weights to calculate the Conditional Value at Risk (CVaR)
|
|
89
|
+
of a returns series as shown in :cite:`d-Cajas3`.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
T : int
|
|
94
|
+
Number of observations of the returns series.
|
|
95
|
+
alpha : float, optional
|
|
96
|
+
Significance level of CVaR. The default is 0.05.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
value : 1d-array
|
|
101
|
+
An OWA weights vector of size Tx1.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
T_ = int(T)
|
|
105
|
+
k = int(np.ceil(T_ * alpha)) - 1
|
|
106
|
+
w_ = np.zeros((T_, 1))
|
|
107
|
+
w_[:k, :] = -1 / (T_ * alpha)
|
|
108
|
+
w_[k, :] = -1 - np.sum(w_[:k, :])
|
|
109
|
+
|
|
110
|
+
return w_
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def owa_wcvar(T, alphas, weights):
|
|
114
|
+
r"""
|
|
115
|
+
Calculate the OWA weights to calculate the Weighted Conditional Value at
|
|
116
|
+
Risk (WCVaR) of a returns series as shown in :cite:`d-Cajas3`.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
T : int
|
|
121
|
+
Number of observations of the returns series.
|
|
122
|
+
alphas : list
|
|
123
|
+
List of significance levels of each CVaR model.
|
|
124
|
+
weights : list
|
|
125
|
+
List of weights of each CVaR model.
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
value : 1d-array
|
|
130
|
+
An OWA weights vector of size Tx1.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
w_ = 0
|
|
134
|
+
T_ = int(T)
|
|
135
|
+
for i, j in zip(alphas, weights):
|
|
136
|
+
w_ += owa_cvar(T_, i) * j
|
|
137
|
+
|
|
138
|
+
return w_
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def owa_tg(T, alpha=0.05, a_sim=100):
|
|
142
|
+
r"""
|
|
143
|
+
Calculate the OWA weights to calculate the Tail Gini of a
|
|
144
|
+
returns series as shown in :cite:`d-Cajas3`.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
T : int
|
|
149
|
+
Number of observations of the returns series.
|
|
150
|
+
alpha : float, optional
|
|
151
|
+
Significance level of TaiL Gini. The default is 0.05.
|
|
152
|
+
a_sim : float, optional
|
|
153
|
+
Number of CVaRs used to approximate the Tail Gini. The default is 100.
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
value : 1d-array
|
|
158
|
+
A OWA weights vector of size Tx1.
|
|
159
|
+
"""
|
|
160
|
+
T_ = int(T)
|
|
161
|
+
a_sim_ = int(a_sim)
|
|
162
|
+
alphas = np.linspace(alpha, 0.0001, a_sim_)[::-1]
|
|
163
|
+
w_ = [(alphas[1] - 0) * alphas[0] / alphas[-1] ** 2]
|
|
164
|
+
for i in range(1, len(alphas) - 1):
|
|
165
|
+
w_.append((alphas[i + 1] - alphas[i - 1]) * alphas[i] / alphas[-1] ** 2)
|
|
166
|
+
w_.append((alphas[-1] - alphas[-2]) / alphas[-1])
|
|
167
|
+
w_ = owa_wcvar(T_, alphas, w_)
|
|
168
|
+
|
|
169
|
+
return w_
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def owa_wr(T):
|
|
173
|
+
r"""
|
|
174
|
+
Calculate the OWA weights to calculate the Worst realization (minimum)
|
|
175
|
+
of a returns series as shown in :cite:`d-Cajas3`.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
T : int
|
|
180
|
+
Number of observations of the returns series.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
value : 1d-array
|
|
185
|
+
A OWA weights vector of size Tx1.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
T_ = int(T)
|
|
189
|
+
w_ = np.zeros((T_, 1))
|
|
190
|
+
w_[0, :] = -1
|
|
191
|
+
|
|
192
|
+
return w_
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def owa_rg(T):
|
|
196
|
+
r"""
|
|
197
|
+
Calculate the OWA weights to calculate the range of a returns series
|
|
198
|
+
as shown in :cite:`d-Cajas3`.
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
T : int
|
|
203
|
+
Number of observations of the returns series.
|
|
204
|
+
|
|
205
|
+
Returns
|
|
206
|
+
-------
|
|
207
|
+
value : 1d-array
|
|
208
|
+
A OWA weights vector of size Tx1.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
T_ = int(T)
|
|
212
|
+
w_ = np.zeros((T_, 1))
|
|
213
|
+
w_[0, :] = -1
|
|
214
|
+
w_[-1, :] = 1
|
|
215
|
+
|
|
216
|
+
return w_
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def owa_cvrg(T, alpha=0.05, beta=None):
|
|
220
|
+
r"""
|
|
221
|
+
Calculate the OWA weights to calculate the CVaR range of a returns series
|
|
222
|
+
as shown in :cite:`d-Cajas3`.
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
T : int
|
|
227
|
+
Number of observations of the returns series.
|
|
228
|
+
alpha : float, optional
|
|
229
|
+
Significance level of CVaR of losses. The default is 0.05.
|
|
230
|
+
beta : float, optional
|
|
231
|
+
Significance level of CVaR of gains. If None it duplicates alpha.
|
|
232
|
+
The default is None.
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
value : 1d-array
|
|
237
|
+
A OWA weights vector of size Tx1.
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
T_ = int(T)
|
|
241
|
+
if beta is None:
|
|
242
|
+
beta = alpha
|
|
243
|
+
|
|
244
|
+
w_ = owa_cvar(T_, alpha) - owa_cvar(T_, beta)[::-1]
|
|
245
|
+
|
|
246
|
+
return w_
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def owa_wcvrg(T, alphas, weights_a, betas=None, weights_b=None):
|
|
250
|
+
r"""
|
|
251
|
+
Calculate the OWA weights to calculate the WCVaR range of a returns series
|
|
252
|
+
as shown in :cite:`d-Cajas3`.
|
|
253
|
+
|
|
254
|
+
Parameters
|
|
255
|
+
----------
|
|
256
|
+
T : int
|
|
257
|
+
Number of observations of the returns series.
|
|
258
|
+
alphas : list
|
|
259
|
+
List of significance levels of each CVaR of losses model.
|
|
260
|
+
weights_a : list
|
|
261
|
+
List of weights of each CVaR of losses model.
|
|
262
|
+
betas : list, optional
|
|
263
|
+
List of significance levels of each CVaR of gains model. If None it duplicates alpha.
|
|
264
|
+
The default is None.
|
|
265
|
+
weights_b : list, optional
|
|
266
|
+
List of weights of each CVaR of gains model. If None it duplicates weights_a.
|
|
267
|
+
The default is None.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
value : 1d-array
|
|
272
|
+
A OWA weights vector of size Tx1.
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
T_ = int(T)
|
|
276
|
+
if betas is None or weights_b is None:
|
|
277
|
+
betas = alphas
|
|
278
|
+
weights_b = weights_a
|
|
279
|
+
|
|
280
|
+
w_ = owa_wcvar(T_, alphas, weights_a) - owa_wcvar(T_, betas, weights_b)[::-1]
|
|
281
|
+
|
|
282
|
+
return w_
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def owa_tgrg(T, alpha=0.05, a_sim=100, beta=None, b_sim=None):
|
|
286
|
+
r"""
|
|
287
|
+
Calculate the OWA weights to calculate the Tail Gini range of a returns
|
|
288
|
+
series as shown in :cite:`d-Cajas3`.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
T : int
|
|
293
|
+
Number of observations of the returns series.
|
|
294
|
+
alpha : float, optional
|
|
295
|
+
Significance level of Tail Gini of losses. The default is 0.05.
|
|
296
|
+
a_sim : float, optional
|
|
297
|
+
Number of CVaRs used to approximate Tail Gini of losses. The default is 100.
|
|
298
|
+
beta : float, optional
|
|
299
|
+
Significance level of Tail Gini of gains. If None it duplicates alpha value.
|
|
300
|
+
The default is None.
|
|
301
|
+
b_sim : float, optional
|
|
302
|
+
Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value.
|
|
303
|
+
The default is None.
|
|
304
|
+
|
|
305
|
+
Returns
|
|
306
|
+
-------
|
|
307
|
+
value : 1d-array
|
|
308
|
+
A OWA weights vector of size Tx1.
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
if beta is None:
|
|
312
|
+
beta = alpha
|
|
313
|
+
if b_sim is None:
|
|
314
|
+
b_sim = a_sim
|
|
315
|
+
|
|
316
|
+
T_ = int(T)
|
|
317
|
+
a_sim_ = int(a_sim)
|
|
318
|
+
b_sim_ = int(b_sim)
|
|
319
|
+
|
|
320
|
+
w_ = owa_tg(T_, alpha, a_sim_) - owa_tg(T_, beta, b_sim_)[::-1]
|
|
321
|
+
|
|
322
|
+
return w_
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def owa_l_moment_crm(T, k=4, method="MSD", g=0.5, max_phi=0.5, solver="CLARABEL"):
|
|
326
|
+
r"""
|
|
327
|
+
Calculate the OWA weights to calculate a convex risk measure that considers
|
|
328
|
+
higher linear moments or L-moments as shown in :cite:`d-Cajas6`.
|
|
329
|
+
|
|
330
|
+
Parameters
|
|
331
|
+
----------
|
|
332
|
+
T : int
|
|
333
|
+
Number of observations of the returns series.
|
|
334
|
+
k : int
|
|
335
|
+
Order of the l-moment. Must be an integer higher or equal than 2.
|
|
336
|
+
method : str, optional
|
|
337
|
+
Method to calculate the weights used to combine the l-moments with order higher than 2.
|
|
338
|
+
The default value is 'MSD'. Possible values are:
|
|
339
|
+
|
|
340
|
+
- 'CRRA': Normalized Constant Relative Risk Aversion coefficients.
|
|
341
|
+
- 'ME': Maximum Entropy.
|
|
342
|
+
- 'MSS': Minimum Sum Squares.
|
|
343
|
+
- 'MSD': Minimum Square Distance.
|
|
344
|
+
|
|
345
|
+
g : float, optional
|
|
346
|
+
Risk aversion coefficient of CRRA utility function. The default is 0.5.
|
|
347
|
+
max_phi : float, optional
|
|
348
|
+
Maximum weight constraint of L-moments.
|
|
349
|
+
The default is 0.5.
|
|
350
|
+
solver: str, optional
|
|
351
|
+
Solver available for CVXPY. Used to calculate 'ME', 'MSS' and 'MSD' weights.
|
|
352
|
+
The default value is 'CLARABEL'.
|
|
353
|
+
|
|
354
|
+
Returns
|
|
355
|
+
-------
|
|
356
|
+
value : 1d-array
|
|
357
|
+
A OWA weights vector of size Tx1.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
if k < 2 or (not isinstance(k, int)):
|
|
361
|
+
raise ValueError("k must be an integer higher equal than 2")
|
|
362
|
+
if method not in ["CRRA", "ME", "MSS", "MSD"]:
|
|
363
|
+
raise ValueError("Available methods are 'CRRA', 'ME', 'MSS' and 'MSD'")
|
|
364
|
+
if g >= 1 or g <= 0:
|
|
365
|
+
raise ValueError("The risk aversion coefficient mus be between 0 and 1")
|
|
366
|
+
if max_phi >= 1 or max_phi <= 0:
|
|
367
|
+
raise ValueError(
|
|
368
|
+
"The constraint on maximum weight of L-moments must be between 0 and 1"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
T_ = int(T)
|
|
372
|
+
ws = np.empty((T_, 0))
|
|
373
|
+
for i in range(2, k + 1):
|
|
374
|
+
w_i = (-1) ** i * owa_l_moment(T_, i)
|
|
375
|
+
ws = np.concatenate([ws, w_i], axis=1)
|
|
376
|
+
|
|
377
|
+
if method == "CRRA":
|
|
378
|
+
phis = []
|
|
379
|
+
e = 1
|
|
380
|
+
for i in range(1, k):
|
|
381
|
+
e *= g + i - 1
|
|
382
|
+
phis.append(e / math.factorial(i + 1))
|
|
383
|
+
phis = np.array(phis)
|
|
384
|
+
phis = phis / np.sum(phis)
|
|
385
|
+
phis = phis.reshape(-1, 1)
|
|
386
|
+
a = ws @ phis
|
|
387
|
+
|
|
388
|
+
w = np.zeros_like(a)
|
|
389
|
+
w[0] = a[0]
|
|
390
|
+
for i in range(1, len(a)):
|
|
391
|
+
w[i, 0] = np.max(a[: i + 1, 0])
|
|
392
|
+
|
|
393
|
+
else:
|
|
394
|
+
theta = cp.Variable((T_, 1))
|
|
395
|
+
n = ws.shape[1]
|
|
396
|
+
phi = cp.Variable((n, 1))
|
|
397
|
+
|
|
398
|
+
constraints = [
|
|
399
|
+
cp.sum(phi) == 1,
|
|
400
|
+
theta == ws @ phi,
|
|
401
|
+
phi <= max_phi,
|
|
402
|
+
phi >= 0,
|
|
403
|
+
phi[1:] <= phi[:-1],
|
|
404
|
+
theta[1:] >= theta[:-1],
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
if method == "ME":
|
|
408
|
+
theta_ = cp.Variable((T_, 1))
|
|
409
|
+
obj = cp.sum(cp.entr(theta_)) * 1000
|
|
410
|
+
constraints += [
|
|
411
|
+
theta_ >= theta,
|
|
412
|
+
theta_ >= -theta,
|
|
413
|
+
]
|
|
414
|
+
objective = cp.Maximize(obj)
|
|
415
|
+
elif method == "MSS":
|
|
416
|
+
obj = cp.pnorm(theta, p=2) * 1000
|
|
417
|
+
objective = cp.Minimize(obj)
|
|
418
|
+
elif method == "MSD":
|
|
419
|
+
obj = cp.pnorm(theta[1:] - theta[:-1], p=2) * 1000
|
|
420
|
+
objective = cp.Minimize(obj)
|
|
421
|
+
|
|
422
|
+
problem = cp.Problem(objective, constraints)
|
|
423
|
+
if solver is not None:
|
|
424
|
+
problem.solve(solver=solver)
|
|
425
|
+
else:
|
|
426
|
+
problem.solve()
|
|
427
|
+
|
|
428
|
+
phis = phi.value
|
|
429
|
+
phis = phis / np.sum(phis)
|
|
430
|
+
phis = phis.reshape(-1, 1)
|
|
431
|
+
w = ws @ phis
|
|
432
|
+
|
|
433
|
+
return w
|