skfolio 0.6.0__py3-none-any.whl → 0.8.0__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/__init__.py +7 -7
- skfolio/cluster/__init__.py +2 -2
- skfolio/cluster/_hierarchical.py +2 -2
- skfolio/datasets/__init__.py +3 -3
- skfolio/datasets/_base.py +2 -2
- skfolio/datasets/data/__init__.py +1 -0
- skfolio/distance/__init__.py +4 -4
- skfolio/distance/_base.py +2 -2
- skfolio/distance/_distance.py +11 -10
- skfolio/distribution/__init__.py +56 -0
- skfolio/distribution/_base.py +203 -0
- skfolio/distribution/copula/__init__.py +35 -0
- skfolio/distribution/copula/_base.py +456 -0
- skfolio/distribution/copula/_clayton.py +539 -0
- skfolio/distribution/copula/_gaussian.py +407 -0
- skfolio/distribution/copula/_gumbel.py +560 -0
- skfolio/distribution/copula/_independent.py +196 -0
- skfolio/distribution/copula/_joe.py +609 -0
- skfolio/distribution/copula/_selection.py +111 -0
- skfolio/distribution/copula/_student_t.py +486 -0
- skfolio/distribution/copula/_utils.py +509 -0
- skfolio/distribution/multivariate/__init__.py +11 -0
- skfolio/distribution/multivariate/_base.py +241 -0
- skfolio/distribution/multivariate/_utils.py +632 -0
- skfolio/distribution/multivariate/_vine_copula.py +1254 -0
- skfolio/distribution/univariate/__init__.py +19 -0
- skfolio/distribution/univariate/_base.py +308 -0
- skfolio/distribution/univariate/_gaussian.py +136 -0
- skfolio/distribution/univariate/_johnson_su.py +152 -0
- skfolio/distribution/univariate/_normal_inverse_gaussian.py +153 -0
- skfolio/distribution/univariate/_selection.py +85 -0
- skfolio/distribution/univariate/_student_t.py +144 -0
- skfolio/exceptions.py +8 -8
- skfolio/measures/__init__.py +24 -24
- skfolio/measures/_enums.py +7 -7
- skfolio/measures/_measures.py +4 -7
- skfolio/metrics/__init__.py +2 -0
- skfolio/metrics/_scorer.py +4 -4
- skfolio/model_selection/__init__.py +4 -4
- skfolio/model_selection/_combinatorial.py +15 -12
- skfolio/model_selection/_validation.py +2 -2
- skfolio/model_selection/_walk_forward.py +3 -3
- skfolio/moments/__init__.py +11 -11
- skfolio/moments/covariance/__init__.py +6 -6
- skfolio/moments/covariance/_base.py +1 -1
- skfolio/moments/covariance/_denoise_covariance.py +3 -2
- skfolio/moments/covariance/_detone_covariance.py +3 -2
- skfolio/moments/covariance/_empirical_covariance.py +3 -2
- skfolio/moments/covariance/_ew_covariance.py +3 -2
- skfolio/moments/covariance/_gerber_covariance.py +3 -2
- skfolio/moments/covariance/_graphical_lasso_cv.py +1 -1
- skfolio/moments/covariance/_implied_covariance.py +3 -8
- skfolio/moments/covariance/_ledoit_wolf.py +1 -1
- skfolio/moments/covariance/_oas.py +1 -1
- skfolio/moments/covariance/_shrunk_covariance.py +1 -1
- skfolio/moments/expected_returns/__init__.py +2 -2
- skfolio/moments/expected_returns/_base.py +1 -1
- skfolio/moments/expected_returns/_empirical_mu.py +3 -2
- skfolio/moments/expected_returns/_equilibrium_mu.py +3 -2
- skfolio/moments/expected_returns/_ew_mu.py +3 -2
- skfolio/moments/expected_returns/_shrunk_mu.py +4 -3
- skfolio/optimization/__init__.py +12 -10
- skfolio/optimization/_base.py +2 -2
- skfolio/optimization/cluster/__init__.py +3 -1
- skfolio/optimization/cluster/_nco.py +10 -9
- skfolio/optimization/cluster/hierarchical/__init__.py +3 -1
- skfolio/optimization/cluster/hierarchical/_base.py +1 -2
- skfolio/optimization/cluster/hierarchical/_herc.py +4 -3
- skfolio/optimization/cluster/hierarchical/_hrp.py +4 -3
- skfolio/optimization/convex/__init__.py +5 -3
- skfolio/optimization/convex/_base.py +10 -9
- skfolio/optimization/convex/_distributionally_robust.py +8 -5
- skfolio/optimization/convex/_maximum_diversification.py +8 -6
- skfolio/optimization/convex/_mean_risk.py +10 -8
- skfolio/optimization/convex/_risk_budgeting.py +6 -4
- skfolio/optimization/ensemble/__init__.py +2 -0
- skfolio/optimization/ensemble/_base.py +2 -2
- skfolio/optimization/ensemble/_stacking.py +3 -3
- skfolio/optimization/naive/__init__.py +3 -1
- skfolio/optimization/naive/_naive.py +4 -3
- skfolio/population/__init__.py +2 -0
- skfolio/population/_population.py +34 -7
- skfolio/portfolio/__init__.py +1 -1
- skfolio/portfolio/_base.py +43 -8
- skfolio/portfolio/_multi_period_portfolio.py +3 -2
- skfolio/portfolio/_portfolio.py +5 -4
- skfolio/pre_selection/__init__.py +3 -1
- skfolio/pre_selection/_drop_correlated.py +3 -3
- skfolio/pre_selection/_select_complete.py +31 -30
- skfolio/pre_selection/_select_k_extremes.py +3 -3
- skfolio/pre_selection/_select_non_dominated.py +3 -3
- skfolio/pre_selection/_select_non_expiring.py +8 -6
- skfolio/preprocessing/__init__.py +2 -0
- skfolio/preprocessing/_returns.py +2 -2
- skfolio/prior/__init__.py +7 -3
- skfolio/prior/_base.py +2 -2
- skfolio/prior/_black_litterman.py +7 -4
- skfolio/prior/_empirical.py +5 -2
- skfolio/prior/_factor_model.py +10 -5
- skfolio/prior/_synthetic_data.py +239 -0
- skfolio/synthetic_returns/__init__.py +1 -0
- skfolio/typing.py +7 -7
- skfolio/uncertainty_set/__init__.py +7 -5
- skfolio/uncertainty_set/_base.py +5 -4
- skfolio/uncertainty_set/_bootstrap.py +1 -1
- skfolio/uncertainty_set/_empirical.py +1 -1
- skfolio/utils/__init__.py +1 -0
- skfolio/utils/bootstrap.py +2 -2
- skfolio/utils/equations.py +13 -10
- skfolio/utils/sorting.py +2 -2
- skfolio/utils/stats.py +15 -15
- skfolio/utils/tools.py +86 -22
- {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info}/METADATA +122 -46
- skfolio-0.8.0.dist-info/RECORD +120 -0
- {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info}/WHEEL +1 -1
- skfolio-0.6.0.dist-info/RECORD +0 -95
- {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info/licenses}/LICENSE +0 -0
- {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# Implementation derived from:
|
7
7
|
# Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
8
|
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
@@ -18,6 +18,7 @@ import sklearn.base as skb
|
|
18
18
|
import sklearn.model_selection as sks
|
19
19
|
import sklearn.utils.metadata_routing as skm
|
20
20
|
import sklearn.utils.parallel as skp
|
21
|
+
import sklearn.utils.validation as skv
|
21
22
|
|
22
23
|
import skfolio.typing as skt
|
23
24
|
from skfolio.cluster import HierarchicalClustering
|
@@ -311,9 +312,9 @@ class NestedClustersOptimization(BaseOptimization):
|
|
311
312
|
w[cluster_ids] = fitted_inner_estimator.weights_
|
312
313
|
inner_weights.append(w)
|
313
314
|
inner_weights = np.array(inner_weights)
|
314
|
-
assert not any(
|
315
|
-
|
316
|
-
)
|
315
|
+
assert not any(fitted_inner_estimators), (
|
316
|
+
"fitted_inner_estimator iterator must be empty"
|
317
|
+
)
|
317
318
|
|
318
319
|
# Outer cluster weights
|
319
320
|
# To train the outer-estimator using the most data as possible, we use
|
@@ -355,10 +356,10 @@ class NestedClustersOptimization(BaseOptimization):
|
|
355
356
|
# We validate and convert to numpy array only after inner-estimator fitting to
|
356
357
|
# keep the assets names in case they are used in the estimator.
|
357
358
|
if y is not None:
|
358
|
-
X, y =
|
359
|
+
X, y = skv.validate_data(self, X, y)
|
359
360
|
y_pred = y[test_indices]
|
360
361
|
else:
|
361
|
-
X =
|
362
|
+
X = skv.validate_data(self, X)
|
362
363
|
y_pred = None
|
363
364
|
|
364
365
|
X_pred = []
|
@@ -379,9 +380,9 @@ class NestedClustersOptimization(BaseOptimization):
|
|
379
380
|
X_pred.append(np.asarray(pred))
|
380
381
|
X_pred = np.array(X_pred).T
|
381
382
|
if cv_predictions is None:
|
382
|
-
assert not any(
|
383
|
-
|
384
|
-
)
|
383
|
+
assert not any(fitted_inner_estimators), (
|
384
|
+
"fitted_inner_estimator iterator must be empty"
|
385
|
+
)
|
385
386
|
else:
|
386
387
|
assert not any(cv_predictions), "cv_predictions iterator must be empty"
|
387
388
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
"""Hierarchical Optimization module."""
|
2
|
+
|
1
3
|
from skfolio.optimization.cluster.hierarchical._base import (
|
2
4
|
BaseHierarchicalOptimization,
|
3
5
|
)
|
@@ -8,6 +10,6 @@ from skfolio.optimization.cluster.hierarchical._hrp import HierarchicalRiskParit
|
|
8
10
|
|
9
11
|
__all__ = [
|
10
12
|
"BaseHierarchicalOptimization",
|
11
|
-
"HierarchicalRiskParity",
|
12
13
|
"HierarchicalEqualRiskContribution",
|
14
|
+
"HierarchicalRiskParity",
|
13
15
|
]
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# Implementation derived from:
|
7
7
|
# Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
8
|
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
@@ -349,7 +349,6 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
|
|
349
349
|
max_weights : ndarray of shape (n_assets,)
|
350
350
|
The weight upper bound 1D array.
|
351
351
|
"""
|
352
|
-
|
353
352
|
if self.min_weights is None:
|
354
353
|
min_weights = np.zeros(n_assets)
|
355
354
|
else:
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# Weight constraints is a novel implementation, see docstring for more details.
|
7
7
|
|
8
8
|
import numpy as np
|
@@ -10,6 +10,7 @@ import numpy.typing as npt
|
|
10
10
|
import pandas as pd
|
11
11
|
import scipy.cluster.hierarchy as sch
|
12
12
|
import sklearn.utils.metadata_routing as skm
|
13
|
+
import sklearn.utils.validation as skv
|
13
14
|
|
14
15
|
import skfolio.typing as skt
|
15
16
|
from skfolio.cluster import HierarchicalClustering
|
@@ -346,7 +347,7 @@ class HierarchicalEqualRiskContribution(BaseHierarchicalOptimization):
|
|
346
347
|
if self.risk_measure in [ExtraRiskMeasure.SKEW, ExtraRiskMeasure.KURTOSIS]:
|
347
348
|
# Because Skew and Kurtosis can take negative values
|
348
349
|
raise ValueError(
|
349
|
-
f"risk_measure {self.risk_measure} currently not supported
|
350
|
+
f"risk_measure {self.risk_measure} currently not supported in HERC"
|
350
351
|
)
|
351
352
|
|
352
353
|
self.prior_estimator_ = check_estimator(
|
@@ -391,7 +392,7 @@ class HierarchicalEqualRiskContribution(BaseHierarchicalOptimization):
|
|
391
392
|
labels = self.hierarchical_clustering_estimator_.labels_
|
392
393
|
linkage_matrix = self.hierarchical_clustering_estimator_.linkage_matrix_
|
393
394
|
|
394
|
-
X =
|
395
|
+
X = skv.validate_data(self, X)
|
395
396
|
n_assets = X.shape[1]
|
396
397
|
|
397
398
|
min_weights, max_weights = self._convert_weights_bounds(n_assets=n_assets)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# The risk measure generalization and constraint features are derived
|
7
7
|
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
8
|
|
@@ -11,6 +11,7 @@ import numpy.typing as npt
|
|
11
11
|
import pandas as pd
|
12
12
|
import scipy.cluster.hierarchy as sch
|
13
13
|
import sklearn.utils.metadata_routing as skm
|
14
|
+
import sklearn.utils.validation as skv
|
14
15
|
|
15
16
|
import skfolio.typing as skt
|
16
17
|
from skfolio.cluster import HierarchicalClustering
|
@@ -298,7 +299,7 @@ class HierarchicalRiskParity(BaseHierarchicalOptimization):
|
|
298
299
|
if self.risk_measure in [ExtraRiskMeasure.SKEW, ExtraRiskMeasure.KURTOSIS]:
|
299
300
|
# Because Skew and Kurtosis can take negative values
|
300
301
|
raise ValueError(
|
301
|
-
f"risk_measure {self.risk_measure} currently not supported
|
302
|
+
f"risk_measure {self.risk_measure} currently not supported in HRP"
|
302
303
|
)
|
303
304
|
|
304
305
|
self.prior_estimator_ = check_estimator(
|
@@ -339,7 +340,7 @@ class HierarchicalRiskParity(BaseHierarchicalOptimization):
|
|
339
340
|
X=distance, y=None, **routed_params.hierarchical_clustering_estimator.fit
|
340
341
|
)
|
341
342
|
|
342
|
-
X =
|
343
|
+
X = skv.validate_data(self, X)
|
343
344
|
n_assets = X.shape[1]
|
344
345
|
|
345
346
|
min_weights, max_weights = self._convert_weights_bounds(n_assets=n_assets)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
"""Convex Optimization module."""
|
2
|
+
|
1
3
|
from skfolio.optimization.convex._base import ConvexOptimization, ObjectiveFunction
|
2
4
|
from skfolio.optimization.convex._distributionally_robust import (
|
3
5
|
DistributionallyRobustCVaR,
|
@@ -7,10 +9,10 @@ from skfolio.optimization.convex._mean_risk import MeanRisk
|
|
7
9
|
from skfolio.optimization.convex._risk_budgeting import RiskBudgeting
|
8
10
|
|
9
11
|
__all__ = [
|
10
|
-
"ObjectiveFunction",
|
11
12
|
"ConvexOptimization",
|
12
|
-
"MeanRisk",
|
13
|
-
"RiskBudgeting",
|
14
13
|
"DistributionallyRobustCVaR",
|
15
14
|
"MaximumDiversification",
|
15
|
+
"MeanRisk",
|
16
|
+
"ObjectiveFunction",
|
17
|
+
"RiskBudgeting",
|
16
18
|
]
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# The optimization features are derived
|
7
7
|
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
8
|
|
@@ -144,7 +144,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
144
144
|
all weights). `None` means no budget constraints.
|
145
145
|
The default value is `1.0` (fully invested portfolio).
|
146
146
|
|
147
|
-
|
147
|
+
For example:
|
148
148
|
|
149
149
|
* `budget = 1` --> fully invested portfolio.
|
150
150
|
* `budget = 0` --> market neutral portfolio.
|
@@ -329,7 +329,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
329
329
|
`groups` if the input `X` of the `fit` method is a DataFrame with these
|
330
330
|
assets names in columns.
|
331
331
|
|
332
|
-
|
332
|
+
For example:
|
333
333
|
|
334
334
|
* "SPX >= 0.10" --> SPX weight must be greater than 10% (note that you can also use `min_weights`)
|
335
335
|
* "SX5E + TLT >= 0.2" --> the sum of SX5E and TLT weights must be greater than 20%
|
@@ -343,7 +343,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
343
343
|
(asset name/asset groups) and the input `X` of the `fit` method must be a
|
344
344
|
DataFrame with the assets names in columns.
|
345
345
|
|
346
|
-
|
346
|
+
For example:
|
347
347
|
|
348
348
|
* groups = {"SX5E": ["Equity", "Europe"], "SPX": ["Equity", "US"], "TLT": ["Bond", "US"]}
|
349
349
|
* groups = [["Equity", "Equity", "Bond"], ["Europe", "US", "US"]]
|
@@ -657,7 +657,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
657
657
|
)
|
658
658
|
|
659
659
|
def _clear_models_cache(self):
|
660
|
-
"""CLear the cache of CVX models"""
|
660
|
+
"""CLear the cache of CVX models."""
|
661
661
|
self._cvx_cache = {}
|
662
662
|
|
663
663
|
def _get_weight_constraints(
|
@@ -989,7 +989,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
989
989
|
self._scale_constraints = cp.Constant(self.scale_constraints)
|
990
990
|
|
991
991
|
def _get_custom_objective(self, w: cp.Variable) -> cp.Expression:
|
992
|
-
"""
|
992
|
+
"""Return the CVXPY expression evaluated by calling the `add_objective`
|
993
993
|
function if provided, otherwise returns the CVXPY constant `0`.
|
994
994
|
|
995
995
|
Parameters
|
@@ -1010,7 +1010,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
1010
1010
|
)
|
1011
1011
|
|
1012
1012
|
def _get_custom_constraints(self, w: cp.Variable) -> list[cp.Expression]:
|
1013
|
-
"""
|
1013
|
+
"""Return the list of CVXPY expressions evaluated by calling the
|
1014
1014
|
`add_constraint`s function if provided, otherwise returns an empty list.
|
1015
1015
|
|
1016
1016
|
Parameters
|
@@ -1037,7 +1037,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
1037
1037
|
def _cvx_expected_return(
|
1038
1038
|
self, prior_model: PriorModel, w: cp.Variable
|
1039
1039
|
) -> cp.Expression:
|
1040
|
-
"""Expected Return expression"""
|
1040
|
+
"""Expected Return expression."""
|
1041
1041
|
if self.overwrite_expected_return is None:
|
1042
1042
|
expected_return = prior_model.mu @ w
|
1043
1043
|
else:
|
@@ -1685,7 +1685,8 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
1685
1685
|
z2 = cp.vstack([w_reshaped, factor_reshaped])
|
1686
1686
|
|
1687
1687
|
risk = covariance_uncertainty_set.k * cp.pnorm(
|
1688
|
-
sc.linalg.sqrtm(covariance_uncertainty_set.sigma)
|
1688
|
+
sc.linalg.sqrtm(covariance_uncertainty_set.sigma)
|
1689
|
+
@ (cp.vec(x, order="F") + cp.vec(y, order="F")),
|
1689
1690
|
2,
|
1690
1691
|
) + cp.trace(prior_model.covariance @ (x + y))
|
1691
1692
|
# semi-definite positive constraints
|
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
|
7
7
|
import cvxpy as cp
|
8
8
|
import numpy as np
|
9
9
|
import numpy.typing as npt
|
10
10
|
import sklearn.utils.metadata_routing as skm
|
11
|
+
import sklearn.utils.validation as skv
|
11
12
|
|
12
13
|
import skfolio.typing as skt
|
13
14
|
from skfolio.measures import RiskMeasure
|
@@ -93,7 +94,7 @@ class DistributionallyRobustCVaR(ConvexOptimization):
|
|
93
94
|
all weights). `None` means no budget constraints.
|
94
95
|
The default value is `1.0` (fully invested portfolio).
|
95
96
|
|
96
|
-
|
97
|
+
For example:
|
97
98
|
|
98
99
|
* `budget = 1` --> fully invested portfolio.
|
99
100
|
* `budget = 0` --> market neutral portfolio.
|
@@ -133,7 +134,7 @@ class DistributionallyRobustCVaR(ConvexOptimization):
|
|
133
134
|
`groups` if the input `X` of the `fit` method is a DataFrame with these
|
134
135
|
assets names in columns.
|
135
136
|
|
136
|
-
|
137
|
+
For example:
|
137
138
|
|
138
139
|
* "SPX >= 0.10" --> SPX weight must be greater than 10% (note that you can also use `min_weights`)
|
139
140
|
* "SX5E + TLT >= 0.2" --> the sum of SX5E and TLT weights must be greater than 20%
|
@@ -147,7 +148,7 @@ class DistributionallyRobustCVaR(ConvexOptimization):
|
|
147
148
|
(asset name/asset groups) and the input `X` of the `fit` method must be a
|
148
149
|
DataFrame with the assets names in columns.
|
149
150
|
|
150
|
-
|
151
|
+
For example:
|
151
152
|
|
152
153
|
* groups = {"SX5E": ["Equity", "Europe"], "SPX": ["Equity", "US"], "TLT": ["Bond", "US"]}
|
153
154
|
* groups = [["Equity", "Equity", "Bond"], ["Europe", "US", "US"]]
|
@@ -331,7 +332,9 @@ class DistributionallyRobustCVaR(ConvexOptimization):
|
|
331
332
|
"""
|
332
333
|
routed_params = skm.process_routing(self, "fit", **fit_params)
|
333
334
|
|
334
|
-
|
335
|
+
# `X` is unchanged and only `feature_names_in_` is performed
|
336
|
+
_ = skv.validate_data(self, X, skip_check_array=True)
|
337
|
+
|
335
338
|
# Used to avoid adding multiple times similar constrains linked to identical
|
336
339
|
# risk models
|
337
340
|
self.prior_estimator_ = check_estimator(
|
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
|
7
7
|
import numpy as np
|
8
8
|
import numpy.typing as npt
|
9
|
+
import sklearn.utils.validation as skv
|
9
10
|
|
10
11
|
import skfolio.typing as skt
|
11
12
|
from skfolio.measures import RiskMeasure
|
@@ -76,7 +77,7 @@ class MaximumDiversification(MeanRisk):
|
|
76
77
|
all weights). `None` means no budget constraints.
|
77
78
|
The default value is `1.0` (fully invested portfolio).
|
78
79
|
|
79
|
-
|
80
|
+
For example:
|
80
81
|
|
81
82
|
* `budget = 1` --> fully invested portfolio.
|
82
83
|
* `budget = 0` --> market neutral portfolio.
|
@@ -209,7 +210,7 @@ class MaximumDiversification(MeanRisk):
|
|
209
210
|
`groups` if the input `X` of the `fit` method is a DataFrame with these
|
210
211
|
assets names in columns.
|
211
212
|
|
212
|
-
|
213
|
+
For example:
|
213
214
|
|
214
215
|
* "SPX >= 0.10" --> SPX weight must be greater than 10% (note that you can also use `min_weights`)
|
215
216
|
* "SX5E + TLT >= 0.2" --> the sum of SX5E and TLT weights must be greater than 20%
|
@@ -223,7 +224,7 @@ class MaximumDiversification(MeanRisk):
|
|
223
224
|
(asset name/asset groups) and the input `X` of the `fit` method must be a
|
224
225
|
DataFrame with the assets names in columns.
|
225
226
|
|
226
|
-
|
227
|
+
For example:
|
227
228
|
|
228
229
|
* groups = {"SX5E": ["Equity", "Europe"], "SPX": ["Equity", "US"], "TLT": ["Bond", "US"]}
|
229
230
|
* groups = [["Equity", "Equity", "Bond"], ["Europe", "US", "US"]]
|
@@ -423,10 +424,11 @@ class MaximumDiversification(MeanRisk):
|
|
423
424
|
self : MaximumDiversification
|
424
425
|
Fitted estimator.
|
425
426
|
"""
|
426
|
-
|
427
|
+
# `X` is unchanged and only `feature_names_in_` is performed
|
428
|
+
_ = skv.validate_data(self, X, skip_check_array=True)
|
427
429
|
|
428
430
|
def func(w, obj):
|
429
|
-
"""
|
431
|
+
"""Weighted volatilities."""
|
430
432
|
covariance = obj.prior_estimator_.prior_model_.covariance
|
431
433
|
return np.sqrt(np.diag(covariance)) @ w
|
432
434
|
|
@@ -4,7 +4,7 @@ import warnings
|
|
4
4
|
|
5
5
|
# Copyright (c) 2023
|
6
6
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
7
|
-
# License: BSD
|
7
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
8
8
|
# The optimization features are derived
|
9
9
|
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
10
10
|
import cvxpy as cp
|
@@ -13,6 +13,7 @@ import numpy.typing as npt
|
|
13
13
|
import pandas as pd
|
14
14
|
import sklearn as sk
|
15
15
|
import sklearn.utils.metadata_routing as skm
|
16
|
+
import sklearn.utils.validation as skv
|
16
17
|
|
17
18
|
import skfolio.typing as skt
|
18
19
|
from skfolio.measures import RiskMeasure
|
@@ -193,7 +194,7 @@ class MeanRisk(ConvexOptimization):
|
|
193
194
|
all weights). `None` means no budget constraints.
|
194
195
|
The default value is `1.0` (fully invested portfolio).
|
195
196
|
|
196
|
-
|
197
|
+
For example:
|
197
198
|
|
198
199
|
* `budget = 1` --> fully invested portfolio.
|
199
200
|
* `budget = 0` --> market neutral portfolio.
|
@@ -378,7 +379,7 @@ class MeanRisk(ConvexOptimization):
|
|
378
379
|
`groups` if the input `X` of the `fit` method is a DataFrame with these
|
379
380
|
assets names in columns.
|
380
381
|
|
381
|
-
|
382
|
+
For example:
|
382
383
|
|
383
384
|
* "SPX >= 0.10" --> SPX weight must be greater than 10% (note that you can also use `min_weights`)
|
384
385
|
* "SX5E + TLT >= 0.2" --> the sum of SX5E and TLT weights must be greater than 20%
|
@@ -392,7 +393,7 @@ class MeanRisk(ConvexOptimization):
|
|
392
393
|
(asset name/asset groups) and the input `X` of the `fit` method must be a
|
393
394
|
DataFrame with the assets names in columns.
|
394
395
|
|
395
|
-
|
396
|
+
For example:
|
396
397
|
|
397
398
|
* groups = {"SX5E": ["Equity", "Europe"], "SPX": ["Equity", "US"], "TLT": ["Bond", "US"]}
|
398
399
|
* groups = [["Equity", "Equity", "Bond"], ["Europe", "US", "US"]]
|
@@ -711,7 +712,7 @@ class MeanRisk(ConvexOptimization):
|
|
711
712
|
self.max_gini_mean_difference = max_gini_mean_difference
|
712
713
|
|
713
714
|
def _validation(self) -> None:
|
714
|
-
"""Validate the input parameters"""
|
715
|
+
"""Validate the input parameters."""
|
715
716
|
if not isinstance(self.risk_measure, RiskMeasure):
|
716
717
|
raise TypeError("risk_measure must be of type `RiskMeasure`")
|
717
718
|
if not isinstance(self.objective_function, ObjectiveFunction):
|
@@ -764,7 +765,9 @@ class MeanRisk(ConvexOptimization):
|
|
764
765
|
"""
|
765
766
|
routed_params = skm.process_routing(self, "fit", **fit_params)
|
766
767
|
|
767
|
-
|
768
|
+
# `X` is unchanged and only `feature_names_in_` is performed
|
769
|
+
_ = skv.validate_data(self, X, skip_check_array=True)
|
770
|
+
|
768
771
|
# Validate
|
769
772
|
self._validation()
|
770
773
|
# Used to avoid adding multiple times similar constrains linked to identical
|
@@ -893,7 +896,7 @@ class MeanRisk(ConvexOptimization):
|
|
893
896
|
" 1d-array, a single-column DataFrame or a Series"
|
894
897
|
)
|
895
898
|
y = y[y.columns[0]]
|
896
|
-
_, y =
|
899
|
+
_, y = skv.validate_data(self, X, y)
|
897
900
|
tracking_error = self._tracking_error(
|
898
901
|
prior_model=prior_model, w=w, y=y, factor=factor
|
899
902
|
)
|
@@ -1117,5 +1120,4 @@ def _optimal_homogenization_factor(mu: np.ndarray) -> float:
|
|
1117
1120
|
value : float
|
1118
1121
|
Homogenization factor.
|
1119
1122
|
"""
|
1120
|
-
|
1121
1123
|
return min(1e3, max(1e-3, np.mean(np.abs(mu))))
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# The optimization features are derived
|
7
7
|
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
8
|
|
@@ -10,6 +10,7 @@ import cvxpy as cp
|
|
10
10
|
import numpy as np
|
11
11
|
import numpy.typing as npt
|
12
12
|
import sklearn.utils.metadata_routing as skm
|
13
|
+
import sklearn.utils.validation as skv
|
13
14
|
|
14
15
|
import skfolio.typing as skt
|
15
16
|
from skfolio.measures import RiskMeasure
|
@@ -225,7 +226,7 @@ class RiskBudgeting(ConvexOptimization):
|
|
225
226
|
`groups` if the input `X` of the `fit` method is a DataFrame with these
|
226
227
|
assets names in columns.
|
227
228
|
|
228
|
-
|
229
|
+
For example:
|
229
230
|
|
230
231
|
* "SPX >= 0.10" --> SPX weight must be greater than 10% (note that you can also use `min_weights`)
|
231
232
|
* "SX5E + TLT >= 0.2" --> the sum of SX5E and TLT weights must be greater than 20%
|
@@ -239,7 +240,7 @@ class RiskBudgeting(ConvexOptimization):
|
|
239
240
|
(asset name/asset groups) and the input `X` of the `fit` method must be a
|
240
241
|
DataFrame with the assets names in columns.
|
241
242
|
|
242
|
-
|
243
|
+
For example:
|
243
244
|
|
244
245
|
* groups = {"SX5E": ["Equity", "Europe"], "SPX": ["Equity", "US"], "TLT": ["Bond", "US"]}
|
245
246
|
* groups = [["Equity", "Equity", "Bond"], ["Europe", "US", "US"]]
|
@@ -452,7 +453,8 @@ class RiskBudgeting(ConvexOptimization):
|
|
452
453
|
"""
|
453
454
|
routed_params = skm.process_routing(self, "fit", **fit_params)
|
454
455
|
|
455
|
-
|
456
|
+
# `X` is unchanged and only `feature_names_in_` is performed
|
457
|
+
_ = skv.validate_data(self, X, skip_check_array=True)
|
456
458
|
|
457
459
|
if not isinstance(self.risk_measure, RiskMeasure):
|
458
460
|
raise TypeError("risk_measure must be of type `RiskMeasure`")
|
@@ -1,10 +1,10 @@
|
|
1
1
|
"""Base Composition estimator.
|
2
|
-
Follow same implementation as Base composition from sklearn
|
2
|
+
Follow same implementation as Base composition from sklearn.
|
3
3
|
"""
|
4
4
|
|
5
5
|
# Copyright (c) 2023
|
6
6
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
7
|
-
# License: BSD
|
7
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
8
8
|
# Implementation derived from:
|
9
9
|
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
10
10
|
# Grisel Licensed under BSD 3 clause.
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Copyright (c) 2023
|
4
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
-
# License: BSD
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
6
6
|
# Implementation derived from:
|
7
7
|
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
8
8
|
# Grisel Licensed under BSD 3 clause.
|
@@ -330,9 +330,9 @@ class StackingOptimization(BaseOptimization, BaseComposition):
|
|
330
330
|
# We validate and convert to numpy array only after base-estimator fitting
|
331
331
|
# to keep the assets names in case they are used in the estimator.
|
332
332
|
if y is not None:
|
333
|
-
_, y =
|
333
|
+
_, y = skv.validate_data(self, X, y, multi_output=True)
|
334
334
|
else:
|
335
|
-
_ =
|
335
|
+
_ = skv.validate_data(self, X)
|
336
336
|
|
337
337
|
if isinstance(self.cv, BaseCombinatorialCV):
|
338
338
|
X_pred = np.array(
|
@@ -1,11 +1,12 @@
|
|
1
1
|
"""Naive estimators."""
|
2
2
|
|
3
3
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
|
-
# License: BSD
|
4
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
5
5
|
|
6
6
|
import numpy as np
|
7
7
|
import numpy.typing as npt
|
8
8
|
import sklearn.utils.metadata_routing as skm
|
9
|
+
import sklearn.utils.validation as skv
|
9
10
|
|
10
11
|
from skfolio.optimization._base import BaseOptimization
|
11
12
|
from skfolio.prior import BasePrior, EmpiricalPrior
|
@@ -141,7 +142,7 @@ class EqualWeighted(BaseOptimization):
|
|
141
142
|
self : EqualWeighted
|
142
143
|
Fitted estimator.
|
143
144
|
"""
|
144
|
-
X =
|
145
|
+
X = skv.validate_data(self, X)
|
145
146
|
n_assets = X.shape[1]
|
146
147
|
self.weights_ = np.ones(n_assets) / n_assets
|
147
148
|
return self
|
@@ -185,7 +186,7 @@ class Random(BaseOptimization):
|
|
185
186
|
self : EqualWeighted
|
186
187
|
Fitted estimator.
|
187
188
|
"""
|
188
|
-
X =
|
189
|
+
X = skv.validate_data(self, X)
|
189
190
|
n_assets = X.shape[1]
|
190
191
|
self.weights_ = rand_weights_dirichlet(n=n_assets)
|
191
192
|
return self
|