skfolio 0.6.0__tar.gz → 0.7.0__tar.gz
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-0.6.0/src/skfolio.egg-info → skfolio-0.7.0}/PKG-INFO +9 -9
- {skfolio-0.6.0 → skfolio-0.7.0}/pyproject.toml +25 -24
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/cluster/__init__.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/cluster/_hierarchical.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/distance/_distance.py +7 -6
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_denoise_covariance.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_detone_covariance.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_empirical_covariance.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_ew_covariance.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_gerber_covariance.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_implied_covariance.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/expected_returns/_empirical_mu.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/expected_returns/_equilibrium_mu.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/expected_returns/_ew_mu.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/expected_returns/_shrunk_mu.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/cluster/_nco.py +3 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/cluster/hierarchical/_herc.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/cluster/hierarchical/_hrp.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/convex/_base.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/convex/_distributionally_robust.py +4 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/convex/_maximum_diversification.py +3 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/convex/_mean_risk.py +5 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/convex/_risk_budgeting.py +3 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/ensemble/_stacking.py +2 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/naive/__init__.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/naive/_naive.py +3 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/portfolio/__init__.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/portfolio/_base.py +1 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/portfolio/_portfolio.py +1 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/pre_selection/_drop_correlated.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/pre_selection/_select_complete.py +6 -4
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/pre_selection/_select_k_extremes.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/pre_selection/_select_non_dominated.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/pre_selection/_select_non_expiring.py +6 -4
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/prior/_black_litterman.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/prior/_empirical.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/prior/_factor_model.py +2 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/uncertainty_set/_base.py +3 -2
- {skfolio-0.6.0 → skfolio-0.7.0/src/skfolio.egg-info}/PKG-INFO +9 -9
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio.egg-info/requires.txt +8 -7
- {skfolio-0.6.0 → skfolio-0.7.0}/LICENSE +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/MANIFEST.in +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/README.rst +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/setup.cfg +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/__init__.py +5 -5
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/datasets/__init__.py +2 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/datasets/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/datasets/data/__init__.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/datasets/data/factors_dataset.csv.gz +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/datasets/data/sp500_dataset.csv.gz +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/datasets/data/sp500_index.csv.gz +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/distance/__init__.py +3 -3
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/distance/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/exceptions.py +2 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/measures/__init__.py +23 -23
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/measures/_enums.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/measures/_measures.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/metrics/__init__.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/metrics/_scorer.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/model_selection/__init__.py +2 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/model_selection/_combinatorial.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/model_selection/_validation.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/model_selection/_walk_forward.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/__init__.py +11 -11
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/__init__.py +6 -6
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_graphical_lasso_cv.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_ledoit_wolf.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_oas.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/covariance/_shrunk_covariance.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/expected_returns/__init__.py +2 -2
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/moments/expected_returns/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/__init__.py +10 -10
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/cluster/__init__.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/cluster/hierarchical/__init__.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/cluster/hierarchical/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/convex/__init__.py +3 -3
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/ensemble/__init__.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/optimization/ensemble/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/population/__init__.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/population/_population.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/portfolio/_multi_period_portfolio.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/pre_selection/__init__.py +1 -1
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/preprocessing/__init__.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/preprocessing/_returns.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/prior/__init__.py +3 -3
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/prior/_base.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/typing.py +6 -6
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/uncertainty_set/__init__.py +5 -5
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/uncertainty_set/_bootstrap.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/uncertainty_set/_empirical.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/utils/__init__.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/utils/bootstrap.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/utils/equations.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/utils/sorting.py +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/utils/stats.py +8 -8
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio/utils/tools.py +10 -10
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio.egg-info/SOURCES.txt +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio.egg-info/dependency_links.txt +0 -0
- {skfolio-0.6.0 → skfolio-0.7.0}/src/skfolio.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: skfolio
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: Portfolio optimization built on top of scikit-learn
|
5
5
|
Author-email: Hugo Delatte <delatte.hugo@gmail.com>
|
6
|
-
Maintainer-email: Hugo Delatte <delatte.hugo@gmail.com>
|
6
|
+
Maintainer-email: Hugo Delatte <delatte.hugo@gmail.com>, Matteo Manzi <matteomanzi09@gmail.com>
|
7
7
|
License: BSD 3-Clause License
|
8
8
|
|
9
9
|
Copyright (c) 2007-2023 The skfolio developers.
|
@@ -60,14 +60,15 @@ Requires-Dist: numpy>=1.23.4
|
|
60
60
|
Requires-Dist: scipy>=1.8.0
|
61
61
|
Requires-Dist: pandas>=1.4.1
|
62
62
|
Requires-Dist: cvxpy>=1.4.1
|
63
|
-
Requires-Dist: scikit-learn>=1.
|
63
|
+
Requires-Dist: scikit-learn>=1.6.0
|
64
64
|
Requires-Dist: joblib>=1.3.2
|
65
65
|
Requires-Dist: plotly>=5.22.0
|
66
|
-
Provides-Extra:
|
67
|
-
Requires-Dist: cvxpy[SCIP]; extra == "
|
68
|
-
Requires-Dist: pytest; extra == "
|
69
|
-
Requires-Dist: pytest-cov; extra == "
|
70
|
-
Requires-Dist: ruff; extra == "
|
66
|
+
Provides-Extra: dev
|
67
|
+
Requires-Dist: cvxpy[SCIP]<2.0.0,>=1.6.0; extra == "dev"
|
68
|
+
Requires-Dist: pytest<9.0.0,>=8.3.4; extra == "dev"
|
69
|
+
Requires-Dist: pytest-cov<7.0.0,>=6.0.0; extra == "dev"
|
70
|
+
Requires-Dist: ruff<1.0.0,>=0.8.4; extra == "dev"
|
71
|
+
Requires-Dist: pre-commit<4.2.0,>=4.0.0; extra == "dev"
|
71
72
|
Provides-Extra: docs
|
72
73
|
Requires-Dist: cvxpy[SCIP]; extra == "docs"
|
73
74
|
Requires-Dist: Sphinx; extra == "docs"
|
@@ -83,7 +84,6 @@ Requires-Dist: sphinx-favicon; extra == "docs"
|
|
83
84
|
Requires-Dist: sphinx-prompt; extra == "docs"
|
84
85
|
Requires-Dist: sphinxext.opengraph; extra == "docs"
|
85
86
|
Requires-Dist: sphinx-sitemap; extra == "docs"
|
86
|
-
Requires-Dist: sphinx-favicon; extra == "docs"
|
87
87
|
Requires-Dist: jupyterlite-sphinx; extra == "docs"
|
88
88
|
Requires-Dist: jupyterlite-pyodide-kernel; extra == "docs"
|
89
89
|
Requires-Dist: nbformat; extra == "docs"
|
@@ -4,25 +4,16 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "skfolio"
|
7
|
-
version = "0.
|
7
|
+
version = "0.7.0"
|
8
8
|
maintainers = [
|
9
9
|
{ name = "Hugo Delatte", email = "delatte.hugo@gmail.com" },
|
10
|
+
{ name = "Matteo Manzi", email = "matteomanzi09@gmail.com" }
|
10
11
|
]
|
11
12
|
authors = [
|
12
13
|
{ name = "Hugo Delatte", email = "delatte.hugo@gmail.com" },
|
13
14
|
]
|
14
15
|
description = "Portfolio optimization built on top of scikit-learn"
|
15
16
|
readme = "README.rst"
|
16
|
-
requires-python = ">=3.10"
|
17
|
-
dependencies = [
|
18
|
-
"numpy>=1.23.4",
|
19
|
-
"scipy>=1.8.0",
|
20
|
-
"pandas>=1.4.1",
|
21
|
-
"cvxpy>=1.4.1",
|
22
|
-
"scikit-learn>=1.5.0",
|
23
|
-
"joblib>=1.3.2",
|
24
|
-
"plotly>=5.22.0"
|
25
|
-
]
|
26
17
|
keywords = [
|
27
18
|
"portfolio",
|
28
19
|
"optimization",
|
@@ -57,13 +48,30 @@ classifiers = [
|
|
57
48
|
'Topic :: Office/Business :: Financial :: Investment',
|
58
49
|
"Topic :: Software Development",
|
59
50
|
]
|
51
|
+
requires-python = ">=3.10"
|
52
|
+
dependencies = [
|
53
|
+
"numpy>=1.23.4",
|
54
|
+
"scipy>=1.8.0",
|
55
|
+
"pandas>=1.4.1",
|
56
|
+
"cvxpy>=1.4.1",
|
57
|
+
"scikit-learn>=1.6.0",
|
58
|
+
"joblib>=1.3.2",
|
59
|
+
"plotly>=5.22.0"
|
60
|
+
]
|
61
|
+
|
62
|
+
[project.urls]
|
63
|
+
"API Reference" = "https://www.skfolio.org/api_reference.html"
|
64
|
+
Documentation = "https://www.skfolio.org"
|
65
|
+
Tutorials = "https://www.skfolio.org"
|
66
|
+
Repository = "https://github.com/skfolio/skfolio"
|
60
67
|
|
61
68
|
[project.optional-dependencies]
|
62
|
-
|
63
|
-
"cvxpy[SCIP]",
|
64
|
-
"pytest",
|
65
|
-
"pytest-cov",
|
66
|
-
"ruff"
|
69
|
+
dev = [
|
70
|
+
"cvxpy[SCIP]>=1.6.0,<2.0.0",
|
71
|
+
"pytest>=8.3.4,<9.0.0",
|
72
|
+
"pytest-cov>=6.0.0,<7.0.0",
|
73
|
+
"ruff>=0.8.4,<1.0.0",
|
74
|
+
"pre-commit>=4.0.0,<4.2.0",
|
67
75
|
]
|
68
76
|
docs = [
|
69
77
|
"cvxpy[SCIP]",
|
@@ -80,18 +88,11 @@ docs = [
|
|
80
88
|
"sphinx-prompt",
|
81
89
|
"sphinxext.opengraph",
|
82
90
|
"sphinx-sitemap",
|
83
|
-
"sphinx-favicon",
|
84
91
|
"jupyterlite-sphinx",
|
85
92
|
"jupyterlite-pyodide-kernel",
|
86
93
|
"nbformat",
|
87
94
|
]
|
88
95
|
|
89
|
-
[project.urls]
|
90
|
-
"API Reference" = "https://www.skfolio.org/api_reference.html"
|
91
|
-
Documentation = "https://www.skfolio.org"
|
92
|
-
Tutorials = "https://www.skfolio.org"
|
93
|
-
Repository = "https://github.com/skfolio/skfolio"
|
94
|
-
|
95
96
|
[tool.setuptools.packages.find]
|
96
97
|
where = ["src"]
|
97
98
|
|
@@ -140,4 +141,4 @@ max-line-length = 320
|
|
140
141
|
[tool.pytest.ini_options]
|
141
142
|
addopts = [
|
142
143
|
"--import-mode=importlib",
|
143
|
-
]
|
144
|
+
]
|
@@ -180,7 +180,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
|
|
180
180
|
self : HierarchicalClustering
|
181
181
|
Fitted estimator.
|
182
182
|
"""
|
183
|
-
X =
|
183
|
+
X = skv.validate_data(self, X)
|
184
184
|
assert_is_distance(X)
|
185
185
|
self.condensed_distance_ = scd.squareform(X, checks=False)
|
186
186
|
self.linkage_matrix_ = sch.linkage(
|
@@ -11,6 +11,7 @@ import scipy.spatial.distance as scd
|
|
11
11
|
import scipy.stats as sct
|
12
12
|
import sklearn.metrics as skmc
|
13
13
|
import sklearn.utils.metadata_routing as skm
|
14
|
+
import sklearn.utils.validation as skv
|
14
15
|
|
15
16
|
from skfolio.distance._base import BaseDistance
|
16
17
|
from skfolio.moments import BaseCovariance, GerberCovariance
|
@@ -85,7 +86,7 @@ class PearsonDistance(BaseDistance):
|
|
85
86
|
self : PearsonDistance
|
86
87
|
Fitted estimator.
|
87
88
|
"""
|
88
|
-
X =
|
89
|
+
X = skv.validate_data(self, X)
|
89
90
|
corr = np.corrcoef(X.T)
|
90
91
|
self.codependence_, self.distance_ = _corr_to_distance(
|
91
92
|
corr, absolute=self.absolute, power=self.power
|
@@ -157,7 +158,7 @@ class KendallDistance(BaseDistance):
|
|
157
158
|
self : KendallDistance
|
158
159
|
Fitted estimator.
|
159
160
|
"""
|
160
|
-
X =
|
161
|
+
X = skv.validate_data(self, X)
|
161
162
|
corr = pd.DataFrame(X).corr(method="kendall").to_numpy()
|
162
163
|
self.codependence_, self.distance_ = _corr_to_distance(
|
163
164
|
corr, absolute=self.absolute, power=self.power
|
@@ -229,7 +230,7 @@ class SpearmanDistance(BaseDistance):
|
|
229
230
|
self : SpearmanDistance
|
230
231
|
Fitted estimator.
|
231
232
|
"""
|
232
|
-
X =
|
233
|
+
X = skv.validate_data(self, X)
|
233
234
|
corr = pd.DataFrame(X).corr(method="spearman").to_numpy()
|
234
235
|
self.codependence_, self.distance_ = _corr_to_distance(
|
235
236
|
corr, absolute=self.absolute, power=self.power
|
@@ -337,7 +338,7 @@ class CovarianceDistance(BaseDistance):
|
|
337
338
|
|
338
339
|
# we validate and convert to numpy after all models have been fitted to keep the
|
339
340
|
# features names information.
|
340
|
-
_ =
|
341
|
+
_ = skv.validate_data(self, X)
|
341
342
|
|
342
343
|
corr, _ = cov_to_corr(self.covariance_estimator_.covariance_)
|
343
344
|
self.codependence_, self.distance_ = _corr_to_distance(
|
@@ -409,7 +410,7 @@ class DistanceCorrelation(BaseDistance):
|
|
409
410
|
self : DistanceCorrelation
|
410
411
|
Fitted estimator.
|
411
412
|
"""
|
412
|
-
X =
|
413
|
+
X = skv.validate_data(self, X)
|
413
414
|
n_assets = X.shape[1]
|
414
415
|
corr = np.ones((n_assets, n_assets))
|
415
416
|
# TODO: parallelize
|
@@ -502,7 +503,7 @@ class MutualInformation(BaseDistance):
|
|
502
503
|
self : MutualInformation
|
503
504
|
Fitted estimator.
|
504
505
|
"""
|
505
|
-
X =
|
506
|
+
X = skv.validate_data(self, X)
|
506
507
|
n_assets = X.shape[1]
|
507
508
|
if self.n_bins is None:
|
508
509
|
match self.n_bins_method:
|
@@ -12,6 +12,7 @@ import numpy.typing as npt
|
|
12
12
|
import scipy.optimize as sco
|
13
13
|
import sklearn.neighbors as skn
|
14
14
|
import sklearn.utils.metadata_routing as skm
|
15
|
+
import sklearn.utils.validation as skv
|
15
16
|
|
16
17
|
from skfolio.moments.covariance._base import BaseCovariance
|
17
18
|
from skfolio.moments.covariance._empirical_covariance import EmpiricalCovariance
|
@@ -139,7 +140,7 @@ class DenoiseCovariance(BaseCovariance):
|
|
139
140
|
|
140
141
|
# we validate and convert to numpy after all models have been fitted to keep
|
141
142
|
# features names information.
|
142
|
-
X =
|
143
|
+
X = skv.validate_data(self, X)
|
143
144
|
n_observations, n_assets = X.shape
|
144
145
|
q = n_observations / n_assets
|
145
146
|
corr, std = cov_to_corr(self.covariance_estimator_.covariance_)
|
@@ -10,6 +10,7 @@
|
|
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
|
from skfolio.moments.covariance._base import BaseCovariance
|
15
16
|
from skfolio.moments.covariance._empirical_covariance import EmpiricalCovariance
|
@@ -141,7 +142,7 @@ class DetoneCovariance(BaseCovariance):
|
|
141
142
|
|
142
143
|
# we validate and convert to numpy after all models have been fitted to keep
|
143
144
|
# features names information.
|
144
|
-
_ =
|
145
|
+
_ = skv.validate_data(self, X)
|
145
146
|
corr, std = cov_to_corr(self.covariance_estimator_.covariance_)
|
146
147
|
e_val, e_vec = np.linalg.eigh(corr)
|
147
148
|
indices = e_val.argsort()[::-1]
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
import numpy as np
|
11
11
|
import numpy.typing as npt
|
12
|
+
import sklearn.utils.validation as skv
|
12
13
|
|
13
14
|
from skfolio.moments.covariance._base import BaseCovariance
|
14
15
|
|
@@ -92,7 +93,7 @@ class EmpiricalCovariance(BaseCovariance):
|
|
92
93
|
self : EmpiricalCovariance
|
93
94
|
Fitted estimator.
|
94
95
|
"""
|
95
|
-
X =
|
96
|
+
X = skv.validate_data(self, X)
|
96
97
|
if self.window_size is not None:
|
97
98
|
X = X[-int(self.window_size) :]
|
98
99
|
covariance = np.cov(X.T, ddof=self.ddof)
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
import numpy.typing as npt
|
11
11
|
import pandas as pd
|
12
|
+
import sklearn.utils.validation as skv
|
12
13
|
|
13
14
|
from skfolio.moments.covariance._base import BaseCovariance
|
14
15
|
|
@@ -94,7 +95,7 @@ class EWCovariance(BaseCovariance):
|
|
94
95
|
self : EWCovariance
|
95
96
|
Fitted estimator.
|
96
97
|
"""
|
97
|
-
X =
|
98
|
+
X = skv.validate_data(self, X)
|
98
99
|
if self.window_size is not None:
|
99
100
|
X = X[-int(self.window_size) :]
|
100
101
|
n_observations = X.shape[0]
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
import numpy as np
|
11
11
|
import numpy.typing as npt
|
12
|
+
import sklearn.utils.validation as skv
|
12
13
|
|
13
14
|
from skfolio.moments.covariance._base import BaseCovariance
|
14
15
|
from skfolio.utils.stats import corr_to_cov
|
@@ -131,7 +132,7 @@ class GerberCovariance(BaseCovariance):
|
|
131
132
|
self : GerberCovariance
|
132
133
|
Fitted estimator.
|
133
134
|
"""
|
134
|
-
X =
|
135
|
+
X = skv.validate_data(self, X)
|
135
136
|
if self.window_size is not None:
|
136
137
|
X = X[-self.window_size :]
|
137
138
|
if not (1 > self.threshold > 0):
|
@@ -272,7 +272,7 @@ class ImpliedCovariance(BaseCovariance):
|
|
272
272
|
# and re-order to follow returns ordering.
|
273
273
|
implied_vol = safe_indexing(implied_vol, indices=indices, axis=1)
|
274
274
|
|
275
|
-
X =
|
275
|
+
X = skv.validate_data(self, X)
|
276
276
|
_, n_assets = X.shape
|
277
277
|
implied_vol = check_implied_vol(implied_vol=implied_vol, X=X)
|
278
278
|
implied_vol /= np.sqrt(self.annualized_factor)
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
import numpy as np
|
11
11
|
import numpy.typing as npt
|
12
|
+
import sklearn.utils.validation as skv
|
12
13
|
|
13
14
|
from skfolio.moments.expected_returns._base import BaseMu
|
14
15
|
|
@@ -56,7 +57,7 @@ class EmpiricalMu(BaseMu):
|
|
56
57
|
self : EmpiricalMu
|
57
58
|
Fitted estimator.
|
58
59
|
"""
|
59
|
-
X =
|
60
|
+
X = skv.validate_data(self, X)
|
60
61
|
if self.window_size is not None:
|
61
62
|
X = X[-self.window_size :]
|
62
63
|
self.mu_ = np.mean(X, axis=0)
|
@@ -10,6 +10,7 @@
|
|
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
|
from skfolio.moments.covariance import BaseCovariance, EmpiricalCovariance
|
15
16
|
from skfolio.moments.expected_returns._base import BaseMu
|
@@ -114,7 +115,7 @@ class EquilibriumMu(BaseMu):
|
|
114
115
|
|
115
116
|
# we validate and convert to numpy after all models have been fitted to keep
|
116
117
|
# features names information.
|
117
|
-
X =
|
118
|
+
X = skv.validate_data(self, X)
|
118
119
|
n_assets = X.shape[1]
|
119
120
|
if self.weights is None:
|
120
121
|
weights = np.ones(n_assets) / n_assets
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
import numpy.typing as npt
|
11
11
|
import pandas as pd
|
12
|
+
import sklearn.utils.validation as skv
|
12
13
|
|
13
14
|
from skfolio.moments.expected_returns._base import BaseMu
|
14
15
|
|
@@ -62,7 +63,7 @@ class EWMu(BaseMu):
|
|
62
63
|
self : EWMu
|
63
64
|
Fitted estimator.
|
64
65
|
"""
|
65
|
-
X =
|
66
|
+
X = skv.validate_data(self, X)
|
66
67
|
if self.window_size is not None:
|
67
68
|
X = X[-self.window_size :]
|
68
69
|
self.mu_ = pd.DataFrame(X).ewm(alpha=self.alpha).mean().iloc[-1, :].to_numpy()
|
@@ -13,6 +13,7 @@ from enum import auto
|
|
13
13
|
import numpy as np
|
14
14
|
import numpy.typing as npt
|
15
15
|
import sklearn.utils.metadata_routing as skm
|
16
|
+
import sklearn.utils.validation as skv
|
16
17
|
|
17
18
|
from skfolio.moments.covariance import BaseCovariance, EmpiricalCovariance
|
18
19
|
from skfolio.moments.expected_returns._base import BaseMu
|
@@ -189,7 +190,7 @@ class ShrunkMu(BaseMu):
|
|
189
190
|
|
190
191
|
# we validate and convert to numpy after all models have been fitted to keep
|
191
192
|
# features names information.
|
192
|
-
X =
|
193
|
+
X = skv.validate_data(self, X)
|
193
194
|
n_observations, n_assets = X.shape
|
194
195
|
|
195
196
|
covariance = self.covariance_estimator_.covariance_
|
@@ -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
|
@@ -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 = []
|
@@ -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
|
@@ -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)
|
@@ -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
|
@@ -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)
|
@@ -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
|
@@ -8,6 +8,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
|
@@ -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(
|
@@ -6,6 +6,7 @@
|
|
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
|
@@ -423,7 +424,8 @@ 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"""
|
@@ -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
|
@@ -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
|
)
|
@@ -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
|
@@ -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`")
|
@@ -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(
|
@@ -6,6 +6,7 @@
|
|
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
|
@@ -10,4 +10,4 @@ from skfolio.portfolio._base import BasePortfolio
|
|
10
10
|
from skfolio.portfolio._multi_period_portfolio import MultiPeriodPortfolio
|
11
11
|
from skfolio.portfolio._portfolio import Portfolio
|
12
12
|
|
13
|
-
__all__ = ["BasePortfolio", "
|
13
|
+
__all__ = ["BasePortfolio", "MultiPeriodPortfolio", "Portfolio"]
|
@@ -74,7 +74,7 @@ class DropCorrelated(skf.SelectorMixin, skb.BaseEstimator):
|
|
74
74
|
self : DropCorrelated
|
75
75
|
Fitted estimator.
|
76
76
|
"""
|
77
|
-
X =
|
77
|
+
X = skv.validate_data(self, X)
|
78
78
|
if not -1 <= self.threshold <= 1:
|
79
79
|
raise ValueError("`threshold` must be between -1 and 1")
|
80
80
|
|
@@ -97,7 +97,7 @@ class SelectComplete(skf.SelectorMixin, skb.BaseEstimator):
|
|
97
97
|
Fitted estimator.
|
98
98
|
"""
|
99
99
|
# Validate by allowing NaNs
|
100
|
-
X =
|
100
|
+
X = skv.validate_data(self, X, ensure_all_finite="allow-nan")
|
101
101
|
|
102
102
|
if self.drop_assets_with_internal_nan:
|
103
103
|
# Identify columns with any NaNs
|
@@ -108,9 +108,11 @@ class SelectComplete(skf.SelectorMixin, skb.BaseEstimator):
|
|
108
108
|
|
109
109
|
return self
|
110
110
|
|
111
|
-
def _get_support_mask(self):
|
111
|
+
def _get_support_mask(self) -> np.ndarray:
|
112
112
|
skv.check_is_fitted(self)
|
113
113
|
return self.to_keep_
|
114
114
|
|
115
|
-
def
|
116
|
-
|
115
|
+
def __sklearn_tags__(self):
|
116
|
+
tags = super().__sklearn_tags__()
|
117
|
+
tags.input_tags.allow_nan = True
|
118
|
+
return tags
|
@@ -76,7 +76,7 @@ class SelectKExtremes(skf.SelectorMixin, skb.BaseEstimator):
|
|
76
76
|
self : SelectKExtremes
|
77
77
|
Fitted estimator.
|
78
78
|
"""
|
79
|
-
X =
|
79
|
+
X = skv.validate_data(self, X)
|
80
80
|
k = int(self.k)
|
81
81
|
if k <= 0:
|
82
82
|
raise ValueError("`k` must be strictly positive")
|