skfolio 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.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.
- skfolio/cluster/_hierarchical.py +1 -0
- skfolio/datasets/_base.py +1 -1
- skfolio/measures/__init__.py +1 -1
- skfolio/model_selection/_combinatorial.py +12 -14
- skfolio/model_selection/_validation.py +6 -8
- skfolio/moments/covariance/_covariance.py +0 -1
- skfolio/moments/expected_returns/_expected_returns.py +1 -0
- skfolio/optimization/_base.py +9 -11
- skfolio/optimization/cluster/hierarchical/_base.py +0 -2
- skfolio/optimization/convex/__init__.py +1 -1
- skfolio/optimization/convex/_base.py +43 -26
- skfolio/optimization/convex/_distributionally_robust.py +22 -10
- skfolio/optimization/convex/_maximum_diversification.py +17 -7
- skfolio/optimization/convex/_mean_risk.py +25 -13
- skfolio/optimization/convex/_risk_budgeting.py +22 -10
- skfolio/optimization/ensemble/_stacking.py +4 -6
- skfolio/population/_population.py +18 -25
- skfolio/portfolio/_portfolio.py +11 -13
- skfolio/pre_selection/_pre_selection.py +6 -6
- skfolio/preprocessing/_returns.py +1 -1
- skfolio/prior/__init__.py +1 -1
- skfolio/prior/_base.py +1 -0
- skfolio/prior/_empirical.py +1 -1
- skfolio/prior/_factor_model.py +4 -6
- skfolio/uncertainty_set/_base.py +1 -0
- skfolio/uncertainty_set/_bootstrap.py +1 -0
- skfolio/uncertainty_set/_empirical.py +2 -0
- skfolio/utils/stats.py +1 -1
- {skfolio-0.0.1.dist-info → skfolio-0.0.3.dist-info}/METADATA +580 -568
- {skfolio-0.0.1.dist-info → skfolio-0.0.3.dist-info}/RECORD +33 -33
- {skfolio-0.0.1.dist-info → skfolio-0.0.3.dist-info}/LICENSE +0 -0
- {skfolio-0.0.1.dist-info → skfolio-0.0.3.dist-info}/WHEEL +0 -0
- {skfolio-0.0.1.dist-info → skfolio-0.0.3.dist-info}/top_level.txt +0 -0
@@ -302,14 +302,17 @@ class RiskBudgeting(ConvexOptimization):
|
|
302
302
|
It is a function that must take as argument the weights `w` and returns a
|
303
303
|
CVPXY expression.
|
304
304
|
|
305
|
-
solver : str,
|
306
|
-
The solver to use.
|
307
|
-
|
305
|
+
solver : str, default="CLARABEL"
|
306
|
+
The solver to use. The default is "CLARABEL" which is written in Rust and has
|
307
|
+
better numerical stability and performance than ECOS and SCS. Cvxpy will replace
|
308
|
+
its default solver "ECOS" by "CLARABEL" in future releases.
|
308
309
|
For more details about available solvers, check the CVXPY documentation:
|
309
310
|
https://www.cvxpy.org/tutorial/advanced/index.html#choosing-a-solver
|
310
311
|
|
311
312
|
solver_params : dict, optional
|
312
313
|
Solver parameters. For example, `solver_params=dict(verbose=True)`.
|
314
|
+
The default (`None`) is use `{"tol_gap_abs": 1e-9, "tol_gap_rel": 1e-9}`
|
315
|
+
for the solver "CLARABEL" and the CVXPY default otherwise.
|
313
316
|
For more details about solver arguments, check the CVXPY documentation:
|
314
317
|
https://www.cvxpy.org/tutorial/advanced/index.html#setting-solver-options
|
315
318
|
|
@@ -323,6 +326,10 @@ class RiskBudgeting(ConvexOptimization):
|
|
323
326
|
It can be used to increase the optimization accuracies in specific cases.
|
324
327
|
The default (`None`) is set depending on the problem.
|
325
328
|
|
329
|
+
save_problem : bool, default=False
|
330
|
+
If this is set to True, the CVXPY Problem is saved in `problem_`.
|
331
|
+
The default is `False`.
|
332
|
+
|
326
333
|
raise_on_failure : bool, default=True
|
327
334
|
If this is set to True, an error is raised when the optimization fail otherwise
|
328
335
|
it passes with a warning.
|
@@ -338,15 +345,16 @@ class RiskBudgeting(ConvexOptimization):
|
|
338
345
|
weights_ : ndarray of shape (n_assets,) or (n_optimizations, n_assets)
|
339
346
|
Weights of the assets.
|
340
347
|
|
341
|
-
problem_: cvxpy.Problem
|
342
|
-
CVXPY problem used for the optimization.
|
343
|
-
|
344
348
|
problem_values_ : dict[str, float] | list[dict[str, float]] of size n_optimizations
|
345
349
|
Expression values retrieved from the CVXPY problem.
|
346
350
|
|
347
351
|
prior_estimator_ : BasePrior
|
348
352
|
Fitted `prior_estimator`.
|
349
353
|
|
354
|
+
problem_: cvxpy.Problem
|
355
|
+
CVXPY problem used for the optimization. Only when `save_problem` is set to
|
356
|
+
`True`.
|
357
|
+
|
350
358
|
n_features_in_ : int
|
351
359
|
Number of assets seen during `fit`.
|
352
360
|
|
@@ -376,10 +384,11 @@ class RiskBudgeting(ConvexOptimization):
|
|
376
384
|
evar_beta: float = 0.95,
|
377
385
|
cdar_beta: float = 0.95,
|
378
386
|
edar_beta: float = 0.95,
|
379
|
-
solver: str
|
387
|
+
solver: str = "CLARABEL",
|
380
388
|
solver_params: dict | None = None,
|
381
389
|
scale_objective: float | None = None,
|
382
390
|
scale_constraints: float | None = None,
|
391
|
+
save_problem: bool = False,
|
383
392
|
raise_on_failure: bool = True,
|
384
393
|
add_objective: skt.ExpressionFunction | None = None,
|
385
394
|
add_constraints: skt.ExpressionFunction | None = None,
|
@@ -409,6 +418,7 @@ class RiskBudgeting(ConvexOptimization):
|
|
409
418
|
solver_params=solver_params,
|
410
419
|
scale_objective=scale_objective,
|
411
420
|
scale_constraints=scale_constraints,
|
421
|
+
save_problem=save_problem,
|
412
422
|
raise_on_failure=raise_on_failure,
|
413
423
|
add_objective=add_objective,
|
414
424
|
add_constraints=add_constraints,
|
@@ -450,7 +460,6 @@ class RiskBudgeting(ConvexOptimization):
|
|
450
460
|
self._validation()
|
451
461
|
# Used to avoid adding multiple times similar constrains linked to identical
|
452
462
|
# risk models
|
453
|
-
self._clear_models_cache()
|
454
463
|
self.prior_estimator_ = check_estimator(
|
455
464
|
self.prior_estimator,
|
456
465
|
default=EmpiricalPrior(),
|
@@ -460,8 +469,11 @@ class RiskBudgeting(ConvexOptimization):
|
|
460
469
|
prior_model = self.prior_estimator_.prior_model_
|
461
470
|
n_observations, n_assets = prior_model.returns.shape
|
462
471
|
|
463
|
-
# set solvers
|
464
|
-
self.
|
472
|
+
# set solvers params
|
473
|
+
if self.solver == "CLARABEL":
|
474
|
+
self._set_solver_params(default={"tol_gap_abs": 1e-9, "tol_gap_rel": 1e-9})
|
475
|
+
else:
|
476
|
+
self._set_solver_params(default=None)
|
465
477
|
|
466
478
|
# set scale
|
467
479
|
self._set_scale_objective(default=1)
|
@@ -306,12 +306,10 @@ class StackingOptimization(BaseOptimization, BaseComposition):
|
|
306
306
|
_ = self._validate_data(X)
|
307
307
|
|
308
308
|
if isinstance(self.cv, BaseCombinatorialCV):
|
309
|
-
X_pred = np.array(
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
]
|
314
|
-
).T
|
309
|
+
X_pred = np.array([
|
310
|
+
pred.quantile(measure=self.quantile_measure, q=self.quantile)
|
311
|
+
for pred in cv_predictions
|
312
|
+
]).T
|
315
313
|
else:
|
316
314
|
X_pred = np.array(cv_predictions).T
|
317
315
|
if y is not None:
|
@@ -139,12 +139,9 @@ class Population(list):
|
|
139
139
|
non-dominated portfolios.
|
140
140
|
"""
|
141
141
|
n = len(self)
|
142
|
-
if n > 0 and np.any(
|
143
|
-
[
|
144
|
-
|
145
|
-
for portfolio in self
|
146
|
-
]
|
147
|
-
):
|
142
|
+
if n > 0 and np.any([
|
143
|
+
portfolio.fitness_measures != self[0].fitness_measures for portfolio in self
|
144
|
+
]):
|
148
145
|
raise ValueError(
|
149
146
|
"Cannot compute non denominated sorting with Portfolios "
|
150
147
|
"containing mixed `fitness_measures`"
|
@@ -189,13 +186,11 @@ class Population(list):
|
|
189
186
|
return self.__class__(
|
190
187
|
[portfolio for portfolio in self if portfolio.tag in tags]
|
191
188
|
)
|
192
|
-
return self.__class__(
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
]
|
198
|
-
)
|
189
|
+
return self.__class__([
|
190
|
+
portfolio
|
191
|
+
for portfolio in self
|
192
|
+
if portfolio.name in names and portfolio.tag in tags
|
193
|
+
])
|
199
194
|
|
200
195
|
def measures(
|
201
196
|
self,
|
@@ -759,7 +754,7 @@ class Population(list):
|
|
759
754
|
fronts = self.non_denominated_sort(first_front_only=False)
|
760
755
|
if tags is not None:
|
761
756
|
ValueError("Cannot plot front with tags selected")
|
762
|
-
df["front"] = -1
|
757
|
+
df["front"] = str(-1)
|
763
758
|
for i, front in enumerate(fronts):
|
764
759
|
for idx in front:
|
765
760
|
df.iloc[idx, -1] = str(i)
|
@@ -788,17 +783,15 @@ class Population(list):
|
|
788
783
|
x=xi,
|
789
784
|
y=yi,
|
790
785
|
z=Z,
|
791
|
-
hovertemplate="<br>".join(
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
]
|
801
|
-
)
|
786
|
+
hovertemplate="<br>".join([
|
787
|
+
str(e)
|
788
|
+
+ ": %{"
|
789
|
+
+ v
|
790
|
+
+ ":"
|
791
|
+
+ (",.3%" if not e.is_ratio else None)
|
792
|
+
+ "}"
|
793
|
+
for e, v in [(x, "x"), (y, "y"), (z, "z")]
|
794
|
+
])
|
802
795
|
+ "<extra></extra>",
|
803
796
|
colorbar=dict(
|
804
797
|
title=str(z),
|
skfolio/portfolio/_portfolio.py
CHANGED
@@ -401,19 +401,17 @@ class Portfolio(BasePortfolio):
|
|
401
401
|
"""
|
402
402
|
|
403
403
|
_read_only_attrs: ClassVar[set] = BasePortfolio._read_only_attrs.copy()
|
404
|
-
_read_only_attrs.update(
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
}
|
416
|
-
)
|
404
|
+
_read_only_attrs.update({
|
405
|
+
"X",
|
406
|
+
"assets",
|
407
|
+
"weights",
|
408
|
+
"previous_weights",
|
409
|
+
"transaction_costs",
|
410
|
+
"management_fees",
|
411
|
+
"n_assets",
|
412
|
+
"total_cost",
|
413
|
+
"total_fee",
|
414
|
+
})
|
417
415
|
|
418
416
|
__slots__ = {
|
419
417
|
# read-only
|
@@ -296,13 +296,13 @@ class SelectNonDominated(skf.SelectorMixin, skb.BaseEstimator):
|
|
296
296
|
)
|
297
297
|
|
298
298
|
# Add pairs with correlation below threshold with minimum variance
|
299
|
-
# ptf_variance =
|
300
|
-
# with
|
301
|
-
# To find the minimum we substitute
|
302
|
-
# respect to
|
299
|
+
# ptf_variance = sigma1^2 w1^2 + sigma2^2 w2^2 + 2 sigma12 w1 w2 (1)
|
300
|
+
# with w1 + w2 = 1
|
301
|
+
# To find the minimum we substitute w2 = 1 - w1 in (1) and differentiate with
|
302
|
+
# respect to w1 and set to zero.
|
303
303
|
# By solving the obtained equation, we get:
|
304
|
-
#
|
305
|
-
#
|
304
|
+
# w1 = (sigma2^2 - sigma12) / (sigma1^2 + sigma2^2 - 2 sigma12)
|
305
|
+
# w2 = 1 - w1
|
306
306
|
|
307
307
|
corr = np.corrcoef(X.T)
|
308
308
|
covariance = np.cov(X.T)
|
@@ -69,7 +69,7 @@ def prices_to_returns(
|
|
69
69
|
|
70
70
|
References
|
71
71
|
----------
|
72
|
-
.. [1] "Linear vs. Compounded Returns
|
72
|
+
.. [1] "Linear vs. Compounded Returns - Common Pitfalls in Portfolio Management".
|
73
73
|
GARP Risk Professional.
|
74
74
|
Attilio Meucci (2010).
|
75
75
|
"""
|
skfolio/prior/__init__.py
CHANGED
@@ -2,8 +2,8 @@ from skfolio.prior._base import BasePrior, PriorModel
|
|
2
2
|
from skfolio.prior._black_litterman import BlackLitterman
|
3
3
|
from skfolio.prior._empirical import EmpiricalPrior
|
4
4
|
from skfolio.prior._factor_model import (
|
5
|
-
FactorModel,
|
6
5
|
BaseLoadingMatrix,
|
6
|
+
FactorModel,
|
7
7
|
LoadingMatrixRegression,
|
8
8
|
)
|
9
9
|
|
skfolio/prior/_base.py
CHANGED
skfolio/prior/_empirical.py
CHANGED
@@ -64,7 +64,7 @@ class EmpiricalPrior(BasePrior):
|
|
64
64
|
|
65
65
|
References
|
66
66
|
----------
|
67
|
-
.. [1] "Linear vs. Compounded Returns
|
67
|
+
.. [1] "Linear vs. Compounded Returns - Common Pitfalls in Portfolio Management".
|
68
68
|
GARP Risk Professional.
|
69
69
|
Attilio Meucci (2010).
|
70
70
|
"""
|
skfolio/prior/_factor_model.py
CHANGED
@@ -109,12 +109,10 @@ class LoadingMatrixRegression(BaseLoadingMatrix):
|
|
109
109
|
self.loading_matrix_ = np.array(
|
110
110
|
[self.multi_output_regressor_.estimators_[i].coef_ for i in range(n_assets)]
|
111
111
|
)
|
112
|
-
self.intercepts_ = np.array(
|
113
|
-
[
|
114
|
-
|
115
|
-
|
116
|
-
]
|
117
|
-
)
|
112
|
+
self.intercepts_ = np.array([
|
113
|
+
self.multi_output_regressor_.estimators_[i].intercept_
|
114
|
+
for i in range(n_assets)
|
115
|
+
])
|
118
116
|
|
119
117
|
|
120
118
|
class FactorModel(BasePrior):
|
skfolio/uncertainty_set/_base.py
CHANGED
@@ -71,6 +71,7 @@ class EmpiricalMuUncertaintySet(BaseMuUncertaintySet):
|
|
71
71
|
Optimization: A Journal of Mathematical Programming and Operations Research,
|
72
72
|
Schöttle & Werner (2009).
|
73
73
|
"""
|
74
|
+
|
74
75
|
prior_estimator_: BasePrior
|
75
76
|
|
76
77
|
def __init__(
|
@@ -178,6 +179,7 @@ class EmpiricalCovarianceUncertaintySet(BaseCovarianceUncertaintySet):
|
|
178
179
|
Optimization: A Journal of Mathematical Programming and Operations Research,
|
179
180
|
Schöttle & Werner (2009).
|
180
181
|
"""
|
182
|
+
|
181
183
|
prior_estimator_: BasePrior
|
182
184
|
|
183
185
|
def __init__(
|