skfolio 0.7.0__tar.gz → 0.8.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.7.0/src/skfolio.egg-info → skfolio-0.8.0}/PKG-INFO +99 -24
- {skfolio-0.7.0 → skfolio-0.8.0}/README.rst +87 -14
- {skfolio-0.7.0 → skfolio-0.8.0}/pyproject.toml +18 -10
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/__init__.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/cluster/__init__.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/cluster/_hierarchical.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/datasets/__init__.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/datasets/_base.py +2 -2
- skfolio-0.8.0/src/skfolio/datasets/data/__init__.py +1 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/distance/__init__.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/distance/_base.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/distance/_distance.py +4 -4
- skfolio-0.8.0/src/skfolio/distribution/__init__.py +56 -0
- skfolio-0.8.0/src/skfolio/distribution/_base.py +203 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/__init__.py +35 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_base.py +456 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_clayton.py +539 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_gaussian.py +407 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_gumbel.py +560 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_independent.py +196 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_joe.py +609 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_selection.py +111 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_student_t.py +486 -0
- skfolio-0.8.0/src/skfolio/distribution/copula/_utils.py +509 -0
- skfolio-0.8.0/src/skfolio/distribution/multivariate/__init__.py +11 -0
- skfolio-0.8.0/src/skfolio/distribution/multivariate/_base.py +241 -0
- skfolio-0.8.0/src/skfolio/distribution/multivariate/_utils.py +632 -0
- skfolio-0.8.0/src/skfolio/distribution/multivariate/_vine_copula.py +1254 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/__init__.py +19 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/_base.py +308 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/_gaussian.py +136 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/_johnson_su.py +152 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/_normal_inverse_gaussian.py +153 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/_selection.py +85 -0
- skfolio-0.8.0/src/skfolio/distribution/univariate/_student_t.py +144 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/exceptions.py +6 -6
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/measures/__init__.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/measures/_enums.py +7 -7
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/measures/_measures.py +4 -7
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/metrics/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/metrics/_scorer.py +4 -4
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/model_selection/__init__.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/model_selection/_combinatorial.py +15 -12
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/model_selection/_validation.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/model_selection/_walk_forward.py +3 -3
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_base.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_denoise_covariance.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_detone_covariance.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_empirical_covariance.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_ew_covariance.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_gerber_covariance.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_graphical_lasso_cv.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_implied_covariance.py +2 -7
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_ledoit_wolf.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_oas.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/_shrunk_covariance.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/expected_returns/_base.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/expected_returns/_empirical_mu.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/expected_returns/_equilibrium_mu.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/expected_returns/_ew_mu.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/expected_returns/_shrunk_mu.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/_base.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/cluster/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/cluster/_nco.py +7 -7
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/cluster/hierarchical/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/cluster/hierarchical/_base.py +1 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/cluster/hierarchical/_herc.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/cluster/hierarchical/_hrp.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/convex/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/convex/_base.py +8 -8
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/convex/_distributionally_robust.py +4 -4
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/convex/_maximum_diversification.py +5 -5
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/convex/_mean_risk.py +5 -6
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/convex/_risk_budgeting.py +3 -3
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/ensemble/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/ensemble/_base.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/ensemble/_stacking.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/naive/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/optimization/naive/_naive.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/population/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/population/_population.py +34 -7
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/portfolio/_base.py +42 -8
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/portfolio/_multi_period_portfolio.py +3 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/portfolio/_portfolio.py +4 -4
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/pre_selection/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/pre_selection/_drop_correlated.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/pre_selection/_select_complete.py +25 -26
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/pre_selection/_select_k_extremes.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/pre_selection/_select_non_dominated.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/pre_selection/_select_non_expiring.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/preprocessing/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/preprocessing/_returns.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/prior/__init__.py +4 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/prior/_base.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/prior/_black_litterman.py +5 -3
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/prior/_empirical.py +3 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/prior/_factor_model.py +8 -4
- skfolio-0.8.0/src/skfolio/prior/_synthetic_data.py +239 -0
- skfolio-0.8.0/src/skfolio/synthetic_returns/__init__.py +1 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/typing.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/uncertainty_set/__init__.py +2 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/uncertainty_set/_base.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/uncertainty_set/_bootstrap.py +1 -1
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/uncertainty_set/_empirical.py +1 -1
- skfolio-0.8.0/src/skfolio/utils/__init__.py +1 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/utils/bootstrap.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/utils/equations.py +13 -10
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/utils/sorting.py +2 -2
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/utils/stats.py +7 -7
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/utils/tools.py +76 -12
- {skfolio-0.7.0 → skfolio-0.8.0/src/skfolio.egg-info}/PKG-INFO +99 -24
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio.egg-info/SOURCES.txt +25 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio.egg-info/requires.txt +9 -8
- skfolio-0.7.0/src/skfolio/datasets/data/__init__.py +0 -0
- skfolio-0.7.0/src/skfolio/utils/__init__.py +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/LICENSE +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/MANIFEST.in +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/setup.cfg +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/datasets/data/factors_dataset.csv.gz +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/datasets/data/sp500_dataset.csv.gz +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/datasets/data/sp500_index.csv.gz +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/__init__.py +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/covariance/__init__.py +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/moments/expected_returns/__init__.py +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio/portfolio/__init__.py +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio.egg-info/dependency_links.txt +0 -0
- {skfolio-0.7.0 → skfolio-0.8.0}/src/skfolio.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: skfolio
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.8.0
|
4
4
|
Summary: Portfolio optimization built on top of scikit-learn
|
5
5
|
Author-email: Hugo Delatte <delatte.hugo@gmail.com>
|
6
6
|
Maintainer-email: Hugo Delatte <delatte.hugo@gmail.com>, Matteo Manzi <matteomanzi09@gmail.com>
|
@@ -57,20 +57,21 @@ Requires-Python: >=3.10
|
|
57
57
|
Description-Content-Type: text/x-rst
|
58
58
|
License-File: LICENSE
|
59
59
|
Requires-Dist: numpy>=1.23.4
|
60
|
-
Requires-Dist: scipy>=1.
|
60
|
+
Requires-Dist: scipy>=1.15.2
|
61
61
|
Requires-Dist: pandas>=1.4.1
|
62
|
-
Requires-Dist: cvxpy>=1.
|
62
|
+
Requires-Dist: cvxpy-base>=1.5.0
|
63
|
+
Requires-Dist: clarabel>=0.9.0
|
63
64
|
Requires-Dist: scikit-learn>=1.6.0
|
64
65
|
Requires-Dist: joblib>=1.3.2
|
65
66
|
Requires-Dist: plotly>=5.22.0
|
66
67
|
Provides-Extra: dev
|
67
|
-
Requires-Dist:
|
68
|
-
Requires-Dist: pytest
|
69
|
-
Requires-Dist: pytest-cov
|
70
|
-
Requires-Dist: ruff
|
71
|
-
Requires-Dist: pre-commit
|
68
|
+
Requires-Dist: PySCIPOpt; extra == "dev"
|
69
|
+
Requires-Dist: pytest; extra == "dev"
|
70
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
71
|
+
Requires-Dist: ruff; extra == "dev"
|
72
|
+
Requires-Dist: pre-commit; extra == "dev"
|
72
73
|
Provides-Extra: docs
|
73
|
-
Requires-Dist:
|
74
|
+
Requires-Dist: PySCIPOpt; extra == "docs"
|
74
75
|
Requires-Dist: Sphinx; extra == "docs"
|
75
76
|
Requires-Dist: sphinx-gallery; extra == "docs"
|
76
77
|
Requires-Dist: sphinx-design; extra == "docs"
|
@@ -87,6 +88,7 @@ Requires-Dist: sphinx-sitemap; extra == "docs"
|
|
87
88
|
Requires-Dist: jupyterlite-sphinx; extra == "docs"
|
88
89
|
Requires-Dist: jupyterlite-pyodide-kernel; extra == "docs"
|
89
90
|
Requires-Dist: nbformat; extra == "docs"
|
91
|
+
Dynamic: license-file
|
90
92
|
|
91
93
|
.. -*- mode: rst -*-
|
92
94
|
|
@@ -129,8 +131,9 @@ Requires-Dist: nbformat; extra == "docs"
|
|
129
131
|
.. |NumpyMinVersion| replace:: 1.23.4
|
130
132
|
.. |ScipyMinVersion| replace:: 1.8.0
|
131
133
|
.. |PandasMinVersion| replace:: 1.4.1
|
132
|
-
.. |
|
133
|
-
.. |
|
134
|
+
.. |CvxpyBaseMinVersion| replace:: 1.5.0
|
135
|
+
.. |ClarabelMinVersion| replace:: 0.9.0
|
136
|
+
.. |SklearnMinVersion| replace:: 1.6.0
|
134
137
|
.. |JoblibMinVersion| replace:: 1.3.2
|
135
138
|
.. |PlotlyMinVersion| replace:: 5.22.0
|
136
139
|
|
@@ -148,7 +151,7 @@ Requires-Dist: nbformat; extra == "docs"
|
|
148
151
|
It offers a unified interface and tools compatible with scikit-learn to build, fine-tune,
|
149
152
|
and cross-validate portfolio models.
|
150
153
|
|
151
|
-
It is distributed under the open
|
154
|
+
It is distributed under the open-source 3-Clause BSD license.
|
152
155
|
|
153
156
|
.. image:: https://raw.githubusercontent.com/skfolio/skfolio/master/docs/_static/expo.jpg
|
154
157
|
:target: https://skfolio.org/auto_examples/
|
@@ -180,7 +183,8 @@ Dependencies
|
|
180
183
|
- numpy (>= |NumpyMinVersion|)
|
181
184
|
- scipy (>= |ScipyMinVersion|)
|
182
185
|
- pandas (>= |PandasMinVersion|)
|
183
|
-
- cvxpy (>= |
|
186
|
+
- cvxpy-base (>= |CvxpyBaseMinVersion|)
|
187
|
+
- clarabel (>= |ClarabelMinVersion|)
|
184
188
|
- scikit-learn (>= |SklearnMinVersion|)
|
185
189
|
- joblib (>= |JoblibMinVersion|)
|
186
190
|
- plotly (>= |PlotlyMinVersion|)
|
@@ -194,7 +198,7 @@ Unfortunately, it faces a number of shortcomings, including high sensitivity to
|
|
194
198
|
input parameters (expected returns and covariance), weight concentration, high turnover,
|
195
199
|
and poor out-of-sample performance.
|
196
200
|
|
197
|
-
It is well
|
201
|
+
It is well-known that naive allocation (1/N, inverse-vol, etc.) tends to outperform
|
198
202
|
MVO out-of-sample (DeMiguel, 2007).
|
199
203
|
|
200
204
|
Numerous approaches have been developed to alleviate these shortcomings (shrinkage,
|
@@ -203,10 +207,10 @@ approaches, coherent risk measures, left-tail risk optimization, distributionall
|
|
203
207
|
optimization, factor model, risk-parity, hierarchical clustering, ensemble methods,
|
204
208
|
pre-selection, etc.).
|
205
209
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
+
Given the large number of methods, and the fact that they can be combined, there is a
|
211
|
+
need for a unified framework with a machine-learning approach to perform model
|
212
|
+
selection, validation, and parameter tuning while mitigating the risk of data leakage
|
213
|
+
and overfitting.
|
210
214
|
|
211
215
|
This framework is built on scikit-learn's API.
|
212
216
|
|
@@ -256,10 +260,27 @@ Available models
|
|
256
260
|
* Distance Correlation
|
257
261
|
* Variation of Information
|
258
262
|
|
263
|
+
* Distribution Estimator:
|
264
|
+
* Univariate:
|
265
|
+
* Gaussian
|
266
|
+
* Student's t
|
267
|
+
* Johnson Su
|
268
|
+
* Normal Inverse Gaussian
|
269
|
+
* Bivariate Copula
|
270
|
+
* Gaussian Copula
|
271
|
+
* Student's t Copula
|
272
|
+
* Clayton Copula
|
273
|
+
* Gumbel Copula
|
274
|
+
* Joe Copula
|
275
|
+
* Independent Copula
|
276
|
+
* Multivariate
|
277
|
+
* Vine Copula (Regular, Centered, Clustered, Conditional Sampling)
|
278
|
+
|
259
279
|
* Prior Estimator:
|
260
280
|
* Empirical
|
261
281
|
* Black & Litterman
|
262
282
|
* Factor Model
|
283
|
+
* Synthetic Data (Stress Test, Factor Stress Test)
|
263
284
|
|
264
285
|
* Uncertainty Set Estimator:
|
265
286
|
* On Expected Returns:
|
@@ -267,12 +288,14 @@ Available models
|
|
267
288
|
* Circular Bootstrap
|
268
289
|
* On Covariance:
|
269
290
|
* Empirical
|
270
|
-
* Circular
|
291
|
+
* Circular Bootstrap
|
271
292
|
|
272
293
|
* Pre-Selection Transformer:
|
273
294
|
* Non-Dominated Selection
|
274
295
|
* Select K Extremes (Best or Worst)
|
275
296
|
* Drop Highly Correlated Assets
|
297
|
+
* Select Non-Expiring Assets
|
298
|
+
* Select Complete Assets (handle late inception, delisting, etc.)
|
276
299
|
|
277
300
|
* Cross-Validation and Model Selection:
|
278
301
|
* Compatible with all `sklearn` methods (KFold, etc.)
|
@@ -317,6 +340,8 @@ Available models
|
|
317
340
|
* Budget Constraints
|
318
341
|
* Tracking Error Constraints
|
319
342
|
* Turnover Constraints
|
343
|
+
* Cardinality and Group Cardinality Constraints
|
344
|
+
* Threshold (Long and Short) Constraints
|
320
345
|
|
321
346
|
Quickstart
|
322
347
|
~~~~~~~~~~
|
@@ -339,6 +364,7 @@ Imports
|
|
339
364
|
|
340
365
|
from skfolio import RatioMeasure, RiskMeasure
|
341
366
|
from skfolio.datasets import load_factors_dataset, load_sp500_dataset
|
367
|
+
from skfolio.distribution import VineCopula
|
342
368
|
from skfolio.model_selection import (
|
343
369
|
CombinatorialPurgedCV,
|
344
370
|
WalkForward,
|
@@ -359,7 +385,7 @@ Imports
|
|
359
385
|
)
|
360
386
|
from skfolio.pre_selection import SelectKExtremes
|
361
387
|
from skfolio.preprocessing import prices_to_returns
|
362
|
-
from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel
|
388
|
+
from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel, SyntheticData
|
363
389
|
from skfolio.uncertainty_set import BootstrapMuUncertaintySet
|
364
390
|
|
365
391
|
Load Dataset
|
@@ -630,12 +656,61 @@ Combinatorial Purged Cross-Validation
|
|
630
656
|
print(population.summary())
|
631
657
|
|
632
658
|
|
659
|
+
Minimum CVaR Optimization on Synthetic Returns
|
660
|
+
----------------------------------------------
|
661
|
+
.. code-block:: python
|
662
|
+
|
663
|
+
vine = VineCopula(log_transform=True, n_jobs=-1)
|
664
|
+
prior = =SyntheticData(distribution_estimator=vine, n_samples=2000)
|
665
|
+
model = MeanRisk(risk_measure=RiskMeasure.CVAR, prior_estimator=prior)
|
666
|
+
model.fit(X)
|
667
|
+
print(model.weights_)
|
668
|
+
|
669
|
+
|
670
|
+
Stress Test
|
671
|
+
-----------
|
672
|
+
.. code-block:: python
|
673
|
+
|
674
|
+
vine = VineCopula(log_transform=True, central_assets=["BAC"] n_jobs=-1)
|
675
|
+
vine.fit(X)
|
676
|
+
X_stressed = vine.sample(n_samples=10_000, conditioning = {"BAC": -0.2})
|
677
|
+
ptf_stressed = model.predict(X_stressed)
|
678
|
+
|
679
|
+
|
680
|
+
Minimum CVaR Optimization on Synthetic Factors
|
681
|
+
----------------------------------------------
|
682
|
+
.. code-block:: python
|
683
|
+
|
684
|
+
vine = VineCopula(central_assets=["QUAL"], log_transform=True, n_jobs=-1)
|
685
|
+
factor_prior = SyntheticData(
|
686
|
+
distribution_estimator=vine,
|
687
|
+
n_samples=10_000,
|
688
|
+
sample_args=dict(conditioning={"QUAL": -0.2}),
|
689
|
+
)
|
690
|
+
factor_model = FactorModel(factor_prior_estimator=factor_prior)
|
691
|
+
model = MeanRisk(risk_measure=RiskMeasure.CVAR, prior_estimator=factor_model)
|
692
|
+
model.fit(X, y)
|
693
|
+
print(model.weights_)
|
694
|
+
|
695
|
+
|
696
|
+
Factor Stress Test
|
697
|
+
------------------
|
698
|
+
.. code-block:: python
|
699
|
+
|
700
|
+
factor_model.set_params(factor_prior_estimator__sample_args=dict(
|
701
|
+
conditioning={"QUAL": -0.5}
|
702
|
+
))
|
703
|
+
factor_model.fit(X,y)
|
704
|
+
stressed_X = factor_model.prior_model_.returns
|
705
|
+
stressed_ptf = model.predict(stressed_X)
|
706
|
+
|
707
|
+
|
633
708
|
Recognition
|
634
709
|
~~~~~~~~~~~
|
635
710
|
|
636
|
-
We would like to thank all contributors
|
637
|
-
scikit-learn and cvxpy,
|
638
|
-
|
711
|
+
We would like to thank all contributors to our direct dependencies, such as
|
712
|
+
scikit-learn and cvxpy, as well as the contributors of the following resources that
|
713
|
+
served as sources of inspiration:
|
639
714
|
|
640
715
|
* PyPortfolioOpt
|
641
716
|
* Riskfolio-Lib
|
@@ -39,8 +39,9 @@
|
|
39
39
|
.. |NumpyMinVersion| replace:: 1.23.4
|
40
40
|
.. |ScipyMinVersion| replace:: 1.8.0
|
41
41
|
.. |PandasMinVersion| replace:: 1.4.1
|
42
|
-
.. |
|
43
|
-
.. |
|
42
|
+
.. |CvxpyBaseMinVersion| replace:: 1.5.0
|
43
|
+
.. |ClarabelMinVersion| replace:: 0.9.0
|
44
|
+
.. |SklearnMinVersion| replace:: 1.6.0
|
44
45
|
.. |JoblibMinVersion| replace:: 1.3.2
|
45
46
|
.. |PlotlyMinVersion| replace:: 5.22.0
|
46
47
|
|
@@ -58,7 +59,7 @@
|
|
58
59
|
It offers a unified interface and tools compatible with scikit-learn to build, fine-tune,
|
59
60
|
and cross-validate portfolio models.
|
60
61
|
|
61
|
-
It is distributed under the open
|
62
|
+
It is distributed under the open-source 3-Clause BSD license.
|
62
63
|
|
63
64
|
.. image:: https://raw.githubusercontent.com/skfolio/skfolio/master/docs/_static/expo.jpg
|
64
65
|
:target: https://skfolio.org/auto_examples/
|
@@ -90,7 +91,8 @@ Dependencies
|
|
90
91
|
- numpy (>= |NumpyMinVersion|)
|
91
92
|
- scipy (>= |ScipyMinVersion|)
|
92
93
|
- pandas (>= |PandasMinVersion|)
|
93
|
-
- cvxpy (>= |
|
94
|
+
- cvxpy-base (>= |CvxpyBaseMinVersion|)
|
95
|
+
- clarabel (>= |ClarabelMinVersion|)
|
94
96
|
- scikit-learn (>= |SklearnMinVersion|)
|
95
97
|
- joblib (>= |JoblibMinVersion|)
|
96
98
|
- plotly (>= |PlotlyMinVersion|)
|
@@ -104,7 +106,7 @@ Unfortunately, it faces a number of shortcomings, including high sensitivity to
|
|
104
106
|
input parameters (expected returns and covariance), weight concentration, high turnover,
|
105
107
|
and poor out-of-sample performance.
|
106
108
|
|
107
|
-
It is well
|
109
|
+
It is well-known that naive allocation (1/N, inverse-vol, etc.) tends to outperform
|
108
110
|
MVO out-of-sample (DeMiguel, 2007).
|
109
111
|
|
110
112
|
Numerous approaches have been developed to alleviate these shortcomings (shrinkage,
|
@@ -113,10 +115,10 @@ approaches, coherent risk measures, left-tail risk optimization, distributionall
|
|
113
115
|
optimization, factor model, risk-parity, hierarchical clustering, ensemble methods,
|
114
116
|
pre-selection, etc.).
|
115
117
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
Given the large number of methods, and the fact that they can be combined, there is a
|
119
|
+
need for a unified framework with a machine-learning approach to perform model
|
120
|
+
selection, validation, and parameter tuning while mitigating the risk of data leakage
|
121
|
+
and overfitting.
|
120
122
|
|
121
123
|
This framework is built on scikit-learn's API.
|
122
124
|
|
@@ -166,10 +168,27 @@ Available models
|
|
166
168
|
* Distance Correlation
|
167
169
|
* Variation of Information
|
168
170
|
|
171
|
+
* Distribution Estimator:
|
172
|
+
* Univariate:
|
173
|
+
* Gaussian
|
174
|
+
* Student's t
|
175
|
+
* Johnson Su
|
176
|
+
* Normal Inverse Gaussian
|
177
|
+
* Bivariate Copula
|
178
|
+
* Gaussian Copula
|
179
|
+
* Student's t Copula
|
180
|
+
* Clayton Copula
|
181
|
+
* Gumbel Copula
|
182
|
+
* Joe Copula
|
183
|
+
* Independent Copula
|
184
|
+
* Multivariate
|
185
|
+
* Vine Copula (Regular, Centered, Clustered, Conditional Sampling)
|
186
|
+
|
169
187
|
* Prior Estimator:
|
170
188
|
* Empirical
|
171
189
|
* Black & Litterman
|
172
190
|
* Factor Model
|
191
|
+
* Synthetic Data (Stress Test, Factor Stress Test)
|
173
192
|
|
174
193
|
* Uncertainty Set Estimator:
|
175
194
|
* On Expected Returns:
|
@@ -177,12 +196,14 @@ Available models
|
|
177
196
|
* Circular Bootstrap
|
178
197
|
* On Covariance:
|
179
198
|
* Empirical
|
180
|
-
* Circular
|
199
|
+
* Circular Bootstrap
|
181
200
|
|
182
201
|
* Pre-Selection Transformer:
|
183
202
|
* Non-Dominated Selection
|
184
203
|
* Select K Extremes (Best or Worst)
|
185
204
|
* Drop Highly Correlated Assets
|
205
|
+
* Select Non-Expiring Assets
|
206
|
+
* Select Complete Assets (handle late inception, delisting, etc.)
|
186
207
|
|
187
208
|
* Cross-Validation and Model Selection:
|
188
209
|
* Compatible with all `sklearn` methods (KFold, etc.)
|
@@ -227,6 +248,8 @@ Available models
|
|
227
248
|
* Budget Constraints
|
228
249
|
* Tracking Error Constraints
|
229
250
|
* Turnover Constraints
|
251
|
+
* Cardinality and Group Cardinality Constraints
|
252
|
+
* Threshold (Long and Short) Constraints
|
230
253
|
|
231
254
|
Quickstart
|
232
255
|
~~~~~~~~~~
|
@@ -249,6 +272,7 @@ Imports
|
|
249
272
|
|
250
273
|
from skfolio import RatioMeasure, RiskMeasure
|
251
274
|
from skfolio.datasets import load_factors_dataset, load_sp500_dataset
|
275
|
+
from skfolio.distribution import VineCopula
|
252
276
|
from skfolio.model_selection import (
|
253
277
|
CombinatorialPurgedCV,
|
254
278
|
WalkForward,
|
@@ -269,7 +293,7 @@ Imports
|
|
269
293
|
)
|
270
294
|
from skfolio.pre_selection import SelectKExtremes
|
271
295
|
from skfolio.preprocessing import prices_to_returns
|
272
|
-
from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel
|
296
|
+
from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel, SyntheticData
|
273
297
|
from skfolio.uncertainty_set import BootstrapMuUncertaintySet
|
274
298
|
|
275
299
|
Load Dataset
|
@@ -540,12 +564,61 @@ Combinatorial Purged Cross-Validation
|
|
540
564
|
print(population.summary())
|
541
565
|
|
542
566
|
|
567
|
+
Minimum CVaR Optimization on Synthetic Returns
|
568
|
+
----------------------------------------------
|
569
|
+
.. code-block:: python
|
570
|
+
|
571
|
+
vine = VineCopula(log_transform=True, n_jobs=-1)
|
572
|
+
prior = =SyntheticData(distribution_estimator=vine, n_samples=2000)
|
573
|
+
model = MeanRisk(risk_measure=RiskMeasure.CVAR, prior_estimator=prior)
|
574
|
+
model.fit(X)
|
575
|
+
print(model.weights_)
|
576
|
+
|
577
|
+
|
578
|
+
Stress Test
|
579
|
+
-----------
|
580
|
+
.. code-block:: python
|
581
|
+
|
582
|
+
vine = VineCopula(log_transform=True, central_assets=["BAC"] n_jobs=-1)
|
583
|
+
vine.fit(X)
|
584
|
+
X_stressed = vine.sample(n_samples=10_000, conditioning = {"BAC": -0.2})
|
585
|
+
ptf_stressed = model.predict(X_stressed)
|
586
|
+
|
587
|
+
|
588
|
+
Minimum CVaR Optimization on Synthetic Factors
|
589
|
+
----------------------------------------------
|
590
|
+
.. code-block:: python
|
591
|
+
|
592
|
+
vine = VineCopula(central_assets=["QUAL"], log_transform=True, n_jobs=-1)
|
593
|
+
factor_prior = SyntheticData(
|
594
|
+
distribution_estimator=vine,
|
595
|
+
n_samples=10_000,
|
596
|
+
sample_args=dict(conditioning={"QUAL": -0.2}),
|
597
|
+
)
|
598
|
+
factor_model = FactorModel(factor_prior_estimator=factor_prior)
|
599
|
+
model = MeanRisk(risk_measure=RiskMeasure.CVAR, prior_estimator=factor_model)
|
600
|
+
model.fit(X, y)
|
601
|
+
print(model.weights_)
|
602
|
+
|
603
|
+
|
604
|
+
Factor Stress Test
|
605
|
+
------------------
|
606
|
+
.. code-block:: python
|
607
|
+
|
608
|
+
factor_model.set_params(factor_prior_estimator__sample_args=dict(
|
609
|
+
conditioning={"QUAL": -0.5}
|
610
|
+
))
|
611
|
+
factor_model.fit(X,y)
|
612
|
+
stressed_X = factor_model.prior_model_.returns
|
613
|
+
stressed_ptf = model.predict(stressed_X)
|
614
|
+
|
615
|
+
|
543
616
|
Recognition
|
544
617
|
~~~~~~~~~~~
|
545
618
|
|
546
|
-
We would like to thank all contributors
|
547
|
-
scikit-learn and cvxpy,
|
548
|
-
|
619
|
+
We would like to thank all contributors to our direct dependencies, such as
|
620
|
+
scikit-learn and cvxpy, as well as the contributors of the following resources that
|
621
|
+
served as sources of inspiration:
|
549
622
|
|
550
623
|
* PyPortfolioOpt
|
551
624
|
* Riskfolio-Lib
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "skfolio"
|
7
|
-
version = "0.
|
7
|
+
version = "0.8.0"
|
8
8
|
maintainers = [
|
9
9
|
{ name = "Hugo Delatte", email = "delatte.hugo@gmail.com" },
|
10
10
|
{ name = "Matteo Manzi", email = "matteomanzi09@gmail.com" }
|
@@ -51,9 +51,10 @@ classifiers = [
|
|
51
51
|
requires-python = ">=3.10"
|
52
52
|
dependencies = [
|
53
53
|
"numpy>=1.23.4",
|
54
|
-
"scipy>=1.
|
54
|
+
"scipy>=1.15.2",
|
55
55
|
"pandas>=1.4.1",
|
56
|
-
"cvxpy>=1.
|
56
|
+
"cvxpy-base>=1.5.0",
|
57
|
+
"clarabel>=0.9.0",
|
57
58
|
"scikit-learn>=1.6.0",
|
58
59
|
"joblib>=1.3.2",
|
59
60
|
"plotly>=5.22.0"
|
@@ -67,14 +68,14 @@ Repository = "https://github.com/skfolio/skfolio"
|
|
67
68
|
|
68
69
|
[project.optional-dependencies]
|
69
70
|
dev = [
|
70
|
-
"
|
71
|
-
"pytest
|
72
|
-
"pytest-cov
|
73
|
-
"ruff
|
74
|
-
"pre-commit
|
71
|
+
"PySCIPOpt",
|
72
|
+
"pytest",
|
73
|
+
"pytest-cov",
|
74
|
+
"ruff",
|
75
|
+
"pre-commit",
|
75
76
|
]
|
76
77
|
docs = [
|
77
|
-
"
|
78
|
+
"PySCIPOpt",
|
78
79
|
"Sphinx",
|
79
80
|
"sphinx-gallery",
|
80
81
|
"sphinx-design",
|
@@ -129,8 +130,12 @@ select = [
|
|
129
130
|
"ISC", # implicit string concatenation
|
130
131
|
"UP", # alert you when better syntax is available in your python version
|
131
132
|
"RUF", # the ruff developer's own rules
|
133
|
+
"D",
|
132
134
|
]
|
133
|
-
ignore = ["E203", "ISC001", "ISC002", "E111", "E114", "E117"
|
135
|
+
ignore = ["E203", "ISC001", "ISC002", "E111", "E114", "E117", "A005", "D401", "D205"]
|
136
|
+
|
137
|
+
[tool.ruff.lint.per-file-ignores]
|
138
|
+
"tests/*" = ["D"]
|
134
139
|
|
135
140
|
[tool.ruff.lint.isort]
|
136
141
|
case-sensitive = true
|
@@ -138,6 +143,9 @@ case-sensitive = true
|
|
138
143
|
[tool.ruff.lint.pycodestyle]
|
139
144
|
max-line-length = 320
|
140
145
|
|
146
|
+
[tool.ruff.lint.pydocstyle]
|
147
|
+
convention = "numpy"
|
148
|
+
|
141
149
|
[tool.pytest.ini_options]
|
142
150
|
addopts = [
|
143
151
|
"--import-mode=importlib",
|
@@ -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-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
8
8
|
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
@@ -74,7 +74,7 @@ def load_gzip_compressed_csv_data(
|
|
74
74
|
encoding="utf-8",
|
75
75
|
datetime_index: bool = True,
|
76
76
|
) -> pd.DataFrame:
|
77
|
-
"""
|
77
|
+
"""Load gzip-compressed csv files with `importlib.resources`.
|
78
78
|
|
79
79
|
1) Open resource file with `importlib.resources.open_binary`
|
80
80
|
2) Decompress csv file with `gzip.open`
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Dataset Data module."""
|
@@ -1,8 +1,8 @@
|
|
1
|
-
"""Distance Estimators"""
|
1
|
+
"""Distance Estimators."""
|
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
|
@@ -215,7 +215,7 @@ class SpearmanDistance(BaseDistance):
|
|
215
215
|
self.power = power
|
216
216
|
|
217
217
|
def fit(self, X: npt.ArrayLike, y=None) -> "SpearmanDistance":
|
218
|
-
"""Fit the Spearman
|
218
|
+
"""Fit the Spearman estimator.
|
219
219
|
|
220
220
|
Parameters
|
221
221
|
----------
|
@@ -384,7 +384,7 @@ class DistanceCorrelation(BaseDistance):
|
|
384
384
|
|
385
385
|
@staticmethod
|
386
386
|
def _dcorr(x: np.ndarray, y: np.ndarray):
|
387
|
-
"""Calculate the distance correlation between two variables"""
|
387
|
+
"""Calculate the distance correlation between two variables."""
|
388
388
|
x = scd.squareform(scd.pdist(x.reshape(-1, 1)))
|
389
389
|
y = scd.squareform(scd.pdist(y.reshape(-1, 1)))
|
390
390
|
x = x - x.mean(axis=0)[np.newaxis, :] - x.mean(axis=1)[:, np.newaxis] + x.mean()
|
@@ -0,0 +1,56 @@
|
|
1
|
+
"""Distribution module."""
|
2
|
+
|
3
|
+
from skfolio.distribution._base import BaseDistribution, SelectionCriterion
|
4
|
+
from skfolio.distribution.copula import (
|
5
|
+
BaseBivariateCopula,
|
6
|
+
ClaytonCopula,
|
7
|
+
CopulaRotation,
|
8
|
+
GaussianCopula,
|
9
|
+
GumbelCopula,
|
10
|
+
IndependentCopula,
|
11
|
+
JoeCopula,
|
12
|
+
StudentTCopula,
|
13
|
+
compute_pseudo_observations,
|
14
|
+
empirical_tail_concentration,
|
15
|
+
plot_tail_concentration,
|
16
|
+
select_bivariate_copula,
|
17
|
+
)
|
18
|
+
from skfolio.distribution.multivariate import (
|
19
|
+
BaseMultivariateDist,
|
20
|
+
DependenceMethod,
|
21
|
+
VineCopula,
|
22
|
+
)
|
23
|
+
from skfolio.distribution.univariate import (
|
24
|
+
BaseUnivariateDist,
|
25
|
+
Gaussian,
|
26
|
+
JohnsonSU,
|
27
|
+
NormalInverseGaussian,
|
28
|
+
StudentT,
|
29
|
+
select_univariate_dist,
|
30
|
+
)
|
31
|
+
|
32
|
+
__all__ = [
|
33
|
+
"BaseBivariateCopula",
|
34
|
+
"BaseDistribution",
|
35
|
+
"BaseMultivariateDist",
|
36
|
+
"BaseUnivariateDist",
|
37
|
+
"ClaytonCopula",
|
38
|
+
"CopulaRotation",
|
39
|
+
"DependenceMethod",
|
40
|
+
"Gaussian",
|
41
|
+
"GaussianCopula",
|
42
|
+
"GumbelCopula",
|
43
|
+
"IndependentCopula",
|
44
|
+
"JoeCopula",
|
45
|
+
"JohnsonSU",
|
46
|
+
"NormalInverseGaussian",
|
47
|
+
"SelectionCriterion",
|
48
|
+
"StudentT",
|
49
|
+
"StudentTCopula",
|
50
|
+
"VineCopula",
|
51
|
+
"compute_pseudo_observations",
|
52
|
+
"empirical_tail_concentration",
|
53
|
+
"plot_tail_concentration",
|
54
|
+
"select_bivariate_copula",
|
55
|
+
"select_univariate_dist",
|
56
|
+
]
|