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.
Files changed (118) hide show
  1. skfolio/__init__.py +7 -7
  2. skfolio/cluster/__init__.py +2 -2
  3. skfolio/cluster/_hierarchical.py +2 -2
  4. skfolio/datasets/__init__.py +3 -3
  5. skfolio/datasets/_base.py +2 -2
  6. skfolio/datasets/data/__init__.py +1 -0
  7. skfolio/distance/__init__.py +4 -4
  8. skfolio/distance/_base.py +2 -2
  9. skfolio/distance/_distance.py +11 -10
  10. skfolio/distribution/__init__.py +56 -0
  11. skfolio/distribution/_base.py +203 -0
  12. skfolio/distribution/copula/__init__.py +35 -0
  13. skfolio/distribution/copula/_base.py +456 -0
  14. skfolio/distribution/copula/_clayton.py +539 -0
  15. skfolio/distribution/copula/_gaussian.py +407 -0
  16. skfolio/distribution/copula/_gumbel.py +560 -0
  17. skfolio/distribution/copula/_independent.py +196 -0
  18. skfolio/distribution/copula/_joe.py +609 -0
  19. skfolio/distribution/copula/_selection.py +111 -0
  20. skfolio/distribution/copula/_student_t.py +486 -0
  21. skfolio/distribution/copula/_utils.py +509 -0
  22. skfolio/distribution/multivariate/__init__.py +11 -0
  23. skfolio/distribution/multivariate/_base.py +241 -0
  24. skfolio/distribution/multivariate/_utils.py +632 -0
  25. skfolio/distribution/multivariate/_vine_copula.py +1254 -0
  26. skfolio/distribution/univariate/__init__.py +19 -0
  27. skfolio/distribution/univariate/_base.py +308 -0
  28. skfolio/distribution/univariate/_gaussian.py +136 -0
  29. skfolio/distribution/univariate/_johnson_su.py +152 -0
  30. skfolio/distribution/univariate/_normal_inverse_gaussian.py +153 -0
  31. skfolio/distribution/univariate/_selection.py +85 -0
  32. skfolio/distribution/univariate/_student_t.py +144 -0
  33. skfolio/exceptions.py +8 -8
  34. skfolio/measures/__init__.py +24 -24
  35. skfolio/measures/_enums.py +7 -7
  36. skfolio/measures/_measures.py +4 -7
  37. skfolio/metrics/__init__.py +2 -0
  38. skfolio/metrics/_scorer.py +4 -4
  39. skfolio/model_selection/__init__.py +4 -4
  40. skfolio/model_selection/_combinatorial.py +15 -12
  41. skfolio/model_selection/_validation.py +2 -2
  42. skfolio/model_selection/_walk_forward.py +3 -3
  43. skfolio/moments/__init__.py +11 -11
  44. skfolio/moments/covariance/__init__.py +6 -6
  45. skfolio/moments/covariance/_base.py +1 -1
  46. skfolio/moments/covariance/_denoise_covariance.py +3 -2
  47. skfolio/moments/covariance/_detone_covariance.py +3 -2
  48. skfolio/moments/covariance/_empirical_covariance.py +3 -2
  49. skfolio/moments/covariance/_ew_covariance.py +3 -2
  50. skfolio/moments/covariance/_gerber_covariance.py +3 -2
  51. skfolio/moments/covariance/_graphical_lasso_cv.py +1 -1
  52. skfolio/moments/covariance/_implied_covariance.py +3 -8
  53. skfolio/moments/covariance/_ledoit_wolf.py +1 -1
  54. skfolio/moments/covariance/_oas.py +1 -1
  55. skfolio/moments/covariance/_shrunk_covariance.py +1 -1
  56. skfolio/moments/expected_returns/__init__.py +2 -2
  57. skfolio/moments/expected_returns/_base.py +1 -1
  58. skfolio/moments/expected_returns/_empirical_mu.py +3 -2
  59. skfolio/moments/expected_returns/_equilibrium_mu.py +3 -2
  60. skfolio/moments/expected_returns/_ew_mu.py +3 -2
  61. skfolio/moments/expected_returns/_shrunk_mu.py +4 -3
  62. skfolio/optimization/__init__.py +12 -10
  63. skfolio/optimization/_base.py +2 -2
  64. skfolio/optimization/cluster/__init__.py +3 -1
  65. skfolio/optimization/cluster/_nco.py +10 -9
  66. skfolio/optimization/cluster/hierarchical/__init__.py +3 -1
  67. skfolio/optimization/cluster/hierarchical/_base.py +1 -2
  68. skfolio/optimization/cluster/hierarchical/_herc.py +4 -3
  69. skfolio/optimization/cluster/hierarchical/_hrp.py +4 -3
  70. skfolio/optimization/convex/__init__.py +5 -3
  71. skfolio/optimization/convex/_base.py +10 -9
  72. skfolio/optimization/convex/_distributionally_robust.py +8 -5
  73. skfolio/optimization/convex/_maximum_diversification.py +8 -6
  74. skfolio/optimization/convex/_mean_risk.py +10 -8
  75. skfolio/optimization/convex/_risk_budgeting.py +6 -4
  76. skfolio/optimization/ensemble/__init__.py +2 -0
  77. skfolio/optimization/ensemble/_base.py +2 -2
  78. skfolio/optimization/ensemble/_stacking.py +3 -3
  79. skfolio/optimization/naive/__init__.py +3 -1
  80. skfolio/optimization/naive/_naive.py +4 -3
  81. skfolio/population/__init__.py +2 -0
  82. skfolio/population/_population.py +34 -7
  83. skfolio/portfolio/__init__.py +1 -1
  84. skfolio/portfolio/_base.py +43 -8
  85. skfolio/portfolio/_multi_period_portfolio.py +3 -2
  86. skfolio/portfolio/_portfolio.py +5 -4
  87. skfolio/pre_selection/__init__.py +3 -1
  88. skfolio/pre_selection/_drop_correlated.py +3 -3
  89. skfolio/pre_selection/_select_complete.py +31 -30
  90. skfolio/pre_selection/_select_k_extremes.py +3 -3
  91. skfolio/pre_selection/_select_non_dominated.py +3 -3
  92. skfolio/pre_selection/_select_non_expiring.py +8 -6
  93. skfolio/preprocessing/__init__.py +2 -0
  94. skfolio/preprocessing/_returns.py +2 -2
  95. skfolio/prior/__init__.py +7 -3
  96. skfolio/prior/_base.py +2 -2
  97. skfolio/prior/_black_litterman.py +7 -4
  98. skfolio/prior/_empirical.py +5 -2
  99. skfolio/prior/_factor_model.py +10 -5
  100. skfolio/prior/_synthetic_data.py +239 -0
  101. skfolio/synthetic_returns/__init__.py +1 -0
  102. skfolio/typing.py +7 -7
  103. skfolio/uncertainty_set/__init__.py +7 -5
  104. skfolio/uncertainty_set/_base.py +5 -4
  105. skfolio/uncertainty_set/_bootstrap.py +1 -1
  106. skfolio/uncertainty_set/_empirical.py +1 -1
  107. skfolio/utils/__init__.py +1 -0
  108. skfolio/utils/bootstrap.py +2 -2
  109. skfolio/utils/equations.py +13 -10
  110. skfolio/utils/sorting.py +2 -2
  111. skfolio/utils/stats.py +15 -15
  112. skfolio/utils/tools.py +86 -22
  113. {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info}/METADATA +122 -46
  114. skfolio-0.8.0.dist-info/RECORD +120 -0
  115. {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info}/WHEEL +1 -1
  116. skfolio-0.6.0.dist-info/RECORD +0 -95
  117. {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info/licenses}/LICENSE +0 -0
  118. {skfolio-0.6.0.dist-info → skfolio-0.8.0.dist-info}/top_level.txt +0 -0
skfolio/utils/tools.py CHANGED
@@ -1,12 +1,13 @@
1
- """Tools module"""
1
+ """Tools module."""
2
2
 
3
3
  # Copyright (c) 2023
4
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
5
- # License: BSD 3 clause
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.
9
9
 
10
+ import warnings
10
11
  from collections.abc import Callable, Iterator
11
12
  from enum import Enum
12
13
  from functools import wraps
@@ -21,34 +22,35 @@ import sklearn.base as skb
21
22
 
22
23
  __all__ = [
23
24
  "AutoEnum",
24
- "cached_property_slots",
25
- "cache_method",
26
- "input_to_array",
27
25
  "args_names",
28
- "format_measure",
29
- "optimal_rounding_decimals",
30
26
  "bisection",
31
- "safe_split",
32
- "fit_single_estimator",
33
- "fit_and_predict",
34
- "safe_indexing",
27
+ "cache_method",
28
+ "cached_property_slots",
29
+ "check_estimator",
35
30
  "deduplicate_names",
36
31
  "default_asset_names",
37
- "check_estimator",
32
+ "fit_and_predict",
33
+ "fit_single_estimator",
34
+ "format_measure",
38
35
  "get_feature_names",
36
+ "input_to_array",
37
+ "optimal_rounding_decimals",
38
+ "safe_indexing",
39
+ "safe_split",
40
+ "validate_input_list",
39
41
  ]
40
42
 
41
43
  GenericAlias = type(list[int])
42
44
 
43
45
 
44
46
  class AutoEnum(str, Enum):
45
- """Base Enum class used in `skfolio`"""
47
+ """Base Enum class used in `skfolio`."""
46
48
 
47
49
  @staticmethod
48
50
  def _generate_next_value_(
49
51
  name: str, start: int, count: int, last_values: Any
50
52
  ) -> str:
51
- """Overriding `auto()`"""
53
+ """Overriding `auto()`."""
52
54
  return name.lower()
53
55
 
54
56
  @classmethod
@@ -68,13 +70,13 @@ class AutoEnum(str, Enum):
68
70
  return value in cls._value2member_map_
69
71
 
70
72
  def __repr__(self) -> str:
71
- """Representation of the Enum"""
73
+ """Representation of the Enum."""
72
74
  return self.name
73
75
 
74
76
 
75
77
  # noinspection PyPep8Naming
76
78
  class cached_property_slots:
77
- """Cached property decorator for slots"""
79
+ """Cached property decorator for slots."""
78
80
 
79
81
  def __init__(self, func):
80
82
  self.func = func
@@ -83,10 +85,12 @@ class cached_property_slots:
83
85
  self.__doc__ = func.__doc__
84
86
 
85
87
  def __set_name__(self, owner, name):
88
+ """Set Name."""
86
89
  self.public_name = name
87
90
  self.private_name = f"_{name}"
88
91
 
89
92
  def __get__(self, instance, owner=None):
93
+ """Getter."""
90
94
  if instance is None:
91
95
  return self
92
96
  if self.private_name is None:
@@ -102,6 +106,7 @@ class cached_property_slots:
102
106
  return value
103
107
 
104
108
  def __set__(self, instance, owner=None):
109
+ """Setter."""
105
110
  raise AttributeError(
106
111
  f"'{type(instance).__name__}' object attribute '{self.public_name}' is"
107
112
  " read-only"
@@ -111,7 +116,7 @@ class cached_property_slots:
111
116
 
112
117
 
113
118
  def _make_key(args, kwds) -> int:
114
- """Make a cache key from optionally typed positional and keyword arguments"""
119
+ """Make a cache key from optionally typed positional and keyword arguments."""
115
120
  key = args
116
121
  if kwds:
117
122
  for item in kwds.items():
@@ -248,7 +253,6 @@ def safe_split(
248
253
  y_subset : array-like
249
254
  Indexed targets.
250
255
  """
251
-
252
256
  X_subset = safe_indexing(X, indices=indices, axis=axis)
253
257
  if y is not None:
254
258
  y_subset = safe_indexing(y, indices=indices, axis=axis)
@@ -340,10 +344,9 @@ def check_estimator(
340
344
 
341
345
  Returns
342
346
  -------
343
- estimator: Estimator
347
+ estimator : Estimator
344
348
  The checked estimator or the default.
345
349
  """
346
-
347
350
  if estimator is None:
348
351
  return default
349
352
  if not isinstance(estimator, check_type):
@@ -439,6 +442,67 @@ def input_to_array(
439
442
  return arr
440
443
 
441
444
 
445
+ def validate_input_list(
446
+ items: list[int | str],
447
+ n_assets: int,
448
+ assets_names: np.ndarray[str] | None,
449
+ name: str,
450
+ raise_if_string_missing: bool = True,
451
+ ) -> list[int]:
452
+ """Convert a list of items (asset indices or asset names) into a list of
453
+ validated asset indices.
454
+
455
+ Parameters
456
+ ----------
457
+ items : list[int | str]
458
+ List of asset indices or asset names.
459
+
460
+ n_assets : int
461
+ Expected number of assets.
462
+ Used for verification.
463
+
464
+ assets_names : ndarray, optional
465
+ Asset names used when `items` contain strings.
466
+
467
+ name : str
468
+ Name of the items used for error messages.
469
+
470
+ raise_if_string_missing : bool, default=True
471
+ If set to True, raises an error if an item string is missing from assets_names;
472
+ otherwise, issue a User Warning.
473
+
474
+ Returns
475
+ -------
476
+ values : list[int]
477
+ Converted and validated list.
478
+ """
479
+ if len(set(items)) != len(items):
480
+ raise ValueError(f"Duplicates found in {items}")
481
+
482
+ asset_indices = set(range(n_assets))
483
+ res = []
484
+ for asset in items:
485
+ if isinstance(asset, str):
486
+ if assets_names is None:
487
+ raise ValueError(
488
+ f"If `{name}` is provided as a list of string, you must input `X` "
489
+ f"as a DataFrame with assets names in columns."
490
+ )
491
+ mask = assets_names == asset
492
+ if np.any(mask):
493
+ res.append(int(np.where(mask)[0][0]))
494
+ else:
495
+ if raise_if_string_missing:
496
+ raise ValueError(f"{asset} not found in {assets_names}")
497
+ else:
498
+ warnings.warn(f"{asset} not found in {assets_names}", stacklevel=2)
499
+ else:
500
+ if asset not in asset_indices:
501
+ raise ValueError(f"`central_assets` {asset} is not in {asset_indices}.")
502
+ res.append(int(asset))
503
+ return res
504
+
505
+
442
506
  def format_measure(x: float, percent: bool = False) -> str:
443
507
  """Format a measure number into a user-friendly string.
444
508
 
@@ -514,7 +578,7 @@ def fit_single_estimator(
514
578
  indices: np.ndarray | None = None,
515
579
  axis: int = 0,
516
580
  ):
517
- """function used to fit an estimator within a job.
581
+ """Function used to fit an estimator within a job.
518
582
 
519
583
  Parameters
520
584
  ----------
@@ -622,7 +686,7 @@ def fit_and_predict(
622
686
 
623
687
 
624
688
  def default_asset_names(n_assets: int) -> np.ndarray:
625
- """Default asset names are `["x0", "x1", ..., "x(n_assets - 1)"]`
689
+ """Default asset names are `["x0", "x1", ..., "x(n_assets - 1)"]`.
626
690
 
627
691
  Parameters
628
692
  ----------
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: skfolio
3
- Version: 0.6.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
- 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.
@@ -56,36 +56,39 @@ Classifier: Topic :: Software Development
56
56
  Requires-Python: >=3.10
57
57
  Description-Content-Type: text/x-rst
58
58
  License-File: LICENSE
59
- Requires-Dist: numpy >=1.23.4
60
- Requires-Dist: scipy >=1.8.0
61
- Requires-Dist: pandas >=1.4.1
62
- Requires-Dist: cvxpy >=1.4.1
63
- Requires-Dist: scikit-learn >=1.5.0
64
- Requires-Dist: joblib >=1.3.2
65
- Requires-Dist: plotly >=5.22.0
59
+ Requires-Dist: numpy>=1.23.4
60
+ Requires-Dist: scipy>=1.15.2
61
+ Requires-Dist: pandas>=1.4.1
62
+ Requires-Dist: cvxpy-base>=1.5.0
63
+ Requires-Dist: clarabel>=0.9.0
64
+ Requires-Dist: scikit-learn>=1.6.0
65
+ Requires-Dist: joblib>=1.3.2
66
+ Requires-Dist: plotly>=5.22.0
67
+ Provides-Extra: dev
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"
66
73
  Provides-Extra: docs
67
- Requires-Dist: cvxpy[scip] ; extra == 'docs'
68
- Requires-Dist: Sphinx ; extra == 'docs'
69
- Requires-Dist: sphinx-gallery ; extra == 'docs'
70
- Requires-Dist: sphinx-design ; extra == 'docs'
71
- Requires-Dist: pydata-sphinx-theme ==0.13.3 ; extra == 'docs'
72
- Requires-Dist: matplotlib ; extra == 'docs'
73
- Requires-Dist: kaleido ==0.2.1 ; extra == 'docs'
74
- Requires-Dist: sphinx-copybutton ; extra == 'docs'
75
- Requires-Dist: numpydoc ; extra == 'docs'
76
- Requires-Dist: sphinx-togglebutton ; extra == 'docs'
77
- Requires-Dist: sphinx-favicon ; extra == 'docs'
78
- Requires-Dist: sphinx-prompt ; extra == 'docs'
79
- Requires-Dist: sphinxext.opengraph ; extra == 'docs'
80
- Requires-Dist: sphinx-sitemap ; extra == 'docs'
81
- Requires-Dist: jupyterlite-sphinx ; extra == 'docs'
82
- Requires-Dist: jupyterlite-pyodide-kernel ; extra == 'docs'
83
- Requires-Dist: nbformat ; extra == 'docs'
84
- Provides-Extra: tests
85
- Requires-Dist: cvxpy[scip] ; extra == 'tests'
86
- Requires-Dist: pytest ; extra == 'tests'
87
- Requires-Dist: pytest-cov ; extra == 'tests'
88
- Requires-Dist: ruff ; extra == 'tests'
74
+ Requires-Dist: PySCIPOpt; extra == "docs"
75
+ Requires-Dist: Sphinx; extra == "docs"
76
+ Requires-Dist: sphinx-gallery; extra == "docs"
77
+ Requires-Dist: sphinx-design; extra == "docs"
78
+ Requires-Dist: pydata-sphinx-theme==0.13.3; extra == "docs"
79
+ Requires-Dist: matplotlib; extra == "docs"
80
+ Requires-Dist: kaleido==0.2.1; extra == "docs"
81
+ Requires-Dist: sphinx-copybutton; extra == "docs"
82
+ Requires-Dist: numpydoc; extra == "docs"
83
+ Requires-Dist: sphinx-togglebutton; extra == "docs"
84
+ Requires-Dist: sphinx-favicon; extra == "docs"
85
+ Requires-Dist: sphinx-prompt; extra == "docs"
86
+ Requires-Dist: sphinxext.opengraph; extra == "docs"
87
+ Requires-Dist: sphinx-sitemap; extra == "docs"
88
+ Requires-Dist: jupyterlite-sphinx; extra == "docs"
89
+ Requires-Dist: jupyterlite-pyodide-kernel; extra == "docs"
90
+ Requires-Dist: nbformat; extra == "docs"
91
+ Dynamic: license-file
89
92
 
90
93
  .. -*- mode: rst -*-
91
94
 
@@ -128,8 +131,9 @@ Requires-Dist: ruff ; extra == 'tests'
128
131
  .. |NumpyMinVersion| replace:: 1.23.4
129
132
  .. |ScipyMinVersion| replace:: 1.8.0
130
133
  .. |PandasMinVersion| replace:: 1.4.1
131
- .. |CvxpyMinVersion| replace:: 1.4.1
132
- .. |SklearnMinVersion| replace:: 1.5.0
134
+ .. |CvxpyBaseMinVersion| replace:: 1.5.0
135
+ .. |ClarabelMinVersion| replace:: 0.9.0
136
+ .. |SklearnMinVersion| replace:: 1.6.0
133
137
  .. |JoblibMinVersion| replace:: 1.3.2
134
138
  .. |PlotlyMinVersion| replace:: 5.22.0
135
139
 
@@ -147,7 +151,7 @@ Requires-Dist: ruff ; extra == 'tests'
147
151
  It offers a unified interface and tools compatible with scikit-learn to build, fine-tune,
148
152
  and cross-validate portfolio models.
149
153
 
150
- It is distributed under the open source 3-Clause BSD license.
154
+ It is distributed under the open-source 3-Clause BSD license.
151
155
 
152
156
  .. image:: https://raw.githubusercontent.com/skfolio/skfolio/master/docs/_static/expo.jpg
153
157
  :target: https://skfolio.org/auto_examples/
@@ -179,7 +183,8 @@ Dependencies
179
183
  - numpy (>= |NumpyMinVersion|)
180
184
  - scipy (>= |ScipyMinVersion|)
181
185
  - pandas (>= |PandasMinVersion|)
182
- - cvxpy (>= |CvxpyMinVersion|)
186
+ - cvxpy-base (>= |CvxpyBaseMinVersion|)
187
+ - clarabel (>= |ClarabelMinVersion|)
183
188
  - scikit-learn (>= |SklearnMinVersion|)
184
189
  - joblib (>= |JoblibMinVersion|)
185
190
  - plotly (>= |PlotlyMinVersion|)
@@ -193,7 +198,7 @@ Unfortunately, it faces a number of shortcomings, including high sensitivity to
193
198
  input parameters (expected returns and covariance), weight concentration, high turnover,
194
199
  and poor out-of-sample performance.
195
200
 
196
- It is well known that naive allocation (1/N, inverse-vol, etc.) tends to outperform
201
+ It is well-known that naive allocation (1/N, inverse-vol, etc.) tends to outperform
197
202
  MVO out-of-sample (DeMiguel, 2007).
198
203
 
199
204
  Numerous approaches have been developed to alleviate these shortcomings (shrinkage,
@@ -202,10 +207,10 @@ approaches, coherent risk measures, left-tail risk optimization, distributionall
202
207
  optimization, factor model, risk-parity, hierarchical clustering, ensemble methods,
203
208
  pre-selection, etc.).
204
209
 
205
- With this large number of methods, added to the fact that they can be composed together,
206
- there is a need for a unified framework with a machine learning approach to perform
207
- model selection, validation, and parameter tuning while reducing the risk of data
208
- leakage and overfitting.
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.
209
214
 
210
215
  This framework is built on scikit-learn's API.
211
216
 
@@ -255,10 +260,27 @@ Available models
255
260
  * Distance Correlation
256
261
  * Variation of Information
257
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
+
258
279
  * Prior Estimator:
259
280
  * Empirical
260
281
  * Black & Litterman
261
282
  * Factor Model
283
+ * Synthetic Data (Stress Test, Factor Stress Test)
262
284
 
263
285
  * Uncertainty Set Estimator:
264
286
  * On Expected Returns:
@@ -266,12 +288,14 @@ Available models
266
288
  * Circular Bootstrap
267
289
  * On Covariance:
268
290
  * Empirical
269
- * Circular bootstrap
291
+ * Circular Bootstrap
270
292
 
271
293
  * Pre-Selection Transformer:
272
294
  * Non-Dominated Selection
273
295
  * Select K Extremes (Best or Worst)
274
296
  * Drop Highly Correlated Assets
297
+ * Select Non-Expiring Assets
298
+ * Select Complete Assets (handle late inception, delisting, etc.)
275
299
 
276
300
  * Cross-Validation and Model Selection:
277
301
  * Compatible with all `sklearn` methods (KFold, etc.)
@@ -316,6 +340,8 @@ Available models
316
340
  * Budget Constraints
317
341
  * Tracking Error Constraints
318
342
  * Turnover Constraints
343
+ * Cardinality and Group Cardinality Constraints
344
+ * Threshold (Long and Short) Constraints
319
345
 
320
346
  Quickstart
321
347
  ~~~~~~~~~~
@@ -338,6 +364,7 @@ Imports
338
364
 
339
365
  from skfolio import RatioMeasure, RiskMeasure
340
366
  from skfolio.datasets import load_factors_dataset, load_sp500_dataset
367
+ from skfolio.distribution import VineCopula
341
368
  from skfolio.model_selection import (
342
369
  CombinatorialPurgedCV,
343
370
  WalkForward,
@@ -358,7 +385,7 @@ Imports
358
385
  )
359
386
  from skfolio.pre_selection import SelectKExtremes
360
387
  from skfolio.preprocessing import prices_to_returns
361
- from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel
388
+ from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel, SyntheticData
362
389
  from skfolio.uncertainty_set import BootstrapMuUncertaintySet
363
390
 
364
391
  Load Dataset
@@ -629,12 +656,61 @@ Combinatorial Purged Cross-Validation
629
656
  print(population.summary())
630
657
 
631
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
+
632
708
  Recognition
633
709
  ~~~~~~~~~~~
634
710
 
635
- We would like to thank all contributors behind our direct dependencies, such as
636
- scikit-learn and cvxpy, but also the contributors of the following resources that were a
637
- source of inspiration:
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:
638
714
 
639
715
  * PyPortfolioOpt
640
716
  * Riskfolio-Lib
@@ -0,0 +1,120 @@
1
+ skfolio/__init__.py,sha256=XdSV1bcfft5pNl5Y_mX8MR0IzjXjRs8uRURp42UGa08,635
2
+ skfolio/exceptions.py,sha256=omi5qQiEuFDpIoZfQHQxORZRcKYkusmdGPLBJt-Sna0,805
3
+ skfolio/typing.py,sha256=5wnu_qoGZtCWKu-nHlZ5w3rOKy5CXxGI5ZvzDSR9pLU,1394
4
+ skfolio/cluster/__init__.py,sha256=ycySaq2MgG3etqNF-pITuYKfYPHYm3-frjFc8PRzMc0,267
5
+ skfolio/cluster/_hierarchical.py,sha256=PTtr6H4keY6DEVvXyYM24AnNjj72sNaXKjGFEyMXZ5c,12839
6
+ skfolio/datasets/__init__.py,sha256=hxeG8dQOQWzlsKagxyAp2nX67wj7fAD-UZtv36xzMIE,497
7
+ skfolio/datasets/_base.py,sha256=n95CwIYEgh4cShFUItph7Prgv5XkndhBZ0OdOIE66Wk,16088
8
+ skfolio/datasets/data/__init__.py,sha256=XN1VFRn8-YLCbu6avsq21aUA94gN5f0WX0I5riTsNEk,27
9
+ skfolio/datasets/data/factors_dataset.csv.gz,sha256=brCJlT25DJo40yg1gnUXAakNtvWZZYR_1ksFeN5JcWE,36146
10
+ skfolio/datasets/data/sp500_dataset.csv.gz,sha256=7iHKwovvsdCnOanOsiGE-ZU5RyaqDP3pohlB0awErA0,426065
11
+ skfolio/datasets/data/sp500_index.csv.gz,sha256=iUw0QxwoT4aqZKRn4Xbio8m2l8hX65qzUAbC3VXT_fI,41898
12
+ skfolio/distance/__init__.py,sha256=KFDHNcpzA8Hng0xAs9JyFEkbDRubgeDrQUP4B2ZwYjw,563
13
+ skfolio/distance/_base.py,sha256=3qqkWfqb3igC6Y7XHwPYir9A2qwi2LjCtLlczMGi6UI,1343
14
+ skfolio/distance/_distance.py,sha256=-ztyE8oittGfn-ib4-JztAXxeIQ4QLAOj4OX4AJ2G64,19107
15
+ skfolio/distribution/__init__.py,sha256=A42xmx-VjMSy-An6ZIdhwPLFAoViCaOY7KUQDHgaOAY,1295
16
+ skfolio/distribution/_base.py,sha256=YEnJsLSO-_NkucUNKSaW9ZTwoORXFj7DWnFrxwaYaco,5665
17
+ skfolio/distribution/copula/__init__.py,sha256=dKN-BGl5t5EXlVb7Qamkktbj1V098Ocf3J6qNlRssI4,1106
18
+ skfolio/distribution/copula/_base.py,sha256=1N3I2lFuzm5ge1zG9VZWufIwwMKf8oE1OT_PtVEPyHo,15982
19
+ skfolio/distribution/copula/_clayton.py,sha256=xat9ZyhV3FbWcDVn6ax6Z8YGuOsHTKGVUXXNxxCqhcQ,18307
20
+ skfolio/distribution/copula/_gaussian.py,sha256=-OxQdvFEBesozIPofBG_X0j1tDmwlu4i9bpxnV7iqS4,14128
21
+ skfolio/distribution/copula/_gumbel.py,sha256=KIhyfGyF9jLRKqZo8D5k19k3Wgw9X6hX1o9pVDxzG00,19354
22
+ skfolio/distribution/copula/_independent.py,sha256=OIqmig7hP-KTCuBi0bsb4fwEkQkaxipYoy1JR1xer6w,6360
23
+ skfolio/distribution/copula/_joe.py,sha256=kSqPqMyyyNTgPEGSnlOm8drrRqMRhV2fNuJw1daxcMc,21446
24
+ skfolio/distribution/copula/_selection.py,sha256=JovKO2_Vq9Zq1WFArFTeKXSDVLFJtCRB8qi3N7vhnRM,4438
25
+ skfolio/distribution/copula/_student_t.py,sha256=Zl5WlmZ5fum2v6CH4VyVPln7Aivo_49Jj5hHcYlNQfU,17821
26
+ skfolio/distribution/copula/_utils.py,sha256=drMtv71bkwlerR0HJCdNCZTuFSitN5vn33avRGWxj94,16440
27
+ skfolio/distribution/multivariate/__init__.py,sha256=E9AR0Hh5wWShOTwj62R1RVMkzZpXc5Ams4ppibwhrUY,339
28
+ skfolio/distribution/multivariate/_base.py,sha256=MV3rhTafPlKdb3wuLbHfhApyV1ll7WmfzdR97Dq8VZw,8716
29
+ skfolio/distribution/multivariate/_utils.py,sha256=WNL1lzO0Ki5x_yO8p3GRKrXwG4fK99je7sDQ3avyUQ8,19274
30
+ skfolio/distribution/multivariate/_vine_copula.py,sha256=b545CKAMXUXMaIiI7hQ-P8jHhmZ4hUnGo2F22uWtbac,49756
31
+ skfolio/distribution/univariate/__init__.py,sha256=m9vZUhZyRUT5IOQRixGPdGci1wtC5ua8RWtHsC8HAlU,628
32
+ skfolio/distribution/univariate/_base.py,sha256=8oPMOdQi2wHc-UOWG18x_eZ57G_ksQhPM9C0ncYf1Lg,9874
33
+ skfolio/distribution/univariate/_gaussian.py,sha256=pe8YxTQjvObeVeZD2YXduN5M-k2kNNTy2q0AvYCm1n4,4274
34
+ skfolio/distribution/univariate/_johnson_su.py,sha256=Dl1WyCmn-sRE4BrckVNGXHz9biDQtXyPq1JXEPKIHBo,4857
35
+ skfolio/distribution/univariate/_normal_inverse_gaussian.py,sha256=oq5omNUQanFWBGaYSNwf9YDa6c-B1j9ZErq6p96resc,4983
36
+ skfolio/distribution/univariate/_selection.py,sha256=6KL4gngiLKwaBUpCDX19ABOkMBzZp1YVRnXFrUtppCs,3110
37
+ skfolio/distribution/univariate/_student_t.py,sha256=GcI4fKp6q5XegfvT_i3AqfWlUMxCq7A5sX6Xsf4pye8,4553
38
+ skfolio/measures/__init__.py,sha256=lB5xBqEFU-8x-12AA1VdCHaPwYpfwvejRaiYnr8IGYg,1647
39
+ skfolio/measures/_enums.py,sha256=S6WOT8NHzm-eMHELuOjngIBupCctCdiTA2BaJlWl-4E,8956
40
+ skfolio/measures/_measures.py,sha256=LmKgSAOXaKGomAcO1FkeypD6tRiEeDLUIh6lySky4vs,16835
41
+ skfolio/metrics/__init__.py,sha256=ebu5h7Q9X0f3ZZ1VFmAEBPic2sirboKG_zNBHO5abjo,98
42
+ skfolio/metrics/_scorer.py,sha256=L-qct4cby15a4xC4arSaG5__1mxBCQYeMjlrHBIVnSY,4325
43
+ skfolio/model_selection/__init__.py,sha256=BT8VCXW7C4bXI2Oam4amTHOcJVlKxLpkcsHjB63pZHQ,524
44
+ skfolio/model_selection/_combinatorial.py,sha256=XVbZuVA0ePEYpbKiyioQcNULtEEm5KM0Bc_-AvzQTtk,19109
45
+ skfolio/model_selection/_validation.py,sha256=fM3PnB9O6JrgyEznf4-yvmFLz-z0trOoYSKkMLzWqVc,10051
46
+ skfolio/model_selection/_walk_forward.py,sha256=Q_RV2Aw4J7nB5UhhrWuWcoYmyxc9CN45yL3vpyfuFGo,15060
47
+ skfolio/moments/__init__.py,sha256=st8AYX3tHT2ZkqnnMNbS6CiwufvHq6Tl6nHtRVhtlq0,794
48
+ skfolio/moments/covariance/__init__.py,sha256=twNNLP44sv4-3EgET27UdJ-8wbVgF2cYmIn8DERwFTk,1068
49
+ skfolio/moments/covariance/_base.py,sha256=vrYW0q1tG-l_uvnRk1TBTlQnamdmMXdJOZutRrRl08M,3986
50
+ skfolio/moments/covariance/_denoise_covariance.py,sha256=mXjcEYZn9HXVTkiUn4CkKM9Jy27XLOnweInbyu4f-_o,7002
51
+ skfolio/moments/covariance/_detone_covariance.py,sha256=Yw24UA68mw4yHe_utOD4FpOdWfV8kFXAMCNLZgGLD8c,6109
52
+ skfolio/moments/covariance/_empirical_covariance.py,sha256=ayIr0yn3oJS_H9LpHNCfHwttSGry3lR6_umXE_nE6tg,3560
53
+ skfolio/moments/covariance/_ew_covariance.py,sha256=z0-mcnewjKya8uz8JOO9BkS5xkSgTI1sG0ndJVScn1o,3732
54
+ skfolio/moments/covariance/_gerber_covariance.py,sha256=IHO4wcefrbvTUECrkEEnL_UOQSHNRLTrj6arrDKk73o,5915
55
+ skfolio/moments/covariance/_graphical_lasso_cv.py,sha256=vCQxDsqxJlNHUAs_ETPPW1y_v_5Zp0ePZC2NkTNmM-s,6555
56
+ skfolio/moments/covariance/_implied_covariance.py,sha256=ioXg1oThiOAHHZVZh3upPdDVJcCzACe831ypPXlZoM8,17625
57
+ skfolio/moments/covariance/_ledoit_wolf.py,sha256=bCorMMeQ7137QDKDW_TUzDiC0DdcRAvzRPoKL5EMCX8,4891
58
+ skfolio/moments/covariance/_oas.py,sha256=V9FAE4-VsiOXpAA5tGilphA32hfPolfzfoI974jLEY4,3700
59
+ skfolio/moments/covariance/_shrunk_covariance.py,sha256=URJgonaWoiOUQcSrtN1LUrJtdi4tCrwiuNUg-FaDNvE,3111
60
+ skfolio/moments/expected_returns/__init__.py,sha256=Bi3c4bok3SyktdYeFUs3VepTrtpmDITIk9GXPhIuDc0,504
61
+ skfolio/moments/expected_returns/_base.py,sha256=Bla1peGBVafIUwOxNuRF5-T84k-QHN8Z2W6mdLoSbzU,887
62
+ skfolio/moments/expected_returns/_empirical_mu.py,sha256=KLwO_9HCK7H6eiB9JU5EdLWcMqD2Vr7wcSjXONJk1Cg,1879
63
+ skfolio/moments/expected_returns/_equilibrium_mu.py,sha256=A4zAYZ7ex2Y68YV0HajYDKtnH0luQuKEN4hENEwk4Lk,4423
64
+ skfolio/moments/expected_returns/_ew_mu.py,sha256=vDOqpTpTY3iaJc9PfMU_dpdfglT1dJ_DuM3pCTpjHpc,2125
65
+ skfolio/moments/expected_returns/_shrunk_mu.py,sha256=nqypZJweZIf6u3Idz-TLPHiD3h3XzuKgTEQWJHSVnwo,8292
66
+ skfolio/optimization/__init__.py,sha256=LA4n85e-wVTeRNI-NlTU1ID5FhP3-B410kmsh9268Ho,1049
67
+ skfolio/optimization/_base.py,sha256=lPVvoV36URnjMUJGOO23QbaFYiuyVA9oRuvk68Dwd7o,5780
68
+ skfolio/optimization/cluster/__init__.py,sha256=nxsuDxviDbj-YMHhQXIkUEWUoKPhPn10bQ0_nULNUoE,424
69
+ skfolio/optimization/cluster/_nco.py,sha256=Gbd18HYlwq_MUd9JmytM1-Uqu-GFT8NXb8QWPVgmDxk,16433
70
+ skfolio/optimization/cluster/hierarchical/__init__.py,sha256=eT1A6YKETKCBEnrUc6pHwyTkDVRcUr8jtdtmN3kdh0c,446
71
+ skfolio/optimization/cluster/hierarchical/_base.py,sha256=mdplfuwUepui8RWPlySxoviuA0PNIkgn6wSYENNy9H0,16295
72
+ skfolio/optimization/cluster/hierarchical/_herc.py,sha256=5ZYkoi8ywN89U2xi1lc-3B8TPQjLlxbUmOSB3PR_nKA,20414
73
+ skfolio/optimization/cluster/hierarchical/_hrp.py,sha256=06kch9QkthV5yB8RNB_5Xz-IUuNq-fcZPjz99Mz5otg,18171
74
+ skfolio/optimization/convex/__init__.py,sha256=q1Q2p7HcnmbQlBIA0SXm0TwDGxl7hRc0JhF1o01lFSg,605
75
+ skfolio/optimization/convex/_base.py,sha256=mUTXVM6bq5cvlieAl6TXNGd6BNIqBajoAiDL28fPx9o,89455
76
+ skfolio/optimization/convex/_distributionally_robust.py,sha256=4iWfEuJGuBawVGU5X1-QHVMMh9hBnMtou2Uh5hRdXeA,17958
77
+ skfolio/optimization/convex/_maximum_diversification.py,sha256=a8nDecN6jTR_bOFKBsenI4G2kNu5t98y5ALY78lNrAU,19657
78
+ skfolio/optimization/convex/_mean_risk.py,sha256=mM5KMCxwAf1dT6JTxJuuQfvvk63hMQg5GD3LumQkfjQ,49534
79
+ skfolio/optimization/convex/_risk_budgeting.py,sha256=xtRg3CGmasi-ebx7e5XevHJs3n9PpccaZR7Z7loyKDc,23653
80
+ skfolio/optimization/ensemble/__init__.py,sha256=IJhsX8f-6wclc9a6Fd8yAQvZKxtxq4Qf7AC2CLryHrU,195
81
+ skfolio/optimization/ensemble/_base.py,sha256=e0dWCEIYnho3HU2KGGS9UHQdycdVuqMcTe7hi0LihjQ,3416
82
+ skfolio/optimization/ensemble/_stacking.py,sha256=DaswFVBTghP10vHGESn6aLT7C9wgp-D8NuXGtpdZcwE,14192
83
+ skfolio/optimization/naive/__init__.py,sha256=1QKgOuA6DoqKVOsJxWKogaGPyOir6ln-aQ28PTAbtJs,181
84
+ skfolio/optimization/naive/_naive.py,sha256=w5lDVpn5YeO6NN5dRzSRvj-V6GpfLogOW7FVxevwZl8,6453
85
+ skfolio/population/__init__.py,sha256=ehKwWhDJCifjhEL-QezVR0xYjzRTeyHbrEMbfWjF9cU,106
86
+ skfolio/population/_population.py,sha256=jmfy759zm5Z0ReRBaHMHCaPHEOf8AFxqXVzYrNhaVIw,31354
87
+ skfolio/portfolio/__init__.py,sha256=YeDSH0ZdyE-lcbDqpNw9IOltURtoM-ewAzzcec44Q5Q,586
88
+ skfolio/portfolio/_base.py,sha256=V81HUQ2CWmohGOeNip1dPESGnmRKQk8eDAthjkvVFhQ,40541
89
+ skfolio/portfolio/_multi_period_portfolio.py,sha256=9z71aZL2GrV6rQ_EkIyPkK-mJ9N2ZLZCIinSScfRgfw,24412
90
+ skfolio/portfolio/_portfolio.py,sha256=o1e1KNZAuxlC8y3zTIcaW7c2jk_LlEBCzEF8FRJht20,32791
91
+ skfolio/pre_selection/__init__.py,sha256=3hqxwd8nAa1dBna5MrE1P5JPrM-OkSvXGyhbMq7ZKIk,511
92
+ skfolio/pre_selection/_drop_correlated.py,sha256=4-PSd8R20Rcdyc8Zzcy9B2eRPEtaEkM3YXi74YKF-Pk,3839
93
+ skfolio/pre_selection/_select_complete.py,sha256=2nEvcjROMJzhAHMCHADeAiCws_tc-BMtndIkjRexL84,3902
94
+ skfolio/pre_selection/_select_k_extremes.py,sha256=nMugK88igmscribCw_I1UnjE_O7cuIjrJF8AGuVTfiA,3082
95
+ skfolio/pre_selection/_select_non_dominated.py,sha256=Auv7G8E1QNO96heb35oBWmFLd68LlVDRgSpcg7wpv5A,6004
96
+ skfolio/pre_selection/_select_non_expiring.py,sha256=hVXLNw5KBU7WxOI6v4feZ9lJaVIgl-CBhW80T9-ZUac,5105
97
+ skfolio/preprocessing/__init__.py,sha256=94jMyP_E7FlwQVE8D_bXDi8KyfAA2xPHTDvYOi6zf_g,123
98
+ skfolio/preprocessing/_returns.py,sha256=6G5qJIVHGnIoeBNAqpJTB-569g9NeXVIyrz033bK5Gk,4576
99
+ skfolio/prior/__init__.py,sha256=daUO3ha87Nu0ixJci33dR1dKgoYC6-1Nf3AUoaskE5o,544
100
+ skfolio/prior/_base.py,sha256=Py3Ip3mDhaDyBVWQy9Mz7ztv3RkovVC58gw4rCcC-jU,1958
101
+ skfolio/prior/_black_litterman.py,sha256=oMNYNyDSBp8Uygp0EvQissjNKS41GMLCVzITUqA0HeY,10470
102
+ skfolio/prior/_empirical.py,sha256=jDWgNhCfqOFVbVBphACZsqpK47OPOKGUCnOVsgmdqXI,7324
103
+ skfolio/prior/_factor_model.py,sha256=GhilLpNu8UdPrj5vb63zKJ9WWnt79k3SpNf6ULqZ8Bk,11571
104
+ skfolio/prior/_synthetic_data.py,sha256=XhavOTbbwBtO1suoA4pfZnm5YAdlykb07NQvvqPpRxo,8551
105
+ skfolio/synthetic_returns/__init__.py,sha256=-dnmFSmrTJcsMmrwIxPCbqENbx6gTuWAm_cx7nQHpns,29
106
+ skfolio/uncertainty_set/__init__.py,sha256=SHbOq0ip3vuwEK9G4pzz0GncDbGsHw7ywF9tPnkUrZ8,648
107
+ skfolio/uncertainty_set/_base.py,sha256=R6qH8Zg5Ti3Qny-guL4Js8rY9JhpF8jMwV_w9HCbgWI,4307
108
+ skfolio/uncertainty_set/_bootstrap.py,sha256=tDnUvhTtl0HWu-xL6MWZZZyWs4Y06PKQ5xPDiOU7RE4,11265
109
+ skfolio/uncertainty_set/_empirical.py,sha256=t9_V23gH1eJ0jaASQcus-QOSATAr9HKVW2hjHMNYjO0,9380
110
+ skfolio/utils/__init__.py,sha256=bC6-MsCVF7xKTr48z7OzJJUeWvqAB7BiHeNTiKsme70,20
111
+ skfolio/utils/bootstrap.py,sha256=6BN_9CgfbeImBSNEE0dF52FRGuQT41HcQXeHPLwFqJc,3565
112
+ skfolio/utils/equations.py,sha256=yj6-TReoPq3YaUQyAx-t4wZNbODON2T4TyA82z2SnkU,15577
113
+ skfolio/utils/sorting.py,sha256=F7gfIBfnulfDUiqvzrlR-pba4PPLJT6NH7-5s4sdRhw,3521
114
+ skfolio/utils/stats.py,sha256=glVHo7rjwy06dl5kkULLOADMrEkVJcfXXAz-1qmYQL4,17005
115
+ skfolio/utils/tools.py,sha256=sGJFiqc60TqXyaWoH7JdsbaFYj_bvwq3hHIk6FxDC3U,22994
116
+ skfolio-0.8.0.dist-info/licenses/LICENSE,sha256=F6Gi-ZJX5BlVzYK8R9NcvAkAsKa7KO29xB1OScbrH6Q,1526
117
+ skfolio-0.8.0.dist-info/METADATA,sha256=2I3oPRJjRLU5M_KQRaAx9xmrcARPZbaMuE8qu2RdSAE,22383
118
+ skfolio-0.8.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
119
+ skfolio-0.8.0.dist-info/top_level.txt,sha256=NXEaoS9Ms7t32gxkb867nV0OKlU0KmssL7IJBVo0fJs,8
120
+ skfolio-0.8.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5