skfolio 0.9.1__py3-none-any.whl → 0.10.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 (41) hide show
  1. skfolio/distribution/multivariate/_vine_copula.py +35 -34
  2. skfolio/distribution/univariate/_base.py +20 -15
  3. skfolio/exceptions.py +5 -0
  4. skfolio/measures/__init__.py +2 -0
  5. skfolio/measures/_measures.py +390 -155
  6. skfolio/optimization/_base.py +21 -4
  7. skfolio/optimization/cluster/hierarchical/_base.py +16 -13
  8. skfolio/optimization/cluster/hierarchical/_herc.py +6 -6
  9. skfolio/optimization/cluster/hierarchical/_hrp.py +8 -6
  10. skfolio/optimization/convex/_base.py +238 -144
  11. skfolio/optimization/convex/_distributionally_robust.py +32 -20
  12. skfolio/optimization/convex/_maximum_diversification.py +15 -15
  13. skfolio/optimization/convex/_mean_risk.py +26 -24
  14. skfolio/optimization/convex/_risk_budgeting.py +23 -21
  15. skfolio/optimization/ensemble/__init__.py +2 -4
  16. skfolio/optimization/ensemble/_stacking.py +1 -1
  17. skfolio/optimization/naive/_naive.py +2 -2
  18. skfolio/population/_population.py +30 -9
  19. skfolio/portfolio/_base.py +68 -26
  20. skfolio/portfolio/_multi_period_portfolio.py +5 -0
  21. skfolio/portfolio/_portfolio.py +5 -0
  22. skfolio/prior/__init__.py +6 -2
  23. skfolio/prior/_base.py +7 -3
  24. skfolio/prior/_black_litterman.py +14 -12
  25. skfolio/prior/_empirical.py +8 -7
  26. skfolio/prior/_entropy_pooling.py +1493 -0
  27. skfolio/prior/_factor_model.py +39 -22
  28. skfolio/prior/_opinion_pooling.py +475 -0
  29. skfolio/prior/_synthetic_data.py +10 -8
  30. skfolio/uncertainty_set/_bootstrap.py +4 -4
  31. skfolio/uncertainty_set/_empirical.py +6 -6
  32. skfolio/utils/equations.py +10 -4
  33. skfolio/utils/figure.py +185 -0
  34. skfolio/utils/tools.py +4 -2
  35. {skfolio-0.9.1.dist-info → skfolio-0.10.0.dist-info}/METADATA +94 -5
  36. {skfolio-0.9.1.dist-info → skfolio-0.10.0.dist-info}/RECORD +40 -38
  37. {skfolio-0.9.1.dist-info → skfolio-0.10.0.dist-info}/WHEEL +1 -1
  38. skfolio/synthetic_returns/__init__.py +0 -1
  39. /skfolio/{optimization/ensemble/_base.py → utils/composition.py} +0 -0
  40. {skfolio-0.9.1.dist-info → skfolio-0.10.0.dist-info}/licenses/LICENSE +0 -0
  41. {skfolio-0.9.1.dist-info → skfolio-0.10.0.dist-info}/top_level.txt +0 -0
@@ -124,11 +124,11 @@ class EmpiricalMuUncertaintySet(BaseMuUncertaintySet):
124
124
  # fitting estimators
125
125
  self.prior_estimator_.fit(X, y, **routed_params.prior_estimator.fit)
126
126
 
127
- prior_model = self.prior_estimator_.prior_model_
128
- n_observations, n_assets = prior_model.returns.shape
127
+ return_distribution = self.prior_estimator_.return_distribution_
128
+ n_observations, n_assets = return_distribution.returns.shape
129
129
  k = np.sqrt(st.chi2.ppf(q=self.confidence_level, df=n_assets))
130
130
 
131
- sigma = prior_model.covariance / n_observations
131
+ sigma = return_distribution.covariance / n_observations
132
132
  if self.diagonal:
133
133
  sigma = np.diag(np.diag(sigma))
134
134
 
@@ -239,11 +239,11 @@ class EmpiricalCovarianceUncertaintySet(BaseCovarianceUncertaintySet):
239
239
  # fitting estimators
240
240
  self.prior_estimator_.fit(X, y, **routed_params.prior_estimator.fit)
241
241
 
242
- prior_model = self.prior_estimator_.prior_model_
243
- n_observations, n_assets = prior_model.returns.shape
242
+ return_distribution = self.prior_estimator_.return_distribution_
243
+ n_observations, n_assets = return_distribution.returns.shape
244
244
  k = np.sqrt(st.chi2.ppf(q=self.confidence_level, df=n_assets**2))
245
245
 
246
- sigma = prior_model.covariance / n_observations
246
+ sigma = return_distribution.covariance / n_observations
247
247
  if self.diagonal:
248
248
  sigma = np.diag(np.diag(sigma))
249
249
 
@@ -103,6 +103,8 @@ def equations_to_matrix(
103
103
  groups = _validate_groups(groups, name=names[0])
104
104
  equations = _validate_equations(equations, name=names[1])
105
105
 
106
+ n_groups, n_assets = groups.shape
107
+
106
108
  a_equality = []
107
109
  b_equality = []
108
110
 
@@ -127,10 +129,14 @@ def equations_to_matrix(
127
129
  raise
128
130
  warnings.warn(str(e), stacklevel=2)
129
131
  return (
130
- np.array(a_equality),
131
- np.array(b_equality),
132
- np.array(a_inequality),
133
- np.array(b_inequality),
132
+ np.array(a_equality, dtype=float)
133
+ if a_equality
134
+ else np.empty((0, n_assets), dtype=float),
135
+ np.array(b_equality, dtype=float),
136
+ np.array(a_inequality, dtype=float)
137
+ if a_inequality
138
+ else np.empty((0, n_assets), dtype=float),
139
+ np.array(b_inequality, dtype=float),
134
140
  )
135
141
 
136
142
 
@@ -0,0 +1,185 @@
1
+ """Figure module."""
2
+
3
+ # Copyright (c) 2023
4
+ # Author: Hugo Delatte <delatte.hugo@gmail.com>
5
+ # SPDX-License-Identifier: BSD-3-Clause
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+ import plotly.express as px
10
+ import plotly.graph_objects as go
11
+ import scipy.stats as st
12
+
13
+
14
+ def plot_kde_distributions(
15
+ X: pd.DataFrame,
16
+ sample_weight: np.ndarray | None = None,
17
+ percentile_cutoff: float | None = None,
18
+ title: str = "Distribution of Asset Returns",
19
+ unweighted_suffix: str = "",
20
+ weighted_suffix: str = "with Sample Weight",
21
+ ) -> go.Figure:
22
+ """
23
+ Plot the Kernel Density Estimate (KDE) of return distributions for multiple assets.
24
+
25
+ Parameters
26
+ ----------
27
+ X : DataFrame of shape (n_observations, n_assets)
28
+ Return data where each column corresponds to an asset and each row to an
29
+ observation.
30
+
31
+ sample_weight : ndarray of shape (n_observations,), optional
32
+ Weights to apply to each observation when computing the KDE.
33
+ If None, equal weighting is used.
34
+
35
+ percentile_cutoff : float, default=None
36
+ Percentile cutoff for tail truncation (percentile), in percent.
37
+ - If a float p is provided, the distribution support is truncated at
38
+ the p-th and (100 - p)-th percentiles.
39
+ - If None, no truncation is applied (uses full min/max of returns).
40
+
41
+ title : str, default="Distribution of Asset Returns"
42
+ Title for the Plotly figure.
43
+
44
+ unweighted_suffix : str, default=""
45
+ Suffix to append to asset names for unweighted KDE traces.
46
+
47
+ weighted_suffix : str, default="weighted"
48
+ Suffix to append to asset names for weighted KDE traces.
49
+
50
+ Returns
51
+ -------
52
+ fig : plotly.graph_objects.Figure
53
+ A Plotly Figure object containing overlaid KDE plots for each asset,
54
+ with separate traces for weighted and unweighted distributions if weights
55
+ are provided.
56
+ """
57
+ asset_names = X.columns.tolist()
58
+ X = X.values
59
+ colors = px.colors.qualitative.Plotly
60
+
61
+ traces: list[go.Scatter] = []
62
+
63
+ for i, asset in enumerate(asset_names):
64
+ x = X[:, i]
65
+ color = colors[i % len(colors)]
66
+ visible = True if i == 0 else "legendonly"
67
+
68
+ # Unweighted: solid line
69
+ traces.append(
70
+ kde_trace(
71
+ x=x,
72
+ sample_weight=None,
73
+ percentile_cutoff=percentile_cutoff,
74
+ name=f"{asset} {unweighted_suffix}".strip(),
75
+ line_color=color,
76
+ fill_opacity=0.17,
77
+ line_dash="solid",
78
+ line_width=1,
79
+ visible=visible,
80
+ )
81
+ )
82
+
83
+ # Weighted: dashed, thicker line
84
+ if sample_weight is not None:
85
+ traces.append(
86
+ kde_trace(
87
+ x=x,
88
+ sample_weight=sample_weight,
89
+ percentile_cutoff=percentile_cutoff,
90
+ name=f"{asset} {weighted_suffix}".strip(),
91
+ line_color=color,
92
+ fill_opacity=0.17,
93
+ line_dash="dash",
94
+ line_width=1.5,
95
+ visible=visible,
96
+ )
97
+ )
98
+
99
+ fig = go.Figure(data=traces)
100
+ fig.update_layout(
101
+ title=title,
102
+ xaxis_title="Returns",
103
+ yaxis_title="Probability Density",
104
+ )
105
+ fig.update_xaxes(tickformat=".0%")
106
+ return fig
107
+
108
+
109
+ def kde_trace(
110
+ x: np.ndarray,
111
+ sample_weight: np.ndarray | None,
112
+ percentile_cutoff: float | None,
113
+ name: str,
114
+ line_color: str,
115
+ fill_opacity: float,
116
+ line_dash: str,
117
+ line_width: float,
118
+ visible: bool,
119
+ ) -> go.Scatter:
120
+ """
121
+ Create a Plotly Scatter trace representing a Gaussian kernel density estimate (KDE),
122
+ with customizable line style and fill opacity.
123
+
124
+ Parameters
125
+ ----------
126
+ x : ndarray of shape (n_observations,)
127
+ One-dimensional array of sample values for which the KDE is computed.
128
+
129
+ sample_weight : ndarray of shape (n_observations,), optional
130
+ Weights to apply to each observation when computing the KDE.
131
+ If None, equal weighting is used.
132
+
133
+ percentile_cutoff : float, default=None
134
+ Percentile cutoff for tail truncation (percentile), in percent.
135
+ - If a float p is provided, the distribution support is truncated at
136
+ the p-th and (100 - p)-th percentiles.
137
+ - If None, no truncation is applied (uses full min/max of returns).
138
+
139
+ name : str
140
+ Legend name for this trace.
141
+
142
+ line_color : str
143
+ Color of the KDE line (hex or named CSS color).
144
+
145
+ fill_opacity : float
146
+ Opacity of the filled area under the curve (0 to 1).
147
+
148
+ line_dash : str
149
+ Dash style for the line ("solid", "dash", "dot", etc.).
150
+
151
+ line_width : float
152
+ Width of the line.
153
+
154
+ visible : bool
155
+ Initial visibility of the trace in the Plotly figure.
156
+
157
+ Returns
158
+ -------
159
+ go.Scatter
160
+ A Plotly Scatter trace with the KDE line and shaded area under the curve.
161
+ """
162
+ if percentile_cutoff is None:
163
+ lower, upper = x.min(), x.max()
164
+ else:
165
+ lower = np.percentile(x, percentile_cutoff)
166
+ upper = np.percentile(x, 100.0 - percentile_cutoff)
167
+
168
+ xs = np.linspace(lower, upper, 500)
169
+ ys = st.gaussian_kde(x, weights=sample_weight)(xs)
170
+
171
+ # build RGBA fill color from the line_color hex
172
+ r, g, b = tuple(int(line_color.lstrip("#")[i : i + 2], 16) for i in (0, 2, 4))
173
+ fill_color = f"rgba({r},{g},{b},{fill_opacity})"
174
+
175
+ return go.Scatter(
176
+ x=xs,
177
+ y=ys,
178
+ mode="lines",
179
+ name=name,
180
+ visible=visible,
181
+ line=dict(color=line_color, dash=line_dash, width=line_width),
182
+ fill="tozeroy",
183
+ fillcolor=fill_color,
184
+ opacity=1.0,
185
+ )
skfolio/utils/tools.py CHANGED
@@ -326,7 +326,9 @@ def args_names(func: object) -> list[str]:
326
326
 
327
327
 
328
328
  def check_estimator(
329
- estimator: skb.BaseEstimator | None, default: skb.BaseEstimator, check_type: Any
329
+ estimator: skb.BaseEstimator | None,
330
+ default: skb.BaseEstimator | None,
331
+ check_type: Any,
330
332
  ):
331
333
  """Check the estimator type and returns its cloned version it provided, otherwise
332
334
  return the default estimator.
@@ -336,7 +338,7 @@ def check_estimator(
336
338
  estimator : BaseEstimator, optional
337
339
  Estimator.
338
340
 
339
- default : BaseEstimator
341
+ default : BaseEstimator, optional
340
342
  Default estimator to return when `estimator` is `None`.
341
343
 
342
344
  check_type : Any
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skfolio
3
- Version: 0.9.1
3
+ Version: 0.10.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>
@@ -92,7 +92,7 @@ Dynamic: license-file
92
92
 
93
93
  .. -*- mode: rst -*-
94
94
 
95
- |Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website| |JupyterLite|
95
+ |Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website| |JupyterLite| |Discord|
96
96
 
97
97
  .. |Licence| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
98
98
  :target: https://github.com/skfolio/skfolio/blob/main/LICENSE
@@ -127,6 +127,9 @@ Dynamic: license-file
127
127
  .. |JupyterLite| image:: https://jupyterlite.rtfd.io/en/latest/_static/badge.svg
128
128
  :target: https://skfolio.org/lite
129
129
 
130
+ .. |Discord| image:: https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white
131
+ :target: https://discord.gg/Bu7EtNYugS
132
+
130
133
  .. |PythonMinVersion| replace:: 3.10
131
134
  .. |NumpyMinVersion| replace:: 1.23.4
132
135
  .. |ScipyMinVersion| replace:: 1.8.0
@@ -281,6 +284,8 @@ Available models
281
284
  * Black & Litterman
282
285
  * Factor Model
283
286
  * Synthetic Data (Stress Test, Factor Stress Test)
287
+ * Entropy Pooling
288
+ * Opinion Pooling
284
289
 
285
290
  * Uncertainty Set Estimator:
286
291
  * On Expected Returns:
@@ -296,6 +301,7 @@ Available models
296
301
  * Drop Highly Correlated Assets
297
302
  * Select Non-Expiring Assets
298
303
  * Select Complete Assets (handle late inception, delisting, etc.)
304
+ * Drop Zero Variance
299
305
 
300
306
  * Cross-Validation and Model Selection:
301
307
  * Compatible with all `sklearn` methods (KFold, etc.)
@@ -385,7 +391,14 @@ Imports
385
391
  )
386
392
  from skfolio.pre_selection import SelectKExtremes
387
393
  from skfolio.preprocessing import prices_to_returns
388
- from skfolio.prior import BlackLitterman, EmpiricalPrior, FactorModel, SyntheticData
394
+ from skfolio.prior import (
395
+ BlackLitterman,
396
+ EmpiricalPrior,
397
+ EntropyPooling,
398
+ FactorModel,
399
+ OpinionPooling,
400
+ SyntheticData,
401
+ )
389
402
  from skfolio.uncertainty_set import BootstrapMuUncertaintySet
390
403
 
391
404
  Load Dataset
@@ -701,8 +714,84 @@ Factor Stress Test
701
714
  conditioning={"QUAL": -0.5}
702
715
  ))
703
716
  factor_model.fit(X,y)
704
- stressed_X = factor_model.prior_model_.returns
705
- stressed_ptf = model.predict(stressed_X)
717
+ stressed_dist = factor_model.return_distribution_
718
+ stressed_ptf = model.predict(stressed_dist)
719
+
720
+ Entropy Pooling
721
+ ---------------
722
+ .. code-block:: python
723
+
724
+ entropy_pooling = EntropyPooling(
725
+ mean_views=[
726
+ "JPM == -0.002",
727
+ "PG >= LLY",
728
+ "BAC >= prior(BAC) * 1.2",
729
+ ],
730
+ cvar_views=[
731
+ "GE == 0.08",
732
+ ],
733
+ )
734
+ entropy_pooling.fit(X)
735
+ print(entropy_pooling.relative_entropy_)
736
+ print(entropy_pooling.effective_number_of_scenarios_)
737
+ print(entropy_pooling.return_distribution_.sample_weight)
738
+
739
+ CVaR Hierarchical Risk Parity optimization on Entropy Pooling
740
+ -------------------------------------------------------------
741
+ .. code-block:: python
742
+
743
+ entropy_pooling = EntropyPooling(cvar_views=["GE == 0.08"])
744
+ model = HierarchicalRiskParity(
745
+ risk_measure=RiskMeasure.CVAR,
746
+ prior_estimator=entropy_pooling
747
+ )
748
+ model.fit(X)
749
+ print(model.weights_)
750
+
751
+ Stress Test with Entropy Pooling on Factor Synthetic Data
752
+ ---------------------------------------------------------
753
+ .. code-block:: python
754
+
755
+ # Regular Vine Copula and sampling of 100,000 synthetic factor returns
756
+ factor_synth = SyntheticData(
757
+ n_samples=100_000,
758
+ distribution_estimator=VineCopula(log_transform=True, n_jobs=-1, random_state=0)
759
+ )
760
+
761
+ # Entropy Pooling by imposing a CVaR-95% of 10% on the Quality factor
762
+ factor_entropy_pooling = EntropyPooling(
763
+ prior_estimator=factor_synth,
764
+ cvar_views=["QUAL == 0.10"],
765
+ )
766
+
767
+ factor_entropy_pooling.fit(X, factors)
768
+
769
+ # We retrieve the stressed distribution:
770
+ stressed_dist = factor_model.return_distribution_
771
+
772
+ # We stress-test our portfolio:
773
+ stressed_ptf = model.predict(stressed_dist)
774
+
775
+ Opinion Pooling
776
+ ---------------
777
+ .. code-block:: python
778
+
779
+ # We consider two expert opinions, each generated via Entropy Pooling with
780
+ # user-defined views.
781
+ # We assign probabilities of 40% to Expert 1, 50% to Expert 2, and by default
782
+ # the remaining 10% is allocated to the prior distribution:
783
+ opinion_1 = EntropyPooling(cvar_views=["AMD == 0.10"])
784
+ opinion_2 = EntropyPooling(
785
+ mean_views=["AMD >= BAC", "JPM <= prior(JPM) * 0.8"],
786
+ cvar_views=["GE == 0.12"],
787
+ )
788
+
789
+ opinion_pooling = OpinionPooling(
790
+ estimators=[("opinion_1", opinion_1), ("opinion_2", opinion_2)],
791
+ opinion_probabilities=[0.4, 0.5],
792
+ )
793
+
794
+ opinion_pooling.fit(X)
706
795
 
707
796
 
708
797
  Recognition
@@ -1,5 +1,5 @@
1
1
  skfolio/__init__.py,sha256=XdSV1bcfft5pNl5Y_mX8MR0IzjXjRs8uRURp42UGa08,635
2
- skfolio/exceptions.py,sha256=omi5qQiEuFDpIoZfQHQxORZRcKYkusmdGPLBJt-Sna0,805
2
+ skfolio/exceptions.py,sha256=4k4cFgV0qCAovDKe0XqthzKs9SX871BTv1GKfX00ovc,880
3
3
  skfolio/typing.py,sha256=5wnu_qoGZtCWKu-nHlZ5w3rOKy5CXxGI5ZvzDSR9pLU,1394
4
4
  skfolio/cluster/__init__.py,sha256=ycySaq2MgG3etqNF-pITuYKfYPHYm3-frjFc8PRzMc0,267
5
5
  skfolio/cluster/_hierarchical.py,sha256=PTtr6H4keY6DEVvXyYM24AnNjj72sNaXKjGFEyMXZ5c,12839
@@ -27,17 +27,17 @@ skfolio/distribution/copula/_utils.py,sha256=drMtv71bkwlerR0HJCdNCZTuFSitN5vn33a
27
27
  skfolio/distribution/multivariate/__init__.py,sha256=E9AR0Hh5wWShOTwj62R1RVMkzZpXc5Ams4ppibwhrUY,339
28
28
  skfolio/distribution/multivariate/_base.py,sha256=MV3rhTafPlKdb3wuLbHfhApyV1ll7WmfzdR97Dq8VZw,8716
29
29
  skfolio/distribution/multivariate/_utils.py,sha256=WNL1lzO0Ki5x_yO8p3GRKrXwG4fK99je7sDQ3avyUQ8,19274
30
- skfolio/distribution/multivariate/_vine_copula.py,sha256=b545CKAMXUXMaIiI7hQ-P8jHhmZ4hUnGo2F22uWtbac,49756
30
+ skfolio/distribution/multivariate/_vine_copula.py,sha256=2SPUwDthWe8pcDpXMZ6dqLMjpI0HGdkguP0sziOlEYc,50099
31
31
  skfolio/distribution/univariate/__init__.py,sha256=m9vZUhZyRUT5IOQRixGPdGci1wtC5ua8RWtHsC8HAlU,628
32
- skfolio/distribution/univariate/_base.py,sha256=8oPMOdQi2wHc-UOWG18x_eZ57G_ksQhPM9C0ncYf1Lg,9874
32
+ skfolio/distribution/univariate/_base.py,sha256=8uleYSiCJ4n6X1uI0ju5f4l0iLvJcyS3Gncxh7yyH90,10096
33
33
  skfolio/distribution/univariate/_gaussian.py,sha256=pe8YxTQjvObeVeZD2YXduN5M-k2kNNTy2q0AvYCm1n4,4274
34
34
  skfolio/distribution/univariate/_johnson_su.py,sha256=Dl1WyCmn-sRE4BrckVNGXHz9biDQtXyPq1JXEPKIHBo,4857
35
35
  skfolio/distribution/univariate/_normal_inverse_gaussian.py,sha256=oq5omNUQanFWBGaYSNwf9YDa6c-B1j9ZErq6p96resc,4983
36
36
  skfolio/distribution/univariate/_selection.py,sha256=6KL4gngiLKwaBUpCDX19ABOkMBzZp1YVRnXFrUtppCs,3110
37
37
  skfolio/distribution/univariate/_student_t.py,sha256=GcI4fKp6q5XegfvT_i3AqfWlUMxCq7A5sX6Xsf4pye8,4553
38
- skfolio/measures/__init__.py,sha256=lB5xBqEFU-8x-12AA1VdCHaPwYpfwvejRaiYnr8IGYg,1647
38
+ skfolio/measures/__init__.py,sha256=saJ3tGQyZ5MRtwnj0tVS9L4yvnLJVSdyXVyH5_FyoNo,1683
39
39
  skfolio/measures/_enums.py,sha256=S6WOT8NHzm-eMHELuOjngIBupCctCdiTA2BaJlWl-4E,8956
40
- skfolio/measures/_measures.py,sha256=LmKgSAOXaKGomAcO1FkeypD6tRiEeDLUIh6lySky4vs,16835
40
+ skfolio/measures/_measures.py,sha256=ysON3rPfcLPSAPY3T1MfkuLPoKU__KN5sRyCzOXe6to,28716
41
41
  skfolio/metrics/__init__.py,sha256=ebu5h7Q9X0f3ZZ1VFmAEBPic2sirboKG_zNBHO5abjo,98
42
42
  skfolio/metrics/_scorer.py,sha256=L-qct4cby15a4xC4arSaG5__1mxBCQYeMjlrHBIVnSY,4325
43
43
  skfolio/model_selection/__init__.py,sha256=BT8VCXW7C4bXI2Oam4amTHOcJVlKxLpkcsHjB63pZHQ,524
@@ -64,30 +64,29 @@ skfolio/moments/expected_returns/_equilibrium_mu.py,sha256=A4zAYZ7ex2Y68YV0HajYD
64
64
  skfolio/moments/expected_returns/_ew_mu.py,sha256=vDOqpTpTY3iaJc9PfMU_dpdfglT1dJ_DuM3pCTpjHpc,2125
65
65
  skfolio/moments/expected_returns/_shrunk_mu.py,sha256=nqypZJweZIf6u3Idz-TLPHiD3h3XzuKgTEQWJHSVnwo,8292
66
66
  skfolio/optimization/__init__.py,sha256=LA4n85e-wVTeRNI-NlTU1ID5FhP3-B410kmsh9268Ho,1049
67
- skfolio/optimization/_base.py,sha256=lPVvoV36URnjMUJGOO23QbaFYiuyVA9oRuvk68Dwd7o,5780
67
+ skfolio/optimization/_base.py,sha256=tbD-HiulBXHMAbrMGq8K62qUxVHu6uf1Cl0pT34VyIc,6351
68
68
  skfolio/optimization/cluster/__init__.py,sha256=nxsuDxviDbj-YMHhQXIkUEWUoKPhPn10bQ0_nULNUoE,424
69
69
  skfolio/optimization/cluster/_nco.py,sha256=Gbd18HYlwq_MUd9JmytM1-Uqu-GFT8NXb8QWPVgmDxk,16433
70
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
71
+ skfolio/optimization/cluster/hierarchical/_base.py,sha256=2YH9m_L_NFdBfhPso-WVCx-Meiy_bCNYVukt72nZHno,16480
72
+ skfolio/optimization/cluster/hierarchical/_herc.py,sha256=3Mwfe6PGWzWWWpOYnfLTWAmeNcYE1d3eh1uXB9hqAyM,20476
73
+ skfolio/optimization/cluster/hierarchical/_hrp.py,sha256=pNr2e2COZIi9jj5IEddT4a79BCD0VjwbPeXHRRSLdoI,18287
74
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=hYnzmHjVrc0gGIrGsMuc0ptGqKJNSU5t16DIjSErYHM,19529
78
- skfolio/optimization/convex/_mean_risk.py,sha256=Puj-OUBwV-0y4vbNwwNVKTQqf1YuwQxqf8pknOTZ3ec,50105
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
75
+ skfolio/optimization/convex/_base.py,sha256=gIFIF5DCIDPL8FeXaipdZBbkiUHP55MsWfyc8DVQ6ng,92190
76
+ skfolio/optimization/convex/_distributionally_robust.py,sha256=BKMp718PqG02t2qQ8ejgCSuY18ZNA_4y05RxLV16OJs,18332
77
+ skfolio/optimization/convex/_maximum_diversification.py,sha256=QBHPRsAHjR4zpxJvvaKoKI0dMMTTHMlRRquB_sOWNuU,19569
78
+ skfolio/optimization/convex/_mean_risk.py,sha256=ZO2CXMeb9uOiOQVunuUmJcgsWj-uwaPw_PqZNmOV-Mk,50287
79
+ skfolio/optimization/convex/_risk_budgeting.py,sha256=jZtQXfXF1iIVTIiV9fphW4xam36qKylHmdfIgJtdG5o,23803
80
+ skfolio/optimization/ensemble/__init__.py,sha256=8YKBsrAMjyAaUWRbDG_ciNuyj0mtSPmn85ms8Afvm90,219
81
+ skfolio/optimization/ensemble/_stacking.py,sha256=i7tAHjzydb75o2xdgzV2lkqpkhEqpwsugXJjCFyV8rY,14182
83
82
  skfolio/optimization/naive/__init__.py,sha256=1QKgOuA6DoqKVOsJxWKogaGPyOir6ln-aQ28PTAbtJs,181
84
- skfolio/optimization/naive/_naive.py,sha256=w5lDVpn5YeO6NN5dRzSRvj-V6GpfLogOW7FVxevwZl8,6453
83
+ skfolio/optimization/naive/_naive.py,sha256=TRwQO7stD8hVg5OEe_QVpBgdcLaT_jUZco2lf5skYqo,6469
85
84
  skfolio/population/__init__.py,sha256=ehKwWhDJCifjhEL-QezVR0xYjzRTeyHbrEMbfWjF9cU,106
86
- skfolio/population/_population.py,sha256=5HNSubjalwKWeukaPPGH8LKgQ9ICpcVFp4h_MtiUhak,31332
85
+ skfolio/population/_population.py,sha256=0mXy1yXNCZWeaYUu9tzRifv-NSttcy_nSlh-44PJiz4,32048
87
86
  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
87
+ skfolio/portfolio/_base.py,sha256=1fzDIYpgz8GIDDySfAO32RN-2IX6waT6vZS6CzvkLT8,42239
88
+ skfolio/portfolio/_multi_period_portfolio.py,sha256=EI0LeJ3DokDUnumNv_Q1-P3wykjeBHpw2qa2KTi1a-M,24649
89
+ skfolio/portfolio/_portfolio.py,sha256=so9wer_KNnJqudvalNeV9ubEy5WIumE-PxvEAAL9Ty4,33028
91
90
  skfolio/pre_selection/__init__.py,sha256=6J_D0QIMi24owwJJP6vxYnIgIyWZuMzCMnpMCEpAvCo,606
92
91
  skfolio/pre_selection/_drop_correlated.py,sha256=4-PSd8R20Rcdyc8Zzcy9B2eRPEtaEkM3YXi74YKF-Pk,3839
93
92
  skfolio/pre_selection/_drop_zero_variance.py,sha256=66Mi0Fta1kdmLw0CCqa7p9AqpoBpS9B3fGPLqhb8VIU,2312
@@ -97,25 +96,28 @@ skfolio/pre_selection/_select_non_dominated.py,sha256=Auv7G8E1QNO96heb35oBWmFLd6
97
96
  skfolio/pre_selection/_select_non_expiring.py,sha256=g7ekl69MxQFkg06Km3NiuJXr0JfYPqvo5hMLQsIcUKY,5101
98
97
  skfolio/preprocessing/__init__.py,sha256=94jMyP_E7FlwQVE8D_bXDi8KyfAA2xPHTDvYOi6zf_g,123
99
98
  skfolio/preprocessing/_returns.py,sha256=6G5qJIVHGnIoeBNAqpJTB-569g9NeXVIyrz033bK5Gk,4576
100
- skfolio/prior/__init__.py,sha256=daUO3ha87Nu0ixJci33dR1dKgoYC6-1Nf3AUoaskE5o,544
101
- skfolio/prior/_base.py,sha256=Py3Ip3mDhaDyBVWQy9Mz7ztv3RkovVC58gw4rCcC-jU,1958
102
- skfolio/prior/_black_litterman.py,sha256=oMNYNyDSBp8Uygp0EvQissjNKS41GMLCVzITUqA0HeY,10470
103
- skfolio/prior/_empirical.py,sha256=jDWgNhCfqOFVbVBphACZsqpK47OPOKGUCnOVsgmdqXI,7324
104
- skfolio/prior/_factor_model.py,sha256=GhilLpNu8UdPrj5vb63zKJ9WWnt79k3SpNf6ULqZ8Bk,11571
105
- skfolio/prior/_synthetic_data.py,sha256=XhavOTbbwBtO1suoA4pfZnm5YAdlykb07NQvvqPpRxo,8551
106
- skfolio/synthetic_returns/__init__.py,sha256=-dnmFSmrTJcsMmrwIxPCbqENbx6gTuWAm_cx7nQHpns,29
99
+ skfolio/prior/__init__.py,sha256=4bi4u7Y-D9vLKb0nxlAXYEZUuYkjPXQcC7qlYUu_DMA,720
100
+ skfolio/prior/_base.py,sha256=aSqyhBYc35RpWq4XpM3UsOu88Bvxbqn7QhctK9bP0I0,2217
101
+ skfolio/prior/_black_litterman.py,sha256=oRH6wUjsL5bkjiNbVtxaIPMNi34rqPp7WBmDbJiklKM,10675
102
+ skfolio/prior/_empirical.py,sha256=zRceQNsbeWdkZHIaFvO91AhZTqkPd8YE2f60cK39M-U,7486
103
+ skfolio/prior/_entropy_pooling.py,sha256=TBczvvauhaV0Uq5NOZXHWc3Ojru-PFNosvbm79RuT9E,59278
104
+ skfolio/prior/_factor_model.py,sha256=lbXFsuidDJvLBX7fwp6fXXvgdL3MzkL5lJCx7HEABcA,12241
105
+ skfolio/prior/_opinion_pooling.py,sha256=dBZ8TjlAOKWA9lZFD4DS_PH5HG8iZYgtNrJLPnqwX0o,19055
106
+ skfolio/prior/_synthetic_data.py,sha256=HsxcIQa9lwcYh81Y-o00xsSr9-djgrTQtRTE4SCNpKo,8735
107
107
  skfolio/uncertainty_set/__init__.py,sha256=SHbOq0ip3vuwEK9G4pzz0GncDbGsHw7ywF9tPnkUrZ8,648
108
108
  skfolio/uncertainty_set/_base.py,sha256=R6qH8Zg5Ti3Qny-guL4Js8rY9JhpF8jMwV_w9HCbgWI,4307
109
- skfolio/uncertainty_set/_bootstrap.py,sha256=tDnUvhTtl0HWu-xL6MWZZZyWs4Y06PKQ5xPDiOU7RE4,11265
110
- skfolio/uncertainty_set/_empirical.py,sha256=t9_V23gH1eJ0jaASQcus-QOSATAr9HKVW2hjHMNYjO0,9380
109
+ skfolio/uncertainty_set/_bootstrap.py,sha256=6cSTae56zFAAS4WCxEm3IO2FdVk3JHbCOJc7IOIx8b4,11297
110
+ skfolio/uncertainty_set/_empirical.py,sha256=ZmCk6OBMxbii3TyFg3tZRHWijU7fH1R0R-WS-jb3gj4,9444
111
111
  skfolio/utils/__init__.py,sha256=bC6-MsCVF7xKTr48z7OzJJUeWvqAB7BiHeNTiKsme70,20
112
112
  skfolio/utils/bootstrap.py,sha256=6BN_9CgfbeImBSNEE0dF52FRGuQT41HcQXeHPLwFqJc,3565
113
- skfolio/utils/equations.py,sha256=ZDEAapAfWeiOHvQbboswSqfQInlQklKJDtkiV__d-sc,15575
113
+ skfolio/utils/composition.py,sha256=e0dWCEIYnho3HU2KGGS9UHQdycdVuqMcTe7hi0LihjQ,3416
114
+ skfolio/utils/equations.py,sha256=N4C937GesVEwC3PWecP2oSpg6myayA58fWsNGjhEbpw,15812
115
+ skfolio/utils/figure.py,sha256=2U0PuHRuza_1N6o_fWD8amNDc0IhgfzM5owFl3zBzwg,5744
114
116
  skfolio/utils/sorting.py,sha256=F7gfIBfnulfDUiqvzrlR-pba4PPLJT6NH7-5s4sdRhw,3521
115
117
  skfolio/utils/stats.py,sha256=glVHo7rjwy06dl5kkULLOADMrEkVJcfXXAz-1qmYQL4,17005
116
- skfolio/utils/tools.py,sha256=sGJFiqc60TqXyaWoH7JdsbaFYj_bvwq3hHIk6FxDC3U,22994
117
- skfolio-0.9.1.dist-info/licenses/LICENSE,sha256=F6Gi-ZJX5BlVzYK8R9NcvAkAsKa7KO29xB1OScbrH6Q,1526
118
- skfolio-0.9.1.dist-info/METADATA,sha256=139u03mcy89z6d922cjhRPNqr-Buh1NT4TIvPFX4sB0,22383
119
- skfolio-0.9.1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
120
- skfolio-0.9.1.dist-info/top_level.txt,sha256=NXEaoS9Ms7t32gxkb867nV0OKlU0KmssL7IJBVo0fJs,8
121
- skfolio-0.9.1.dist-info/RECORD,,
118
+ skfolio/utils/tools.py,sha256=XQ-bkbhIqBTuv_ZLK-vDnDx8NGFCFvmWoJF8Ui1tj38,23020
119
+ skfolio-0.10.0.dist-info/licenses/LICENSE,sha256=F6Gi-ZJX5BlVzYK8R9NcvAkAsKa7KO29xB1OScbrH6Q,1526
120
+ skfolio-0.10.0.dist-info/METADATA,sha256=bZvW8kQjrwBd2XWFdvZElhNYQaJVzCxkjo0f8Ju7pUU,25053
121
+ skfolio-0.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
122
+ skfolio-0.10.0.dist-info/top_level.txt,sha256=NXEaoS9Ms7t32gxkb867nV0OKlU0KmssL7IJBVo0fJs,8
123
+ skfolio-0.10.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +0,0 @@
1
- """Synthetic Data module."""