skfolio 0.10.0__py3-none-any.whl → 0.10.2__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.
@@ -136,7 +136,9 @@ def variance(
136
136
  if sample_weight is None:
137
137
  return np.var(returns, ddof=0 if biased else 1, axis=0)
138
138
 
139
- biased_var = sample_weight @ (returns - mean(returns)) ** 2
139
+ biased_var = (
140
+ sample_weight @ (returns - mean(returns, sample_weight=sample_weight)) ** 2
141
+ )
140
142
  if biased:
141
143
  return biased_var
142
144
  n_eff = 1 / np.sum(sample_weight**2)
@@ -177,7 +179,7 @@ def semi_variance(
177
179
  If `returns` is a 2D-array, the result is a ndarray of shape (n_assets,).
178
180
  """
179
181
  if min_acceptable_return is None:
180
- min_acceptable_return = mean(returns)
182
+ min_acceptable_return = mean(returns, sample_weight=sample_weight)
181
183
 
182
184
  biased_semi_var = mean(
183
185
  np.maximum(0, min_acceptable_return - returns) ** 2, sample_weight=sample_weight
@@ -191,6 +191,7 @@ class EmpiricalPrior(BasePrior):
191
191
  # horizon
192
192
  mu = np.exp(mu + 0.5 * np.diag(covariance))
193
193
  covariance = np.outer(mu, mu) * (np.exp(covariance) - 1)
194
+ mu -= 1
194
195
 
195
196
  # we validate and convert to numpy after all models have been fitted to keep
196
197
  # features names information.
@@ -589,10 +589,10 @@ class EntropyPooling(BasePrior):
589
589
 
590
590
  Parameters
591
591
  ----------
592
- a : ndarray of shape (n_observations, n_constrains)
592
+ a : ndarray of shape (n_observations, n_constraints)
593
593
  Left matrix in `x @ a == b` or `x @ a <= b`.
594
594
 
595
- a : ndarray of shape (n_observations, n_constrains)
595
+ a : ndarray of shape (n_observations, n_constraints)
596
596
  Right vector in `x @ a == b` or `x @ a <= b`.
597
597
 
598
598
  Returns
@@ -1029,16 +1029,16 @@ class EntropyPooling(BasePrior):
1029
1029
 
1030
1030
  """
1031
1031
  n_observations, _ = self._returns.shape
1032
- # Init constrains with sum(p)==1, rescaled by its norm
1032
+ # Init constraints with sum(p)==1, rescaled by its norm
1033
1033
  # Has better convergence than the normalized form done inside the dual.
1034
1034
  a = [np.ones(n_observations).reshape(-1, 1) / np.sqrt(n_observations)]
1035
1035
  b = [np.array([1.0]) / np.sqrt(n_observations)]
1036
1036
  bounds = [(None, None)]
1037
- for name, constrains in self._constraints.items():
1038
- if constrains is not None:
1039
- a.append(constrains[0])
1040
- b.append(constrains[1])
1041
- s = constrains[1].size
1037
+ for name, constraints in self._constraints.items():
1038
+ if constraints is not None:
1039
+ a.append(constraints[0])
1040
+ b.append(constraints[1])
1041
+ s = constraints[1].size
1042
1042
  match name:
1043
1043
  case "equality" | "cvar_equality":
1044
1044
  bounds += [(None, None)] * s
@@ -1145,7 +1145,7 @@ class EntropyPooling(BasePrior):
1145
1145
 
1146
1146
  if self._constraints["fixed_equality"] is not None:
1147
1147
  a, b = self._constraints["fixed_equality"]
1148
- # Relaxe the problem with slack variables with a norm1 penalty to avoid
1148
+ # Relax the problem with slack variables with a norm1 penalty to avoid
1149
1149
  # solver infeasibility that may arise from overly tight constraints from
1150
1150
  # fixing the moments.
1151
1151
  slack = cp.Variable(b.size)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skfolio
3
- Version: 0.10.0
3
+ Version: 0.10.2
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>, Matteo Manzi <matteomanzi09@gmail.com>
6
+ Maintainer-email: Hugo Delatte <delatte.hugo@gmail.com>, Matteo Manzi <matteomanzi09@gmail.com>, Carlo Nicolini <c.nicolini@ipazia.com>
7
7
  License: BSD 3-Clause License
8
8
 
9
9
  Copyright (c) 2007-2023 The skfolio developers.
@@ -176,7 +176,6 @@ Installation
176
176
  pip install -U skfolio
177
177
 
178
178
 
179
-
180
179
  Dependencies
181
180
  ~~~~~~~~~~~~
182
181
 
@@ -192,6 +191,23 @@ Dependencies
192
191
  - joblib (>= |JoblibMinVersion|)
193
192
  - plotly (>= |PlotlyMinVersion|)
194
193
 
194
+ Docker
195
+ ~~~~~~
196
+
197
+ You can also spin up a reproducible JupyterLab environment using Docker:
198
+
199
+ Build the image::
200
+
201
+ docker build -t skfolio-jupyterlab .
202
+
203
+ Run the container::
204
+
205
+ docker run -p 8888:8888 -v <path-to-your-folder-containing-data>:/app/data -it skfolio-jupyterlab
206
+
207
+ Browse:
208
+
209
+ Open localhost:8888/lab and start using `skfolio`
210
+
195
211
  Key Concepts
196
212
  ~~~~~~~~~~~~
197
213
  Since the development of modern portfolio theory by Markowitz (1952), mean-variance
@@ -385,6 +401,7 @@ Imports
385
401
  )
386
402
  from skfolio.optimization import (
387
403
  MeanRisk,
404
+ HierarchicalRiskParity,
388
405
  NestedClustersOptimization,
389
406
  ObjectiveFunction,
390
407
  RiskBudgeting,
@@ -584,11 +601,13 @@ Factor Model
584
601
 
585
602
  factor_prices = load_factors_dataset()
586
603
 
587
- X, y = prices_to_returns(prices, factor_prices)
588
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, shuffle=False)
604
+ X, factors = prices_to_returns(prices, factor_prices)
605
+ X_train, X_test, factors_train, factors_test = train_test_split(
606
+ X, factors, test_size=0.33, shuffle=False
607
+ )
589
608
 
590
609
  model = MeanRisk(prior_estimator=FactorModel())
591
- model.fit(X_train, y_train)
610
+ model.fit(X_train, factors_train)
592
611
 
593
612
  print(model.weights_)
594
613
 
@@ -597,7 +616,6 @@ Factor Model
597
616
  print(portfolio.calmar_ratio)
598
617
  print(portfolio.summary())
599
618
 
600
-
601
619
  Factor Model & Covariance Detoning
602
620
  ----------------------------------
603
621
  .. code-block:: python
@@ -658,7 +676,7 @@ Combinatorial Purged Cross-Validation
658
676
 
659
677
  cv = CombinatorialPurgedCV(n_folds=10, n_test_folds=2)
660
678
 
661
- print(cv.get_summary(X_train))
679
+ print(cv.summary(X_train))
662
680
 
663
681
  population = cross_val_predict(model, X_train, cv=cv)
664
682
 
@@ -674,7 +692,7 @@ Minimum CVaR Optimization on Synthetic Returns
674
692
  .. code-block:: python
675
693
 
676
694
  vine = VineCopula(log_transform=True, n_jobs=-1)
677
- prior = =SyntheticData(distribution_estimator=vine, n_samples=2000)
695
+ prior = SyntheticData(distribution_estimator=vine, n_samples=2000)
678
696
  model = MeanRisk(risk_measure=RiskMeasure.CVAR, prior_estimator=prior)
679
697
  model.fit(X)
680
698
  print(model.weights_)
@@ -684,7 +702,7 @@ Stress Test
684
702
  -----------
685
703
  .. code-block:: python
686
704
 
687
- vine = VineCopula(log_transform=True, central_assets=["BAC"] n_jobs=-1)
705
+ vine = VineCopula(log_transform=True, central_assets=["BAC"], n_jobs=-1)
688
706
  vine.fit(X)
689
707
  X_stressed = vine.sample(n_samples=10_000, conditioning = {"BAC": -0.2})
690
708
  ptf_stressed = model.predict(X_stressed)
@@ -702,7 +720,7 @@ Minimum CVaR Optimization on Synthetic Factors
702
720
  )
703
721
  factor_model = FactorModel(factor_prior_estimator=factor_prior)
704
722
  model = MeanRisk(risk_measure=RiskMeasure.CVAR, prior_estimator=factor_model)
705
- model.fit(X, y)
723
+ model.fit(X, factors)
706
724
  print(model.weights_)
707
725
 
708
726
 
@@ -713,7 +731,7 @@ Factor Stress Test
713
731
  factor_model.set_params(factor_prior_estimator__sample_args=dict(
714
732
  conditioning={"QUAL": -0.5}
715
733
  ))
716
- factor_model.fit(X,y)
734
+ factor_model.fit(X, factors)
717
735
  stressed_dist = factor_model.return_distribution_
718
736
  stressed_ptf = model.predict(stressed_dist)
719
737
 
@@ -798,7 +816,7 @@ Recognition
798
816
  ~~~~~~~~~~~
799
817
 
800
818
  We would like to thank all contributors to our direct dependencies, such as
801
- scikit-learn and cvxpy, as well as the contributors of the following resources that
819
+ `scikit-learn <https://github.com/scikit-learn/scikit-learn>`_ and `cvxpy <https://github.com/cvxpy/cvxpy>`_, as well as the contributors of the following resources that
802
820
  served as sources of inspiration:
803
821
 
804
822
  * PyPortfolioOpt
@@ -807,6 +825,7 @@ served as sources of inspiration:
807
825
  * microprediction
808
826
  * statsmodels
809
827
  * rsome
828
+ * danielppalomar.com
810
829
  * gautier.marti.ai
811
830
 
812
831
 
@@ -815,12 +834,25 @@ Citation
815
834
 
816
835
  If you use `skfolio` in a scientific publication, we would appreciate citations:
817
836
 
818
- Bibtex entry::
819
-
820
- @misc{skfolio,
821
- author = {Delatte, Hugo and Nicolini, Carlo},
822
- title = {skfolio},
823
- year = {2023},
824
- url = {https://github.com/skfolio/skfolio}
825
- }
837
+ **The library**::
838
+
839
+ @software{skfolio,
840
+ title = {skfolio},
841
+ author = {Delatte, Hugo and Nicolini, Carlo and Manzi, Matteo},
842
+ year = {2024},
843
+ doi = {TBD after next release},
844
+ url = {https://github.com/skfolio/skfolio}
845
+ }
846
+
847
+ **The paper**::
848
+
849
+ @article{nicolini2025skfolio,
850
+ title = {skfolio: Portfolio Optimization in Python},
851
+ author = {Nicolini, Carlo and Manzi, Matteo and Delatte, Hugo},
852
+ journal = {arXiv preprint arXiv:2507.04176},
853
+ year = {2025},
854
+ eprint = {2507.04176},
855
+ archivePrefix = {arXiv},
856
+ url = {https://arxiv.org/abs/2507.04176}
857
+ }
826
858
 
@@ -37,7 +37,7 @@ skfolio/distribution/univariate/_selection.py,sha256=6KL4gngiLKwaBUpCDX19ABOkMBz
37
37
  skfolio/distribution/univariate/_student_t.py,sha256=GcI4fKp6q5XegfvT_i3AqfWlUMxCq7A5sX6Xsf4pye8,4553
38
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=ysON3rPfcLPSAPY3T1MfkuLPoKU__KN5sRyCzOXe6to,28716
40
+ skfolio/measures/_measures.py,sha256=-pF9DtJqsFliSiv9Ek_7IzytTmoKQFdqCxUl5fdSlYQ,28790
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
@@ -99,8 +99,8 @@ skfolio/preprocessing/_returns.py,sha256=6G5qJIVHGnIoeBNAqpJTB-569g9NeXVIyrz033b
99
99
  skfolio/prior/__init__.py,sha256=4bi4u7Y-D9vLKb0nxlAXYEZUuYkjPXQcC7qlYUu_DMA,720
100
100
  skfolio/prior/_base.py,sha256=aSqyhBYc35RpWq4XpM3UsOu88Bvxbqn7QhctK9bP0I0,2217
101
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
102
+ skfolio/prior/_empirical.py,sha256=f_5PTd3orjIXIaqWEs9fPffnStNLXe3Y_-PZ1dc0p3U,7506
103
+ skfolio/prior/_entropy_pooling.py,sha256=x4qd-bU52ZGePwduPNY5fBhWwrNHfI77oWp5x0KyLPc,59285
104
104
  skfolio/prior/_factor_model.py,sha256=lbXFsuidDJvLBX7fwp6fXXvgdL3MzkL5lJCx7HEABcA,12241
105
105
  skfolio/prior/_opinion_pooling.py,sha256=dBZ8TjlAOKWA9lZFD4DS_PH5HG8iZYgtNrJLPnqwX0o,19055
106
106
  skfolio/prior/_synthetic_data.py,sha256=HsxcIQa9lwcYh81Y-o00xsSr9-djgrTQtRTE4SCNpKo,8735
@@ -116,8 +116,8 @@ skfolio/utils/figure.py,sha256=2U0PuHRuza_1N6o_fWD8amNDc0IhgfzM5owFl3zBzwg,5744
116
116
  skfolio/utils/sorting.py,sha256=F7gfIBfnulfDUiqvzrlR-pba4PPLJT6NH7-5s4sdRhw,3521
117
117
  skfolio/utils/stats.py,sha256=glVHo7rjwy06dl5kkULLOADMrEkVJcfXXAz-1qmYQL4,17005
118
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,,
119
+ skfolio-0.10.2.dist-info/licenses/LICENSE,sha256=F6Gi-ZJX5BlVzYK8R9NcvAkAsKa7KO29xB1OScbrH6Q,1526
120
+ skfolio-0.10.2.dist-info/METADATA,sha256=GO42LTbqL_TT9Ml1TWGrTTIM_KPKg0lTx_yyoYxqxgU,26181
121
+ skfolio-0.10.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
122
+ skfolio-0.10.2.dist-info/top_level.txt,sha256=NXEaoS9Ms7t32gxkb867nV0OKlU0KmssL7IJBVo0fJs,8
123
+ skfolio-0.10.2.dist-info/RECORD,,