skfolio 0.1.3__tar.gz → 0.2.1__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.
Files changed (87) hide show
  1. {skfolio-0.1.3/src/skfolio.egg-info → skfolio-0.2.1}/PKG-INFO +16 -16
  2. {skfolio-0.1.3 → skfolio-0.2.1}/README.rst +15 -15
  3. {skfolio-0.1.3 → skfolio-0.2.1}/pyproject.toml +1 -1
  4. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/model_selection/__init__.py +2 -0
  5. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/model_selection/_combinatorial.py +162 -13
  6. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/_base.py +8 -3
  7. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/cluster/hierarchical/_base.py +5 -4
  8. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/cluster/hierarchical/_herc.py +2 -2
  9. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/cluster/hierarchical/_hrp.py +2 -2
  10. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/convex/_base.py +5 -4
  11. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/convex/_distributionally_robust.py +2 -2
  12. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/convex/_maximum_diversification.py +2 -2
  13. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/convex/_mean_risk.py +2 -2
  14. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/convex/_risk_budgeting.py +2 -2
  15. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/naive/_naive.py +6 -6
  16. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/population/_population.py +23 -21
  17. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/prior/_black_litterman.py +1 -1
  18. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/prior/_empirical.py +5 -4
  19. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/prior/_factor_model.py +2 -1
  20. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/tools.py +8 -7
  21. {skfolio-0.1.3 → skfolio-0.2.1/src/skfolio.egg-info}/PKG-INFO +16 -16
  22. {skfolio-0.1.3 → skfolio-0.2.1}/LICENSE +0 -0
  23. {skfolio-0.1.3 → skfolio-0.2.1}/MANIFEST.in +0 -0
  24. {skfolio-0.1.3 → skfolio-0.2.1}/setup.cfg +0 -0
  25. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/__init__.py +0 -0
  26. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/cluster/__init__.py +0 -0
  27. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/cluster/_hierarchical.py +0 -0
  28. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/datasets/__init__.py +0 -0
  29. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/datasets/_base.py +0 -0
  30. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/datasets/data/__init__.py +0 -0
  31. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/datasets/data/factors_dataset.csv.gz +0 -0
  32. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/datasets/data/sp500_dataset.csv.gz +0 -0
  33. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/datasets/data/sp500_index.csv.gz +0 -0
  34. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/distance/__init__.py +0 -0
  35. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/distance/_base.py +0 -0
  36. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/distance/_distance.py +0 -0
  37. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/exceptions.py +0 -0
  38. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/measures/__init__.py +0 -0
  39. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/measures/_enums.py +0 -0
  40. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/measures/_measures.py +0 -0
  41. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/metrics/__init__.py +0 -0
  42. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/metrics/_scorer.py +0 -0
  43. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/model_selection/_validation.py +0 -0
  44. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/model_selection/_walk_forward.py +0 -0
  45. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/__init__.py +0 -0
  46. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/covariance/__init__.py +0 -0
  47. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/covariance/_base.py +0 -0
  48. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/covariance/_covariance.py +0 -0
  49. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/expected_returns/__init__.py +0 -0
  50. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/expected_returns/_base.py +0 -0
  51. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/moments/expected_returns/_expected_returns.py +0 -0
  52. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/__init__.py +0 -0
  53. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/cluster/__init__.py +0 -0
  54. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/cluster/_nco.py +0 -0
  55. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/cluster/hierarchical/__init__.py +0 -0
  56. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/convex/__init__.py +0 -0
  57. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/ensemble/__init__.py +0 -0
  58. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/ensemble/_base.py +0 -0
  59. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/ensemble/_stacking.py +0 -0
  60. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/optimization/naive/__init__.py +0 -0
  61. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/population/__init__.py +0 -0
  62. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/portfolio/__init__.py +0 -0
  63. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/portfolio/_base.py +0 -0
  64. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/portfolio/_multi_period_portfolio.py +0 -0
  65. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/portfolio/_portfolio.py +0 -0
  66. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/pre_selection/__init__.py +0 -0
  67. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/pre_selection/_pre_selection.py +0 -0
  68. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/preprocessing/__init__.py +0 -0
  69. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/preprocessing/_returns.py +0 -0
  70. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/prior/__init__.py +0 -0
  71. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/prior/_base.py +0 -0
  72. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/typing.py +0 -0
  73. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/uncertainty_set/__init__.py +0 -0
  74. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/uncertainty_set/_base.py +0 -0
  75. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/uncertainty_set/_bootstrap.py +0 -0
  76. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/uncertainty_set/_empirical.py +0 -0
  77. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/__init__.py +0 -0
  78. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/bootstrap.py +0 -0
  79. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/equations.py +0 -0
  80. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/fixes/__init__.py +0 -0
  81. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/fixes/_dendrogram.py +0 -0
  82. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/sorting.py +0 -0
  83. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio/utils/stats.py +0 -0
  84. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio.egg-info/SOURCES.txt +0 -0
  85. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio.egg-info/dependency_links.txt +0 -0
  86. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio.egg-info/requires.txt +0 -0
  87. {skfolio-0.1.3 → skfolio-0.2.1}/src/skfolio.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: skfolio
3
- Version: 0.1.3
3
+ Version: 0.2.1
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>
@@ -85,37 +85,37 @@ Requires-Dist: sphinx-favicon; extra == "docs"
85
85
 
86
86
  .. -*- mode: rst -*-
87
87
 
88
- |Licence|_ |Codecov|_ |Black|_ |PythonVersion|_ |PyPi|_ |CI/CD|_ |Downloads|_ |Ruff|_ |Contribution|_ |Website|_
88
+ |Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website|
89
89
 
90
90
  .. |Licence| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
91
- .. _Licence: https://github.com/skfolio/skfolio/blob/main/LICENSE
91
+ :target: https://github.com/skfolio/skfolio/blob/main/LICENSE
92
92
 
93
93
  .. |Codecov| image:: https://codecov.io/gh/skfolio/skfolio/graph/badge.svg?token=KJ0SE4LHPV
94
- .. _Codecov: https://codecov.io/gh/skfolio/skfolio
94
+ :target: https://codecov.io/gh/skfolio/skfolio
95
95
 
96
- .. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue
97
- .. _PythonVersion: https://pypi.org/project/skfolio/
96
+ .. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg
97
+ :target: https://pypi.org/project/skfolio/
98
98
 
99
99
  .. |PyPi| image:: https://img.shields.io/pypi/v/skfolio
100
- .. _PyPi: https://pypi.org/project/skfolio
100
+ :target: https://pypi.org/project/skfolio
101
101
 
102
102
  .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
103
- .. _Black: https://github.com/psf/black
103
+ :target: https://github.com/psf/black
104
104
 
105
- .. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml?logo=github
106
- .. _CI/CD: https://github.com/skfolio/skfolio/raw/main/LICENSE
105
+ .. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml.svg?logo=github
106
+ :target: https://github.com/skfolio/skfolio/raw/main/LICENSE
107
107
 
108
108
  .. |Downloads| image:: https://static.pepy.tech/badge/skfolio
109
- .. _Downloads: https://pepy.tech/project/skfolio
109
+ :target: https://pepy.tech/project/skfolio
110
110
 
111
111
  .. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
112
- .. _Ruff: https://github.com/astral-sh/ruff
112
+ :target: https://github.com/astral-sh/ruff
113
113
 
114
114
  .. |Contribution| image:: https://img.shields.io/badge/Contributions-Welcome-blue
115
- .. _Contribution: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
115
+ :target: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
116
116
 
117
- .. |Website| image:: https://img.shields.io/website-up-down-53cc0d-red/http/skfolio.org
118
- .. _Website: https://skfolio.org
117
+ .. |Website| image:: https://img.shields.io/website.svg?down_color=red&down_message=down&up_color=53cc0d&up_message=up&url=https://skfolio.org
118
+ :target: https://skfolio.org
119
119
 
120
120
  .. |PythonMinVersion| replace:: 3.10
121
121
  .. |NumpyMinVersion| replace:: 1.23.4
@@ -564,7 +564,7 @@ Black & Litterman Factor Model
564
564
  ------------------------------
565
565
  .. code-block:: python
566
566
 
567
- factor_views = ["MTUM - QUAL == 0.03 ", "SIZE - TLT == 0.04", "VLUE == 0.06"]
567
+ factor_views = ["MTUM - QUAL == 0.03 ", "VLUE == 0.06"]
568
568
  model = MeanRisk(
569
569
  objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
570
570
  prior_estimator=FactorModel(
@@ -1,36 +1,36 @@
1
1
  .. -*- mode: rst -*-
2
2
 
3
- |Licence|_ |Codecov|_ |Black|_ |PythonVersion|_ |PyPi|_ |CI/CD|_ |Downloads|_ |Ruff|_ |Contribution|_ |Website|_
3
+ |Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website|
4
4
 
5
5
  .. |Licence| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
6
- .. _Licence: https://github.com/skfolio/skfolio/blob/main/LICENSE
6
+ :target: https://github.com/skfolio/skfolio/blob/main/LICENSE
7
7
 
8
8
  .. |Codecov| image:: https://codecov.io/gh/skfolio/skfolio/graph/badge.svg?token=KJ0SE4LHPV
9
- .. _Codecov: https://codecov.io/gh/skfolio/skfolio
9
+ :target: https://codecov.io/gh/skfolio/skfolio
10
10
 
11
- .. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue
12
- .. _PythonVersion: https://pypi.org/project/skfolio/
11
+ .. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg
12
+ :target: https://pypi.org/project/skfolio/
13
13
 
14
14
  .. |PyPi| image:: https://img.shields.io/pypi/v/skfolio
15
- .. _PyPi: https://pypi.org/project/skfolio
15
+ :target: https://pypi.org/project/skfolio
16
16
 
17
17
  .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
18
- .. _Black: https://github.com/psf/black
18
+ :target: https://github.com/psf/black
19
19
 
20
- .. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml?logo=github
21
- .. _CI/CD: https://github.com/skfolio/skfolio/raw/main/LICENSE
20
+ .. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml.svg?logo=github
21
+ :target: https://github.com/skfolio/skfolio/raw/main/LICENSE
22
22
 
23
23
  .. |Downloads| image:: https://static.pepy.tech/badge/skfolio
24
- .. _Downloads: https://pepy.tech/project/skfolio
24
+ :target: https://pepy.tech/project/skfolio
25
25
 
26
26
  .. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
27
- .. _Ruff: https://github.com/astral-sh/ruff
27
+ :target: https://github.com/astral-sh/ruff
28
28
 
29
29
  .. |Contribution| image:: https://img.shields.io/badge/Contributions-Welcome-blue
30
- .. _Contribution: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
30
+ :target: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
31
31
 
32
- .. |Website| image:: https://img.shields.io/website-up-down-53cc0d-red/http/skfolio.org
33
- .. _Website: https://skfolio.org
32
+ .. |Website| image:: https://img.shields.io/website.svg?down_color=red&down_message=down&up_color=53cc0d&up_message=up&url=https://skfolio.org
33
+ :target: https://skfolio.org
34
34
 
35
35
  .. |PythonMinVersion| replace:: 3.10
36
36
  .. |NumpyMinVersion| replace:: 1.23.4
@@ -479,7 +479,7 @@ Black & Litterman Factor Model
479
479
  ------------------------------
480
480
  .. code-block:: python
481
481
 
482
- factor_views = ["MTUM - QUAL == 0.03 ", "SIZE - TLT == 0.04", "VLUE == 0.06"]
482
+ factor_views = ["MTUM - QUAL == 0.03 ", "VLUE == 0.06"]
483
483
  model = MeanRisk(
484
484
  objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
485
485
  prior_estimator=FactorModel(
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "skfolio"
7
- version = "0.1.3"
7
+ version = "0.2.1"
8
8
  maintainers = [
9
9
  { name = "Hugo Delatte", email = "delatte.hugo@gmail.com" },
10
10
  ]
@@ -6,6 +6,7 @@
6
6
  from skfolio.model_selection._combinatorial import (
7
7
  BaseCombinatorialCV,
8
8
  CombinatorialPurgedCV,
9
+ optimal_folds_number,
9
10
  )
10
11
  from skfolio.model_selection._validation import cross_val_predict
11
12
  from skfolio.model_selection._walk_forward import WalkForward
@@ -15,4 +16,5 @@ __all__ = [
15
16
  "WalkForward",
16
17
  "BaseCombinatorialCV",
17
18
  "CombinatorialPurgedCV",
19
+ "optimal_folds_number",
18
20
  ]
@@ -197,19 +197,13 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
197
197
  @property
198
198
  def n_splits(self) -> int:
199
199
  """Number of splits"""
200
- return int(
201
- math.factorial(self.n_folds)
202
- / (
203
- math.factorial(self.n_test_folds)
204
- * math.factorial(self.n_folds - self.n_test_folds)
205
- )
206
- )
200
+ return _n_splits(n_folds=self.n_folds, n_test_folds=self.n_test_folds)
207
201
 
208
202
  @property
209
203
  def n_test_paths(self) -> int:
210
204
  """Number of test paths that can be reconstructed from the train/test
211
205
  combinations"""
212
- return self.n_splits * self.n_test_folds // self.n_folds
206
+ return _n_test_paths(n_folds=self.n_folds, n_test_folds=self.n_test_folds)
213
207
 
214
208
  @property
215
209
  def test_set_index(self) -> np.ndarray:
@@ -320,17 +314,20 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
320
314
  yield train_index, test_index_list
321
315
 
322
316
  def summary(self, X) -> pd.Series:
323
- n_samples = X.shape[0]
317
+ n_observations = X.shape[0]
318
+ avg_train_size = _avg_train_size(
319
+ n_observations=n_observations,
320
+ n_folds=self.n_folds,
321
+ n_test_folds=self.n_test_folds,
322
+ )
324
323
  return pd.Series(
325
324
  {
326
- "Number of Observations": n_samples,
325
+ "Number of Observations": n_observations,
327
326
  "Total Number of Folds": self.n_folds,
328
327
  "Number of Test Folds": self.n_test_folds,
329
328
  "Purge Size": self.purged_size,
330
329
  "Embargo Size": self.embargo_size,
331
- "Average Training Size": int(
332
- n_samples / self.n_folds * (self.n_folds - self.n_test_folds)
333
- ),
330
+ "Average Training Size": int(avg_train_size),
334
331
  "Number of Test Paths": self.n_test_paths,
335
332
  "Number of Training Combinations": self.n_splits,
336
333
  }
@@ -410,3 +407,155 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
410
407
  )
411
408
 
412
409
  return fig
410
+
411
+
412
+ def _n_splits(n_folds: int, n_test_folds: int) -> int:
413
+ """Number of splits.
414
+
415
+ Parameters
416
+ ----------
417
+ n_folds : int
418
+ Number of folds.
419
+
420
+ n_test_folds : int
421
+ Number of test folds.
422
+
423
+ Returns
424
+ -------
425
+ n_splits : int
426
+ Number of splits
427
+ """
428
+ return int(math.comb(n_folds, n_test_folds))
429
+
430
+
431
+ def _n_test_paths(n_folds: int, n_test_folds: int) -> int:
432
+ """Number of test paths that can be reconstructed from the train/test
433
+ combinations
434
+
435
+ Parameters
436
+ ----------
437
+ n_folds : int
438
+ Number of folds.
439
+
440
+ n_test_folds : int
441
+ Number of test folds.
442
+
443
+ Returns
444
+ -------
445
+ n_splits : int
446
+ Number of test paths.
447
+ """
448
+ return (
449
+ _n_splits(n_folds=n_folds, n_test_folds=n_test_folds) * n_test_folds // n_folds
450
+ )
451
+
452
+
453
+ def _avg_train_size(n_observations: int, n_folds: int, n_test_folds: int) -> float:
454
+ """Average number of observations contained in each training set.
455
+
456
+ Parameters
457
+ ----------
458
+ n_observations : int
459
+ Number of observations.
460
+
461
+ n_folds : int
462
+ Number of folds.
463
+
464
+ n_test_folds : int
465
+ Number of test folds.
466
+
467
+ Returns
468
+ -------
469
+ avg_train_size : float
470
+ Average number of observations contained in each training set.
471
+ """
472
+ return n_observations / n_folds * (n_folds - n_test_folds)
473
+
474
+
475
+ def optimal_folds_number(
476
+ n_observations: int,
477
+ target_train_size: int,
478
+ target_n_test_paths: int,
479
+ weight_train_size: float = 1,
480
+ weight_n_test_paths: float = 1,
481
+ ) -> tuple[int, int]:
482
+ r"""Find the optimal number of folds (total folds and test folds) for a target
483
+ training size and a target number of test paths.
484
+
485
+ We find `x = n_folds` and `y = n_test_folds` that minimizes the below
486
+ cost function of the relative distance from the two targets:
487
+
488
+ .. math::
489
+ cost(x,y) = w_{f} \times \lvert\frac{f(x,y)-f_{target}}{f_{target}}\rvert + w_{g} \times \lvert\frac{g(x,y)-g_{target}}{g_{target}}\rvert
490
+
491
+ with :math:`w_{f}` and :math:`w_{g}` the weights assigned to the distance
492
+ from each target and :math:`f(x,y)` and :math:`g(x,y)` the average training size
493
+ and the number of test paths as a function of the number of total folds and test
494
+ folds.
495
+
496
+ This is a combinatorial problem with :math:`\frac{T\times(T-3)}{2}` combinations,
497
+ with :math:`T` the number of observations.
498
+
499
+ We reduce the search space by using the combinatorial symetry
500
+ :math:`{n \choose k}={n \choose n-k}` and skipping cost computation above 1e5.
501
+
502
+ Parameters
503
+ ----------
504
+ n_observations : int
505
+ Number of observations.
506
+
507
+ target_train_size : int
508
+ The target number of observation in the training set.
509
+
510
+ target_n_test_paths : int
511
+ The target number of test paths (that can be reconstructed from the train/test
512
+ combinations).
513
+
514
+ weight_train_size : float, default=1
515
+ The weight assigned to the distance from the target train size.
516
+ The default value is 1.
517
+
518
+ weight_n_test_paths : float, default=1
519
+ The weight assigned to the distance from the target number of test paths.
520
+ The default value is 1.
521
+
522
+ Returns
523
+ -------
524
+ n_folds : int
525
+ Optimal number of total folds.
526
+
527
+ n_test_folds : int
528
+ Optimal number of test folds.
529
+ """
530
+
531
+ def _cost(
532
+ x: int,
533
+ y: int,
534
+ ) -> float:
535
+ n_test_paths = _n_test_paths(n_folds=x, n_test_folds=y)
536
+ avg_train_size = _avg_train_size(
537
+ n_observations=n_observations, n_folds=x, n_test_folds=y
538
+ )
539
+ return (
540
+ weight_n_test_paths
541
+ * abs(n_test_paths - target_n_test_paths)
542
+ / target_n_test_paths
543
+ + weight_train_size
544
+ * abs(avg_train_size - target_train_size)
545
+ / target_train_size
546
+ )
547
+
548
+ costs = []
549
+ res = []
550
+ for n_folds in range(3, n_observations + 1):
551
+ i = None
552
+ for n_test_folds in range(2, n_folds):
553
+ if i is None or n_folds - n_test_folds <= i:
554
+ cost = _cost(x=n_folds, y=n_test_folds)
555
+ costs.append(cost)
556
+ res.append((n_folds, n_test_folds))
557
+ if i is None and cost > 1e5:
558
+ i = n_test_folds
559
+
560
+ j = np.argmin(costs)
561
+ return res[j]
@@ -29,8 +29,8 @@ class BaseOptimization(skb.BaseEstimator, ABC):
29
29
  portfolio_params : dict, optional
30
30
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
31
31
  `score` methods. If not provided, the `name`, `transaction_costs`,
32
- `management_fees` and `previous_weights` are copied from the optimization
33
- model and systematically passed to the portfolio.
32
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
33
+ optimization model and passed to the portfolio.
34
34
 
35
35
  Attributes
36
36
  ----------
@@ -84,7 +84,12 @@ class BaseOptimization(skb.BaseEstimator, ABC):
84
84
  ptf_kwargs = self.portfolio_params.copy()
85
85
 
86
86
  # Set the default portfolio parameters equal to the optimization parameters
87
- for param in ["transaction_costs", "management_fees", "previous_weights"]:
87
+ for param in [
88
+ "transaction_costs",
89
+ "management_fees",
90
+ "previous_weights",
91
+ "risk_free_rate",
92
+ ]:
88
93
  if param not in ptf_kwargs and hasattr(self, param):
89
94
  ptf_kwargs[param] = getattr(self, param)
90
95
 
@@ -8,6 +8,7 @@
8
8
  # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
9
 
10
10
  from abc import ABC, abstractmethod
11
+ from typing import Any
11
12
 
12
13
  import numpy as np
13
14
  import numpy.typing as npt
@@ -183,8 +184,8 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
183
184
  portfolio_params : dict, optional
184
185
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
185
186
  `score` methods. If not provided, the `name`, `transaction_costs`,
186
- `management_fees` and `previous_weights` are copied from the optimization
187
- model and systematically passed to the portfolio.
187
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
188
+ optimization model and passed to the portfolio.
188
189
 
189
190
  Attributes
190
191
  ----------
@@ -235,7 +236,7 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
235
236
  self,
236
237
  value: float | dict | np.ndarray | list,
237
238
  n_assets: int,
238
- fill_value: any,
239
+ fill_value: Any,
239
240
  name: str,
240
241
  ) -> np.ndarray:
241
242
  """Convert input to cleaned 1D array
@@ -250,7 +251,7 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
250
251
  n_assets : int
251
252
  Number of assets. Used to verify the shape of the converted array.
252
253
 
253
- fill_value : any
254
+ fill_value : Any
254
255
  When `items` is a dictionary, elements that are not in `asset_names` are
255
256
  filled with `fill_value` in the converted array.
256
257
 
@@ -204,8 +204,8 @@ class HierarchicalEqualRiskContribution(BaseHierarchicalOptimization):
204
204
  portfolio_params : dict, optional
205
205
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
206
206
  `score` methods. If not provided, the `name`, `transaction_costs`,
207
- `management_fees` and `previous_weights` are copied from the optimization
208
- model and systematically passed to the portfolio.
207
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
208
+ optimization model and passed to the portfolio.
209
209
 
210
210
  Attributes
211
211
  ----------
@@ -204,8 +204,8 @@ class HierarchicalRiskParity(BaseHierarchicalOptimization):
204
204
  portfolio_params : dict, optional
205
205
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
206
206
  `score` methods. If not provided, the `name`, `transaction_costs`,
207
- `management_fees` and `previous_weights` are copied from the optimization
208
- model and systematically passed to the portfolio.
207
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
208
+ optimization model and passed to the portfolio.
209
209
 
210
210
  Attributes
211
211
  ----------
@@ -9,6 +9,7 @@
9
9
  import warnings
10
10
  from abc import ABC, abstractmethod
11
11
  from enum import auto
12
+ from typing import Any
12
13
 
13
14
  import cvxpy as cp
14
15
  import cvxpy.constraints.constraint as cpc
@@ -403,8 +404,8 @@ class ConvexOptimization(BaseOptimization, ABC):
403
404
  portfolio_params : dict, optional
404
405
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
405
406
  `score` methods. If not provided, the `name`, `transaction_costs`,
406
- `management_fees` and `previous_weights` are copied from the optimization
407
- model and systematically passed to the portfolio.
407
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
408
+ optimization model and passed to the portfolio.
408
409
 
409
410
  Attributes
410
411
  ----------
@@ -575,7 +576,7 @@ class ConvexOptimization(BaseOptimization, ABC):
575
576
  self,
576
577
  value: float | dict | npt.ArrayLike | None,
577
578
  n_assets: int,
578
- fill_value: any,
579
+ fill_value: Any,
579
580
  name: str,
580
581
  ) -> float | np.ndarray:
581
582
  """Convert input to cleaned float or ndarray.
@@ -588,7 +589,7 @@ class ConvexOptimization(BaseOptimization, ABC):
588
589
  n_assets : int
589
590
  Number of assets. Used to verify the shape of the converted array.
590
591
 
591
- fill_value : any
592
+ fill_value : Any
592
593
  When `items` is a dictionary, elements that are not in `asset_names` are
593
594
  filled with `fill_value` in the converted array.
594
595
 
@@ -208,8 +208,8 @@ class DistributionallyRobustCVaR(ConvexOptimization):
208
208
  portfolio_params : dict, optional
209
209
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
210
210
  `score` methods. If not provided, the `name`, `transaction_costs`,
211
- `management_fees` and `previous_weights` are copied from the optimization
212
- model and systematically passed to the portfolio.
211
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
212
+ optimization model and passed to the portfolio.
213
213
 
214
214
  Attributes
215
215
  ----------
@@ -303,8 +303,8 @@ class MaximumDiversification(MeanRisk):
303
303
  portfolio_params : dict, optional
304
304
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
305
305
  `score` methods. If not provided, the `name`, `transaction_costs`,
306
- `management_fees` and `previous_weights` are copied from the optimization
307
- model and systematically passed to the portfolio.
306
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
307
+ optimization model and passed to the portfolio.
308
308
 
309
309
  Attributes
310
310
  ----------
@@ -510,8 +510,8 @@ class MeanRisk(ConvexOptimization):
510
510
  portfolio_params : dict, optional
511
511
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
512
512
  `score` methods. If not provided, the `name`, `transaction_costs`,
513
- `management_fees` and `previous_weights` are copied from the optimization
514
- model and systematically passed to the portfolio.
513
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
514
+ optimization model and passed to the portfolio.
515
515
 
516
516
  Attributes
517
517
  ----------
@@ -340,8 +340,8 @@ class RiskBudgeting(ConvexOptimization):
340
340
  portfolio_params : dict, optional
341
341
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
342
342
  `score` methods. If not provided, the `name`, `transaction_costs`,
343
- `management_fees` and `previous_weights` are copied from the optimization
344
- model and systematically passed to the portfolio.
343
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
344
+ optimization model and passed to the portfolio.
345
345
 
346
346
  Attributes
347
347
  ----------
@@ -31,8 +31,8 @@ class InverseVolatility(BaseOptimization):
31
31
  portfolio_params : dict, optional
32
32
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
33
33
  `score` methods. If not provided, the `name`, `transaction_costs`,
34
- `management_fees` and `previous_weights` are copied from the optimization
35
- model and systematically passed to the portfolio.
34
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
35
+ optimization model and passed to the portfolio.
36
36
 
37
37
  Attributes
38
38
  ----------
@@ -95,8 +95,8 @@ class EqualWeighted(BaseOptimization):
95
95
  portfolio_params : dict, optional
96
96
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
97
97
  `score` methods. If not provided, the `name`, `transaction_costs`,
98
- `management_fees` and `previous_weights` are copied from the optimization
99
- model and systematically passed to the portfolio.
98
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
99
+ optimization model and passed to the portfolio.
100
100
 
101
101
  Attributes
102
102
  ----------
@@ -139,8 +139,8 @@ class Random(BaseOptimization):
139
139
  portfolio_params : dict, optional
140
140
  Portfolio parameters passed to the portfolio evaluated by the `predict` and
141
141
  `score` methods. If not provided, the `name`, `transaction_costs`,
142
- `management_fees` and `previous_weights` are copied from the optimization
143
- model and systematically passed to the portfolio.
142
+ `management_fees`, `previous_weights` and `risk_free_rate` are copied from the
143
+ optimization model and passed to the portfolio.
144
144
 
145
145
  Attributes
146
146
  ----------
@@ -7,6 +7,7 @@ A population is a collection of portfolios.
7
7
  # License: BSD 3 clause
8
8
 
9
9
  import inspect
10
+ from typing import Any
10
11
 
11
12
  import numpy as np
12
13
  import pandas as pd
@@ -15,7 +16,7 @@ import plotly.graph_objects as go
15
16
  import scipy.interpolate as sci
16
17
 
17
18
  import skfolio.typing as skt
18
- from skfolio.portfolio import BasePortfolio, MultiPeriodPortfolio, Portfolio
19
+ from skfolio.portfolio import BasePortfolio, MultiPeriodPortfolio
19
20
  from skfolio.utils.sorting import non_denominated_sort
20
21
  from skfolio.utils.tools import deduplicate_names
21
22
 
@@ -30,14 +31,14 @@ class Population(list):
30
31
 
31
32
  Parameters
32
33
  ----------
33
- iterable : list[Portfolio | MultiPeriodPortfolio]
34
+ iterable : list[BasePortfolio]
34
35
  The list of portfolios. Each item can be of type
35
36
  :class:`~skfolio.portfolio.Portfolio` and/or
36
37
  :class:`~skfolio.portfolio.MultiPeriodPortfolio`.
37
38
  Empty list are accepted.
38
39
  """
39
40
 
40
- def __init__(self, iterable: list[Portfolio | MultiPeriodPortfolio]) -> None:
41
+ def __init__(self, iterable: list[BasePortfolio]) -> None:
41
42
  super().__init__(self._validate_item(item) for item in iterable)
42
43
 
43
44
  def __repr__(self) -> str:
@@ -45,43 +46,43 @@ class Population(list):
45
46
 
46
47
  def __getitem__(
47
48
  self, indices: int | list[int] | slice
48
- ) -> "Portfolio | MultiPeriodPortfolio|Population":
49
+ ) -> "BasePortfolio | Population":
49
50
  item = super().__getitem__(indices)
50
51
  if isinstance(item, list):
51
52
  return self.__class__(item)
52
53
  return item
53
54
 
54
- def __setitem__(self, index: int, item: Portfolio | MultiPeriodPortfolio) -> None:
55
+ def __setitem__(self, index: int, item: BasePortfolio) -> None:
55
56
  super().__setitem__(index, self._validate_item(item))
56
57
 
57
- def __add__(self, other: Portfolio | MultiPeriodPortfolio) -> "Population":
58
+ def __add__(self, other: BasePortfolio) -> "Population":
58
59
  if not isinstance(other, Population):
59
60
  raise TypeError(
60
61
  f"Cannot add a Population with an object of type {type(other)}"
61
62
  )
62
63
  return self.__class__(super().__add__(other))
63
64
 
64
- def insert(self, index, item: Portfolio | MultiPeriodPortfolio) -> None:
65
+ def insert(self, index, item: BasePortfolio) -> None:
65
66
  """Insert portfolio before index."""
66
67
  super().insert(index, self._validate_item(item))
67
68
 
68
- def append(self, item: Portfolio | MultiPeriodPortfolio) -> None:
69
+ def append(self, item: BasePortfolio) -> None:
69
70
  """Append portfolio to the end of the population list."""
70
71
  super().append(self._validate_item(item))
71
72
 
72
- def extend(self, other: Portfolio | MultiPeriodPortfolio) -> None:
73
+ def extend(self, other: BasePortfolio) -> None:
73
74
  """Extend population list by appending elements from the iterable."""
74
75
  if isinstance(other, type(self)):
75
76
  super().extend(other)
76
77
  else:
77
78
  super().extend(self._validate_item(item) for item in other)
78
79
 
79
- def set_portfolio_params(self, **params: any) -> "Population":
80
+ def set_portfolio_params(self, **params: Any) -> "Population":
80
81
  """Set the parameters of all the portfolios.
81
82
 
82
83
  Parameters
83
84
  ----------
84
- **params : any
85
+ **params : Any
85
86
  Portfolio parameters.
86
87
 
87
88
  Returns
@@ -111,13 +112,14 @@ class Population(list):
111
112
 
112
113
  @staticmethod
113
114
  def _validate_item(
114
- item: Portfolio | MultiPeriodPortfolio,
115
- ) -> Portfolio | MultiPeriodPortfolio:
115
+ item: BasePortfolio,
116
+ ) -> BasePortfolio:
116
117
  """Validate that items are of type Portfolio or MultiPeriodPortfolio."""
117
- if isinstance(item, Portfolio | MultiPeriodPortfolio):
118
+ if isinstance(item, BasePortfolio):
118
119
  return item
119
120
  raise TypeError(
120
- "Population only accept items of type Portfolio and MultiPeriodPortfolio"
121
+ "Population only accept items that inherit from BasePortfolio such as "
122
+ "Portfolio or MultiPeriodPortfolio"
121
123
  f", got {type(item).__name__}"
122
124
  )
123
125
 
@@ -324,7 +326,7 @@ class Population(list):
324
326
  q: float,
325
327
  names: skt.Names | None = None,
326
328
  tags: skt.Tags | None = None,
327
- ) -> Portfolio | MultiPeriodPortfolio:
329
+ ) -> BasePortfolio:
328
330
  """Returns the portfolio corresponding to the `q` quantile for a given portfolio
329
331
  measure.
330
332
 
@@ -344,7 +346,7 @@ class Population(list):
344
346
 
345
347
  Returns
346
348
  -------
347
- values : Portfolio | MultiPeriodPortfolio
349
+ values : BasePortfolio
348
350
  Portfolio corresponding to the `q` quantile for the measure.
349
351
  """
350
352
  if not 0 <= q <= 1:
@@ -360,7 +362,7 @@ class Population(list):
360
362
  measure: skt.Measure,
361
363
  names: skt.Names | None = None,
362
364
  tags: skt.Tags | None = None,
363
- ) -> Portfolio | MultiPeriodPortfolio:
365
+ ) -> BasePortfolio:
364
366
  """Returns the portfolio with the minimum measure.
365
367
 
366
368
  Parameters
@@ -376,7 +378,7 @@ class Population(list):
376
378
 
377
379
  Returns
378
380
  -------
379
- values : Portfolio | MultiPeriodPortfolio
381
+ values : BasePortfolio
380
382
  The portfolio with minimum measure.
381
383
  """
382
384
  return self.quantile(measure=measure, q=0, names=names, tags=tags)
@@ -386,7 +388,7 @@ class Population(list):
386
388
  measure: skt.Measure,
387
389
  names: skt.Names | None = None,
388
390
  tags: skt.Tags | None = None,
389
- ) -> Portfolio | MultiPeriodPortfolio:
391
+ ) -> BasePortfolio:
390
392
  """Returns the portfolio with the maximum measure.
391
393
 
392
394
  Parameters
@@ -402,7 +404,7 @@ class Population(list):
402
404
 
403
405
  Returns
404
406
  -------
405
- values : Portfolio | MultiPeriodPortfolio
407
+ values : BasePortfolio
406
408
  The portfolio with maximum measure.
407
409
  """
408
410
  return self.quantile(measure=measure, q=1, names=names, tags=tags)
@@ -156,7 +156,7 @@ class BlackLitterman(BasePrior):
156
156
  check_type=BasePrior,
157
157
  )
158
158
  # fitting prior estimator
159
- self.prior_estimator_.fit(X)
159
+ self.prior_estimator_.fit(X, y)
160
160
 
161
161
  prior_mu = self.prior_estimator_.prior_model_.mu
162
162
  prior_covariance = self.prior_estimator_.prior_model_.covariance
@@ -120,11 +120,11 @@ class EmpiricalPrior(BasePrior):
120
120
  "`is_log_normal` is `False`"
121
121
  )
122
122
  # Expected returns
123
- self.mu_estimator_.fit(X)
123
+ self.mu_estimator_.fit(X, y)
124
124
  mu = self.mu_estimator_.mu_
125
125
 
126
126
  # Covariance
127
- self.covariance_estimator_.fit(X)
127
+ self.covariance_estimator_.fit(X, y)
128
128
  covariance = self.covariance_estimator_.covariance_
129
129
  else:
130
130
  if self.investment_horizon is None:
@@ -134,14 +134,15 @@ class EmpiricalPrior(BasePrior):
134
134
  )
135
135
  # Convert linear returns to log returns
136
136
  X_log = np.log(1 + X)
137
+ y_log = np.log(1 + y) if y is not None else None
137
138
 
138
139
  # Estimates the moments on the log returns
139
140
  # Expected returns
140
- self.mu_estimator_.fit(X_log)
141
+ self.mu_estimator_.fit(X_log, y_log)
141
142
  mu = self.mu_estimator_.mu_
142
143
 
143
144
  # Covariance
144
- self.covariance_estimator_.fit(X_log)
145
+ self.covariance_estimator_.fit(X_log, y_log)
145
146
  covariance = self.covariance_estimator_.covariance_
146
147
 
147
148
  # Using the property of aggregation across time we scale this distribution
@@ -9,6 +9,7 @@
9
9
  # Grisel Licensed under BSD 3 clause.
10
10
 
11
11
  from abc import ABC, abstractmethod
12
+ from typing import Any
12
13
 
13
14
  import numpy as np
14
15
  import numpy.typing as npt
@@ -196,7 +197,7 @@ class FactorModel(BasePrior):
196
197
  self.max_iteration = max_iteration
197
198
 
198
199
  # noinspection PyMethodOverriding, PyPep8Naming
199
- def fit(self, X: npt.ArrayLike, y: any):
200
+ def fit(self, X: npt.ArrayLike, y: Any):
200
201
  """Fit the Factor Model estimator.
201
202
 
202
203
  Parameters
@@ -10,6 +10,7 @@
10
10
  from collections.abc import Callable, Iterator
11
11
  from enum import Enum
12
12
  from functools import wraps
13
+ from typing import Any
13
14
 
14
15
  import numpy as np
15
16
  import numpy.typing as npt
@@ -41,7 +42,7 @@ class AutoEnum(str, Enum):
41
42
 
42
43
  @staticmethod
43
44
  def _generate_next_value_(
44
- name: str, start: int, count: int, last_values: any
45
+ name: str, start: int, count: int, last_values: Any
45
46
  ) -> str:
46
47
  """Overriding `auto()`"""
47
48
  return name.lower()
@@ -179,7 +180,7 @@ def args_names(func: object) -> list[str]:
179
180
 
180
181
 
181
182
  def check_estimator(
182
- estimator: skb.BaseEstimator | None, default: skb.BaseEstimator, check_type: any
183
+ estimator: skb.BaseEstimator | None, default: skb.BaseEstimator, check_type: Any
183
184
  ):
184
185
  """Check the estimator type and returns its cloned version it provided, otherwise
185
186
  return the default estimator.
@@ -192,7 +193,7 @@ def check_estimator(
192
193
  default : BaseEstimator
193
194
  Default estimator to return when `estimator` is `None`.
194
195
 
195
- check_type : any
196
+ check_type : Any
196
197
  Expected type of the estimator to check against.
197
198
 
198
199
  Returns
@@ -211,7 +212,7 @@ def check_estimator(
211
212
  def input_to_array(
212
213
  items: dict | npt.ArrayLike,
213
214
  n_assets: int,
214
- fill_value: any,
215
+ fill_value: Any,
215
216
  dim: int,
216
217
  assets_names: np.ndarray | None,
217
218
  name: str,
@@ -228,7 +229,7 @@ def input_to_array(
228
229
  Expected number of assets.
229
230
  Used to verify the shape of the converted array.
230
231
 
231
- fill_value : any
232
+ fill_value : Any
232
233
  When `items` is a dictionary, elements that are not in `asset_names` are filled
233
234
  with `fill_value` in the converted array.
234
235
 
@@ -424,7 +425,7 @@ def safe_split(
424
425
 
425
426
 
426
427
  def fit_single_estimator(
427
- estimator: any,
428
+ estimator: Any,
428
429
  X: npt.ArrayLike,
429
430
  y: npt.ArrayLike | None = None,
430
431
  indices: np.ndarray | None = None,
@@ -463,7 +464,7 @@ def fit_single_estimator(
463
464
 
464
465
 
465
466
  def fit_and_predict(
466
- estimator: any,
467
+ estimator: Any,
467
468
  X: npt.ArrayLike,
468
469
  y: npt.ArrayLike | None,
469
470
  train: np.ndarray,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: skfolio
3
- Version: 0.1.3
3
+ Version: 0.2.1
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>
@@ -85,37 +85,37 @@ Requires-Dist: sphinx-favicon; extra == "docs"
85
85
 
86
86
  .. -*- mode: rst -*-
87
87
 
88
- |Licence|_ |Codecov|_ |Black|_ |PythonVersion|_ |PyPi|_ |CI/CD|_ |Downloads|_ |Ruff|_ |Contribution|_ |Website|_
88
+ |Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website|
89
89
 
90
90
  .. |Licence| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
91
- .. _Licence: https://github.com/skfolio/skfolio/blob/main/LICENSE
91
+ :target: https://github.com/skfolio/skfolio/blob/main/LICENSE
92
92
 
93
93
  .. |Codecov| image:: https://codecov.io/gh/skfolio/skfolio/graph/badge.svg?token=KJ0SE4LHPV
94
- .. _Codecov: https://codecov.io/gh/skfolio/skfolio
94
+ :target: https://codecov.io/gh/skfolio/skfolio
95
95
 
96
- .. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue
97
- .. _PythonVersion: https://pypi.org/project/skfolio/
96
+ .. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg
97
+ :target: https://pypi.org/project/skfolio/
98
98
 
99
99
  .. |PyPi| image:: https://img.shields.io/pypi/v/skfolio
100
- .. _PyPi: https://pypi.org/project/skfolio
100
+ :target: https://pypi.org/project/skfolio
101
101
 
102
102
  .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
103
- .. _Black: https://github.com/psf/black
103
+ :target: https://github.com/psf/black
104
104
 
105
- .. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml?logo=github
106
- .. _CI/CD: https://github.com/skfolio/skfolio/raw/main/LICENSE
105
+ .. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml.svg?logo=github
106
+ :target: https://github.com/skfolio/skfolio/raw/main/LICENSE
107
107
 
108
108
  .. |Downloads| image:: https://static.pepy.tech/badge/skfolio
109
- .. _Downloads: https://pepy.tech/project/skfolio
109
+ :target: https://pepy.tech/project/skfolio
110
110
 
111
111
  .. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
112
- .. _Ruff: https://github.com/astral-sh/ruff
112
+ :target: https://github.com/astral-sh/ruff
113
113
 
114
114
  .. |Contribution| image:: https://img.shields.io/badge/Contributions-Welcome-blue
115
- .. _Contribution: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
115
+ :target: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
116
116
 
117
- .. |Website| image:: https://img.shields.io/website-up-down-53cc0d-red/http/skfolio.org
118
- .. _Website: https://skfolio.org
117
+ .. |Website| image:: https://img.shields.io/website.svg?down_color=red&down_message=down&up_color=53cc0d&up_message=up&url=https://skfolio.org
118
+ :target: https://skfolio.org
119
119
 
120
120
  .. |PythonMinVersion| replace:: 3.10
121
121
  .. |NumpyMinVersion| replace:: 1.23.4
@@ -564,7 +564,7 @@ Black & Litterman Factor Model
564
564
  ------------------------------
565
565
  .. code-block:: python
566
566
 
567
- factor_views = ["MTUM - QUAL == 0.03 ", "SIZE - TLT == 0.04", "VLUE == 0.06"]
567
+ factor_views = ["MTUM - QUAL == 0.03 ", "VLUE == 0.06"]
568
568
  model = MeanRisk(
569
569
  objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
570
570
  prior_estimator=FactorModel(
File without changes
File without changes
File without changes
File without changes
File without changes