skfolio 0.1.2__py3-none-any.whl → 0.2.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.
- skfolio/datasets/_base.py +4 -1
- skfolio/distance/_distance.py +0 -1
- skfolio/measures/_enums.py +3 -1
- skfolio/measures/_measures.py +0 -1
- skfolio/model_selection/__init__.py +2 -0
- skfolio/model_selection/_combinatorial.py +172 -21
- skfolio/model_selection/_validation.py +8 -6
- skfolio/optimization/_base.py +19 -12
- skfolio/optimization/cluster/hierarchical/_base.py +5 -4
- skfolio/optimization/cluster/hierarchical/_herc.py +2 -2
- skfolio/optimization/cluster/hierarchical/_hrp.py +2 -3
- skfolio/optimization/convex/_base.py +5 -4
- skfolio/optimization/convex/_distributionally_robust.py +2 -2
- skfolio/optimization/convex/_maximum_diversification.py +2 -2
- skfolio/optimization/convex/_mean_risk.py +2 -2
- skfolio/optimization/convex/_risk_budgeting.py +2 -2
- skfolio/optimization/ensemble/_base.py +1 -3
- skfolio/optimization/ensemble/_stacking.py +6 -4
- skfolio/optimization/naive/_naive.py +6 -6
- skfolio/population/_population.py +48 -39
- skfolio/portfolio/_base.py +0 -1
- skfolio/portfolio/_portfolio.py +13 -12
- skfolio/prior/_factor_model.py +8 -5
- skfolio/utils/fixes/_dendrogram.py +9 -7
- skfolio/utils/tools.py +8 -7
- {skfolio-0.1.2.dist-info → skfolio-0.2.0.dist-info}/METADATA +20 -21
- {skfolio-0.1.2.dist-info → skfolio-0.2.0.dist-info}/RECORD +30 -30
- {skfolio-0.1.2.dist-info → skfolio-0.2.0.dist-info}/WHEEL +1 -1
- {skfolio-0.1.2.dist-info → skfolio-0.2.0.dist-info}/LICENSE +0 -0
- {skfolio-0.1.2.dist-info → skfolio-0.2.0.dist-info}/top_level.txt +0 -0
skfolio/datasets/_base.py
CHANGED
@@ -140,7 +140,10 @@ def download_dataset(
|
|
140
140
|
DataFrame with each row representing one observation and each column
|
141
141
|
representing the asset price of a given observation.
|
142
142
|
"""
|
143
|
-
url =
|
143
|
+
url = (
|
144
|
+
f"https://github.com/skfolio/skfolio-datasets/raw/main/"
|
145
|
+
f"datasets/{data_filename}.csv.gz"
|
146
|
+
)
|
144
147
|
|
145
148
|
data_home = get_data_home(data_home=data_home)
|
146
149
|
filepath = os.path.join(data_home, f"{data_filename}.pkz")
|
skfolio/distance/_distance.py
CHANGED
skfolio/measures/_enums.py
CHANGED
skfolio/measures/_measures.py
CHANGED
@@ -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
|
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.
|
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,19 +314,24 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
|
|
320
314
|
yield train_index, test_index_list
|
321
315
|
|
322
316
|
def summary(self, X) -> pd.Series:
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
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
|
+
)
|
323
|
+
return pd.Series(
|
324
|
+
{
|
325
|
+
"Number of Observations": n_observations,
|
326
|
+
"Total Number of Folds": self.n_folds,
|
327
|
+
"Number of Test Folds": self.n_test_folds,
|
328
|
+
"Purge Size": self.purged_size,
|
329
|
+
"Embargo Size": self.embargo_size,
|
330
|
+
"Average Training Size": int(avg_train_size),
|
331
|
+
"Number of Test Paths": self.n_test_paths,
|
332
|
+
"Number of Training Combinations": self.n_splits,
|
333
|
+
}
|
334
|
+
)
|
336
335
|
|
337
336
|
def plot_train_test_folds(self) -> skt.Figure:
|
338
337
|
"""Plot the train/test fold locations"""
|
@@ -408,3 +407,155 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
|
|
408
407
|
)
|
409
408
|
|
410
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]
|
@@ -170,12 +170,14 @@ def cross_val_predict(
|
|
170
170
|
path_id = path_ids[i, j]
|
171
171
|
portfolios[path_id].append(p)
|
172
172
|
name = portfolio_params.pop("name", "path")
|
173
|
-
pred = Population(
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
173
|
+
pred = Population(
|
174
|
+
[
|
175
|
+
MultiPeriodPortfolio(
|
176
|
+
name=f"{name}_{i}", portfolios=portfolios[i], **portfolio_params
|
177
|
+
)
|
178
|
+
for i in range(path_nb)
|
179
|
+
]
|
180
|
+
)
|
179
181
|
else:
|
180
182
|
# We need to re-order the test folds in case they were un-ordered by the
|
181
183
|
# CV generator.
|
skfolio/optimization/_base.py
CHANGED
@@ -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 `
|
33
|
-
model and
|
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 [
|
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
|
|
@@ -97,15 +102,17 @@ class BaseOptimization(skb.BaseEstimator, ABC):
|
|
97
102
|
# For a 2D array we return a population of portfolios.
|
98
103
|
if self.weights_.ndim == 2:
|
99
104
|
n_portfolios = self.weights_.shape[0]
|
100
|
-
return Population(
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
return Population(
|
106
|
+
[
|
107
|
+
Portfolio(
|
108
|
+
X=X,
|
109
|
+
weights=self.weights_[i],
|
110
|
+
name=f"ptf{i} - {name}",
|
111
|
+
**ptf_kwargs,
|
112
|
+
)
|
113
|
+
for i in range(n_portfolios)
|
114
|
+
]
|
115
|
+
)
|
109
116
|
return Portfolio(X=X, weights=self.weights_, name=name, **ptf_kwargs)
|
110
117
|
|
111
118
|
def score(self, X: npt.ArrayLike, y: npt.ArrayLike = None) -> float:
|
@@ -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 `
|
187
|
-
model and
|
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:
|
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 :
|
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 `
|
208
|
-
model and
|
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
|
----------
|
@@ -6,7 +6,6 @@
|
|
6
6
|
# The risk measure generalization and constraint features are derived
|
7
7
|
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
8
|
|
9
|
-
|
10
9
|
import numpy as np
|
11
10
|
import numpy.typing as npt
|
12
11
|
import pandas as pd
|
@@ -205,8 +204,8 @@ class HierarchicalRiskParity(BaseHierarchicalOptimization):
|
|
205
204
|
portfolio_params : dict, optional
|
206
205
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
207
206
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
208
|
-
`management_fees` and `
|
209
|
-
model and
|
207
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
208
|
+
optimization model and passed to the portfolio.
|
210
209
|
|
211
210
|
Attributes
|
212
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 `
|
407
|
-
model and
|
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:
|
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 :
|
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 `
|
212
|
-
model and
|
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 `
|
307
|
-
model and
|
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 `
|
514
|
-
model and
|
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 `
|
344
|
-
model and
|
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
|
----------
|
@@ -80,9 +80,7 @@ class BaseComposition(skb.BaseEstimator, ABC):
|
|
80
80
|
invalid_names = set(names).intersection(self.get_params(deep=False))
|
81
81
|
if invalid_names:
|
82
82
|
raise ValueError(
|
83
|
-
"Estimator names conflict with constructor arguments: {!r}"
|
84
|
-
sorted(invalid_names)
|
85
|
-
)
|
83
|
+
f"Estimator names conflict with constructor arguments: {sorted(invalid_names)!r}"
|
86
84
|
)
|
87
85
|
invalid_names = [name for name in names if "__" in name]
|
88
86
|
if invalid_names:
|
@@ -312,10 +312,12 @@ class StackingOptimization(BaseOptimization, BaseComposition):
|
|
312
312
|
_ = self._validate_data(X)
|
313
313
|
|
314
314
|
if isinstance(self.cv, BaseCombinatorialCV):
|
315
|
-
X_pred = np.array(
|
316
|
-
|
317
|
-
|
318
|
-
|
315
|
+
X_pred = np.array(
|
316
|
+
[
|
317
|
+
pred.quantile(measure=self.quantile_measure, q=self.quantile)
|
318
|
+
for pred in cv_predictions
|
319
|
+
]
|
320
|
+
).T
|
319
321
|
else:
|
320
322
|
X_pred = np.array(cv_predictions).T
|
321
323
|
if y is not None:
|
@@ -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 `
|
35
|
-
model and
|
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 `
|
99
|
-
model and
|
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 `
|
143
|
-
model and
|
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
|
----------
|
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Population module.
|
2
2
|
A population is a collection of portfolios.
|
3
3
|
"""
|
4
4
|
|
@@ -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
|
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[
|
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[
|
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
|
-
) -> "
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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 :
|
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:
|
115
|
-
) ->
|
115
|
+
item: BasePortfolio,
|
116
|
+
) -> BasePortfolio:
|
116
117
|
"""Validate that items are of type Portfolio or MultiPeriodPortfolio."""
|
117
|
-
if isinstance(item,
|
118
|
+
if isinstance(item, BasePortfolio):
|
118
119
|
return item
|
119
120
|
raise TypeError(
|
120
|
-
"Population only accept items
|
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
|
|
@@ -140,9 +142,12 @@ class Population(list):
|
|
140
142
|
non-dominated portfolios.
|
141
143
|
"""
|
142
144
|
n = len(self)
|
143
|
-
if n > 0 and np.any(
|
144
|
-
|
145
|
-
|
145
|
+
if n > 0 and np.any(
|
146
|
+
[
|
147
|
+
portfolio.fitness_measures != self[0].fitness_measures
|
148
|
+
for portfolio in self
|
149
|
+
]
|
150
|
+
):
|
146
151
|
raise ValueError(
|
147
152
|
"Cannot compute non denominated sorting with Portfolios "
|
148
153
|
"containing mixed `fitness_measures`"
|
@@ -187,11 +192,13 @@ class Population(list):
|
|
187
192
|
return self.__class__(
|
188
193
|
[portfolio for portfolio in self if portfolio.tag in tags]
|
189
194
|
)
|
190
|
-
return self.__class__(
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
+
return self.__class__(
|
196
|
+
[
|
197
|
+
portfolio
|
198
|
+
for portfolio in self
|
199
|
+
if portfolio.name in names and portfolio.tag in tags
|
200
|
+
]
|
201
|
+
)
|
195
202
|
|
196
203
|
def measures(
|
197
204
|
self,
|
@@ -319,7 +326,7 @@ class Population(list):
|
|
319
326
|
q: float,
|
320
327
|
names: skt.Names | None = None,
|
321
328
|
tags: skt.Tags | None = None,
|
322
|
-
) ->
|
329
|
+
) -> BasePortfolio:
|
323
330
|
"""Returns the portfolio corresponding to the `q` quantile for a given portfolio
|
324
331
|
measure.
|
325
332
|
|
@@ -339,7 +346,7 @@ class Population(list):
|
|
339
346
|
|
340
347
|
Returns
|
341
348
|
-------
|
342
|
-
values :
|
349
|
+
values : BasePortfolio
|
343
350
|
Portfolio corresponding to the `q` quantile for the measure.
|
344
351
|
"""
|
345
352
|
if not 0 <= q <= 1:
|
@@ -355,7 +362,7 @@ class Population(list):
|
|
355
362
|
measure: skt.Measure,
|
356
363
|
names: skt.Names | None = None,
|
357
364
|
tags: skt.Tags | None = None,
|
358
|
-
) ->
|
365
|
+
) -> BasePortfolio:
|
359
366
|
"""Returns the portfolio with the minimum measure.
|
360
367
|
|
361
368
|
Parameters
|
@@ -371,7 +378,7 @@ class Population(list):
|
|
371
378
|
|
372
379
|
Returns
|
373
380
|
-------
|
374
|
-
values :
|
381
|
+
values : BasePortfolio
|
375
382
|
The portfolio with minimum measure.
|
376
383
|
"""
|
377
384
|
return self.quantile(measure=measure, q=0, names=names, tags=tags)
|
@@ -381,7 +388,7 @@ class Population(list):
|
|
381
388
|
measure: skt.Measure,
|
382
389
|
names: skt.Names | None = None,
|
383
390
|
tags: skt.Tags | None = None,
|
384
|
-
) ->
|
391
|
+
) -> BasePortfolio:
|
385
392
|
"""Returns the portfolio with the maximum measure.
|
386
393
|
|
387
394
|
Parameters
|
@@ -397,7 +404,7 @@ class Population(list):
|
|
397
404
|
|
398
405
|
Returns
|
399
406
|
-------
|
400
|
-
values :
|
407
|
+
values : BasePortfolio
|
401
408
|
The portfolio with maximum measure.
|
402
409
|
"""
|
403
410
|
return self.quantile(measure=measure, q=1, names=names, tags=tags)
|
@@ -784,15 +791,17 @@ class Population(list):
|
|
784
791
|
x=xi,
|
785
792
|
y=yi,
|
786
793
|
z=Z,
|
787
|
-
hovertemplate="<br>".join(
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
794
|
+
hovertemplate="<br>".join(
|
795
|
+
[
|
796
|
+
str(e)
|
797
|
+
+ ": %{"
|
798
|
+
+ v
|
799
|
+
+ ":"
|
800
|
+
+ (",.3%" if not e.is_ratio else None)
|
801
|
+
+ "}"
|
802
|
+
for e, v in [(x, "x"), (y, "y"), (z, "z")]
|
803
|
+
]
|
804
|
+
)
|
796
805
|
+ "<extra></extra>",
|
797
806
|
colorbar=dict(
|
798
807
|
title=str(z),
|
skfolio/portfolio/_base.py
CHANGED
skfolio/portfolio/_portfolio.py
CHANGED
@@ -8,7 +8,6 @@ is the dot product of the assets weights with the assets returns.
|
|
8
8
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
9
9
|
# License: BSD 3 clause
|
10
10
|
|
11
|
-
|
12
11
|
import numbers
|
13
12
|
from typing import ClassVar
|
14
13
|
|
@@ -402,17 +401,19 @@ class Portfolio(BasePortfolio):
|
|
402
401
|
"""
|
403
402
|
|
404
403
|
_read_only_attrs: ClassVar[set] = BasePortfolio._read_only_attrs.copy()
|
405
|
-
_read_only_attrs.update(
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
404
|
+
_read_only_attrs.update(
|
405
|
+
{
|
406
|
+
"X",
|
407
|
+
"assets",
|
408
|
+
"weights",
|
409
|
+
"previous_weights",
|
410
|
+
"transaction_costs",
|
411
|
+
"management_fees",
|
412
|
+
"n_assets",
|
413
|
+
"total_cost",
|
414
|
+
"total_fee",
|
415
|
+
}
|
416
|
+
)
|
416
417
|
|
417
418
|
__slots__ = {
|
418
419
|
# read-only
|
skfolio/prior/_factor_model.py
CHANGED
@@ -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
|
@@ -114,10 +115,12 @@ class LoadingMatrixRegression(BaseLoadingMatrix):
|
|
114
115
|
self.loading_matrix_ = np.array(
|
115
116
|
[self.multi_output_regressor_.estimators_[i].coef_ for i in range(n_assets)]
|
116
117
|
)
|
117
|
-
self.intercepts_ = np.array(
|
118
|
-
|
119
|
-
|
120
|
-
|
118
|
+
self.intercepts_ = np.array(
|
119
|
+
[
|
120
|
+
self.multi_output_regressor_.estimators_[i].intercept_
|
121
|
+
for i in range(n_assets)
|
122
|
+
]
|
123
|
+
)
|
121
124
|
|
122
125
|
|
123
126
|
class FactorModel(BasePrior):
|
@@ -194,7 +197,7 @@ class FactorModel(BasePrior):
|
|
194
197
|
self.max_iteration = max_iteration
|
195
198
|
|
196
199
|
# noinspection PyMethodOverriding, PyPep8Naming
|
197
|
-
def fit(self, X: npt.ArrayLike, y:
|
200
|
+
def fit(self, X: npt.ArrayLike, y: Any):
|
198
201
|
"""Fit the Factor Model estimator.
|
199
202
|
|
200
203
|
Parameters
|
@@ -290,13 +290,15 @@ class _Dendrogram:
|
|
290
290
|
Sets and returns default layout object for dendrogram figure.
|
291
291
|
|
292
292
|
"""
|
293
|
-
self.layout.update(
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
293
|
+
self.layout.update(
|
294
|
+
{
|
295
|
+
"showlegend": False,
|
296
|
+
"autosize": False,
|
297
|
+
"hovermode": "closest",
|
298
|
+
"width": width,
|
299
|
+
"height": height,
|
300
|
+
}
|
301
|
+
)
|
300
302
|
|
301
303
|
self.set_axis_layout(self.xaxis)
|
302
304
|
self.set_axis_layout(self.yaxis)
|
skfolio/utils/tools.py
CHANGED
@@ -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:
|
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:
|
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 :
|
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:
|
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 :
|
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:
|
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:
|
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.
|
3
|
+
Version: 0.2.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>
|
@@ -80,42 +80,41 @@ Requires-Dist: sphinx-sitemap ; extra == 'docs'
|
|
80
80
|
Provides-Extra: tests
|
81
81
|
Requires-Dist: pytest ; extra == 'tests'
|
82
82
|
Requires-Dist: pytest-cov ; extra == 'tests'
|
83
|
-
Requires-Dist: black ; extra == 'tests'
|
84
83
|
Requires-Dist: ruff ; extra == 'tests'
|
85
84
|
|
86
85
|
.. -*- mode: rst -*-
|
87
86
|
|
88
|
-
|Licence|
|
87
|
+
|Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website|
|
89
88
|
|
90
89
|
.. |Licence| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
|
91
|
-
|
90
|
+
:target: https://github.com/skfolio/skfolio/blob/main/LICENSE
|
92
91
|
|
93
92
|
.. |Codecov| image:: https://codecov.io/gh/skfolio/skfolio/graph/badge.svg?token=KJ0SE4LHPV
|
94
|
-
|
93
|
+
:target: https://codecov.io/gh/skfolio/skfolio
|
95
94
|
|
96
|
-
.. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue
|
97
|
-
|
95
|
+
.. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg
|
96
|
+
:target: https://pypi.org/project/skfolio/
|
98
97
|
|
99
98
|
.. |PyPi| image:: https://img.shields.io/pypi/v/skfolio
|
100
|
-
|
99
|
+
:target: https://pypi.org/project/skfolio
|
101
100
|
|
102
101
|
.. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
103
|
-
|
102
|
+
:target: https://github.com/psf/black
|
104
103
|
|
105
|
-
.. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml?logo=github
|
106
|
-
|
104
|
+
.. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml.svg?logo=github
|
105
|
+
:target: https://github.com/skfolio/skfolio/raw/main/LICENSE
|
107
106
|
|
108
107
|
.. |Downloads| image:: https://static.pepy.tech/badge/skfolio
|
109
|
-
|
108
|
+
:target: https://pepy.tech/project/skfolio
|
110
109
|
|
111
110
|
.. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
112
|
-
|
111
|
+
:target: https://github.com/astral-sh/ruff
|
113
112
|
|
114
113
|
.. |Contribution| image:: https://img.shields.io/badge/Contributions-Welcome-blue
|
115
|
-
|
114
|
+
:target: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
|
116
115
|
|
117
|
-
.. |Website| image:: https://img.shields.io/website
|
118
|
-
|
116
|
+
.. |Website| image:: https://img.shields.io/website.svg?down_color=red&down_message=down&up_color=53cc0d&up_message=up&url=https://skfolio.org
|
117
|
+
:target: https://skfolio.org
|
119
118
|
|
120
119
|
.. |PythonMinVersion| replace:: 3.10
|
121
120
|
.. |NumpyMinVersion| replace:: 1.23.4
|
@@ -645,9 +644,9 @@ If you use `skfolio` in a scientific publication, we would appreciate citations:
|
|
645
644
|
Bibtex entry::
|
646
645
|
|
647
646
|
@misc{skfolio,
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
647
|
+
author = {Delatte, Hugo and Nicolini, Carlo},
|
648
|
+
title = {skfolio},
|
649
|
+
year = {2023},
|
650
|
+
url = {https://github.com/skfolio/skfolio}
|
651
|
+
}
|
653
652
|
|
@@ -4,22 +4,22 @@ skfolio/typing.py,sha256=yEZiCZ6UIyfYUqtfj9Kf2KA9mrjUbmxyzpH9uqVboJs,1378
|
|
4
4
|
skfolio/cluster/__init__.py,sha256=4g-PFB_ld9BhiQ1ZPvvAorpFbRwd_p_DkeRlulDv2Hk,251
|
5
5
|
skfolio/cluster/_hierarchical.py,sha256=SoISGgdyq4Rgqq1_TCzMbLv69c8a2Q91j_s9-jqaB3E,12817
|
6
6
|
skfolio/datasets/__init__.py,sha256=9Tpf0Uj8wgr-g7xqvqQP4S4TUYDUNmNmK8t6lqBw2Fs,407
|
7
|
-
skfolio/datasets/_base.py,sha256=
|
7
|
+
skfolio/datasets/_base.py,sha256=laAj8vE8evEKv6NAAJdypLrsfmlhqOJ27aP_AcpcxVQ,13952
|
8
8
|
skfolio/datasets/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
skfolio/datasets/data/factors_dataset.csv.gz,sha256=brCJlT25DJo40yg1gnUXAakNtvWZZYR_1ksFeN5JcWE,36146
|
10
10
|
skfolio/datasets/data/sp500_dataset.csv.gz,sha256=7iHKwovvsdCnOanOsiGE-ZU5RyaqDP3pohlB0awErA0,426065
|
11
11
|
skfolio/datasets/data/sp500_index.csv.gz,sha256=iUw0QxwoT4aqZKRn4Xbio8m2l8hX65qzUAbC3VXT_fI,41898
|
12
12
|
skfolio/distance/__init__.py,sha256=vpdmjFlJeI0AvPV3r5tp2zooAG4N9ihCwPlaqqdVj1w,547
|
13
13
|
skfolio/distance/_base.py,sha256=jBgRk6lZrP1woSI9541fTfxBBkp4WCTLlRPmWcmA3j4,1326
|
14
|
-
skfolio/distance/_distance.py,sha256=
|
14
|
+
skfolio/distance/_distance.py,sha256=nQRm141leyCXtjxqXNhsTPtq2dATbpyyEQHKhZ98rE4,18538
|
15
15
|
skfolio/measures/__init__.py,sha256=9ThQikIAQcfKRLSCoMr-Z5vE2-ThtYe9B-L40b6Ewg0,1631
|
16
|
-
skfolio/measures/_enums.py,sha256=
|
17
|
-
skfolio/measures/_measures.py,sha256=
|
16
|
+
skfolio/measures/_enums.py,sha256=NJcngwg9b2JMMiekwkWU9POfnDvgfUgtYtyV2VSFDVM,8934
|
17
|
+
skfolio/measures/_measures.py,sha256=VU9tahJKJvt8PaJZNxWkmFkFU4PsN0FfYO32Je6D53E,16829
|
18
18
|
skfolio/metrics/__init__.py,sha256=MomHJ5_bgjq4qUwGS2bfhNmG_ld0oQ4wK6y0Yy_Eonc,75
|
19
19
|
skfolio/metrics/_scorer.py,sha256=h1VuZk-zzn4rIChHl9FvM7RxqVT3b-jR1CEB-cr9F2s,4306
|
20
|
-
skfolio/model_selection/__init__.py,sha256=
|
21
|
-
skfolio/model_selection/_combinatorial.py,sha256=
|
22
|
-
skfolio/model_selection/_validation.py,sha256=
|
20
|
+
skfolio/model_selection/__init__.py,sha256=8j9Z5tpbgBScjFbn8ZsCm_6rZO7RkPQ1QIF8BqYMVA8,507
|
21
|
+
skfolio/model_selection/_combinatorial.py,sha256=DHfbnjqaOGV5bT_9pGuoR23JN6iEp8Tn0Kr6sLq1Xmg,19045
|
22
|
+
skfolio/model_selection/_validation.py,sha256=Rs3j6LFfm8HJCnaoUXlPnNsqxIyjRmMhmGWvvJ0olXA,7686
|
23
23
|
skfolio/model_selection/_walk_forward.py,sha256=pbMD8VM1OoKiEtrlPeCLG29xVijsq1RArSjTvGO1FnU,7539
|
24
24
|
skfolio/moments/__init__.py,sha256=BaP6FjU-CEsV_mcC2AOKXNFy-_wXZsk-jqzJG6mTpsM,746
|
25
25
|
skfolio/moments/covariance/__init__.py,sha256=KuzhGuiu-eOwQMgWmEhFhqCcOmiayrYp5yeL1NsFW98,563
|
@@ -29,30 +29,30 @@ skfolio/moments/expected_returns/__init__.py,sha256=NETEcKKYKsQvzUU4ZIq6mgT14zdr
|
|
29
29
|
skfolio/moments/expected_returns/_base.py,sha256=xk9mzi48uCOHaMTGQBMr3FU7Ai_shxYhmGeOsVwjv9Q,871
|
30
30
|
skfolio/moments/expected_returns/_expected_returns.py,sha256=T_tt_dsYgXrjIM2REUii7_4_R8IEc3xdtuv45yYM-5o,13388
|
31
31
|
skfolio/optimization/__init__.py,sha256=vXIbwWJL48zJ1jy7ZB2PPBVx7rZo0vVA8QQQuD-L6ts,1021
|
32
|
-
skfolio/optimization/_base.py,sha256=
|
32
|
+
skfolio/optimization/_base.py,sha256=LoRONJP70AwbFpdgqVS_g145pCx0JGkazjWvkQzT_iM,5748
|
33
33
|
skfolio/optimization/cluster/__init__.py,sha256=M3xVdYhNKp4e9CB7hzb4yjTxkkNCHh7Mt_KGFFrkOgs,388
|
34
34
|
skfolio/optimization/cluster/_nco.py,sha256=MKzNvDIAbqS_D41Vp7tNa7OrXYq_bYVSQEVbVCHOEWA,14834
|
35
35
|
skfolio/optimization/cluster/hierarchical/__init__.py,sha256=YnfcPHvjwB6kcG4hoQqc0NqIJKaG7OjBtmXNbOxCq08,405
|
36
|
-
skfolio/optimization/cluster/hierarchical/_base.py,sha256=
|
37
|
-
skfolio/optimization/cluster/hierarchical/_herc.py,sha256=
|
38
|
-
skfolio/optimization/cluster/hierarchical/_hrp.py,sha256=
|
36
|
+
skfolio/optimization/cluster/hierarchical/_base.py,sha256=BVJ8Yq2Yg3Z0iqgx0HwS2OxPLVruoSzrelyYQmt_AiQ,17281
|
37
|
+
skfolio/optimization/cluster/hierarchical/_herc.py,sha256=p7VdEeJOD5ykA1H9FOLaEqIL6D_YYF8VXXfAqTSRdjs,17238
|
38
|
+
skfolio/optimization/cluster/hierarchical/_hrp.py,sha256=i3bliOjWxDX3V3vekhl_Gf_MZ4HroU4r_3rAR-QnyK0,16123
|
39
39
|
skfolio/optimization/convex/__init__.py,sha256=F6BPFikTo0B-7JCKazqLGEwM3RkgTNbFm5GAGkaq9Uo,570
|
40
|
-
skfolio/optimization/convex/_base.py,sha256=
|
41
|
-
skfolio/optimization/convex/_distributionally_robust.py,sha256=
|
42
|
-
skfolio/optimization/convex/_maximum_diversification.py,sha256=
|
43
|
-
skfolio/optimization/convex/_mean_risk.py,sha256=
|
44
|
-
skfolio/optimization/convex/_risk_budgeting.py,sha256=
|
40
|
+
skfolio/optimization/convex/_base.py,sha256=xvhgSQ5X7Lqz6kzGtldlDLIMV61gwrphW9_qMQSG498,75439
|
41
|
+
skfolio/optimization/convex/_distributionally_robust.py,sha256=PHAZvXb_zRXpeDD90fgxVEQAuYH5EY8fTpYQ8jO9shA,17311
|
42
|
+
skfolio/optimization/convex/_maximum_diversification.py,sha256=vqUJZdR28X75LQ18XXWLGd7yX9-f5wvOByN0UVSyd_Y,19140
|
43
|
+
skfolio/optimization/convex/_mean_risk.py,sha256=q9aH_thsmKeK_3-8jQOLN1S9z9DXvZmYGgj0a2ezAl0,43134
|
44
|
+
skfolio/optimization/convex/_risk_budgeting.py,sha256=pRMTA-CCTsV-yysADA3b9RDjLGPk4Jw0eP5ZVQ5W_sc,23622
|
45
45
|
skfolio/optimization/ensemble/__init__.py,sha256=8TXxcxH2_gG3C1xtgQj9OHHr0Le8lhdejtlURL6T3ZY,158
|
46
|
-
skfolio/optimization/ensemble/_base.py,sha256=
|
47
|
-
skfolio/optimization/ensemble/_stacking.py,sha256=
|
46
|
+
skfolio/optimization/ensemble/_base.py,sha256=GaNDQu6ivosYuwMrb-b0PhToCsNrmhSYyXkxeM8W4rU,3399
|
47
|
+
skfolio/optimization/ensemble/_stacking.py,sha256=2PodXf_JahToShSAbwQRXosEer3ESOAhpsnB6SCwztk,13148
|
48
48
|
skfolio/optimization/naive/__init__.py,sha256=Dkr55R48urC-jfYN007NTbei16N91Na_EDYLVqzhGgQ,147
|
49
|
-
skfolio/optimization/naive/_naive.py,sha256=
|
49
|
+
skfolio/optimization/naive/_naive.py,sha256=lXBUK0-JZd_30xUqnwv3IN1-yvKNVrub2A4ZVdfjmnY,5570
|
50
50
|
skfolio/population/__init__.py,sha256=rsPPMUv95aTK7vmpPeQwF8NzFuBwk6RDo5g4HNaPzNM,80
|
51
|
-
skfolio/population/_population.py,sha256=
|
51
|
+
skfolio/population/_population.py,sha256=DNBjR2Ob_Rz3B70mwLIrWd_ElOvHxDWjN3EQs-rGHCk,29148
|
52
52
|
skfolio/portfolio/__init__.py,sha256=YYtcAPmA2zeCxFGTXegg2FXcA7py6CxOX7IMTdYuXl0,586
|
53
|
-
skfolio/portfolio/_base.py,sha256=
|
53
|
+
skfolio/portfolio/_base.py,sha256=jO6YKXu8oxE-uldl2CayLvbA40yfeCA1vNa_xm-WcWo,38313
|
54
54
|
skfolio/portfolio/_multi_period_portfolio.py,sha256=63RwL0oTn-3L5LON6f2e9J58zw9q8S2gQ5c8JfSnN28,22759
|
55
|
-
skfolio/portfolio/_portfolio.py,sha256=
|
55
|
+
skfolio/portfolio/_portfolio.py,sha256=I2P-ogv78wx6aRytHN7z8BHLkye-E8T5LinVARgsuIQ,31649
|
56
56
|
skfolio/pre_selection/__init__.py,sha256=VtUtDn-U-Mn_xR2k7yfld0Yb0rPhLakEAiBwUyi-4Z8,189
|
57
57
|
skfolio/pre_selection/_pre_selection.py,sha256=w84T14nKmzkgzbw5CW_AIlci741lXYxKUwB5pBjhTTI,12163
|
58
58
|
skfolio/preprocessing/__init__.py,sha256=15A1bzfPsbfxxXgGP1gstf4R0E_347Wn18z5W5jH-hk,94
|
@@ -61,7 +61,7 @@ skfolio/prior/__init__.py,sha256=jql8NTiWlykPKJUXTOPdqm531mP8Pul1QAR6hXTXA6c,446
|
|
61
61
|
skfolio/prior/_base.py,sha256=Dx6rX0X6ymDiieFOI-ik3xMNNFhYEtwLSXOdajf5wZY,1927
|
62
62
|
skfolio/prior/_black_litterman.py,sha256=JuwVXLXY5qHETBZDg0wmBGMSQehJp_1t7c1-kanKaiU,9397
|
63
63
|
skfolio/prior/_empirical.py,sha256=YhCYW8R3vqs3ntF4WSokuwLW6Gd98df7f8LL8zCS-D0,5740
|
64
|
-
skfolio/prior/_factor_model.py,sha256=
|
64
|
+
skfolio/prior/_factor_model.py,sha256=xC-womM7oC91jRaIQP7MSiRPkwm5b7pkWHi-5mOb2jY,9665
|
65
65
|
skfolio/uncertainty_set/__init__.py,sha256=LlMHtYv9G9fgtM7m4sCSToS9et57Pm2Q2gGchTVrj6c,617
|
66
66
|
skfolio/uncertainty_set/_base.py,sha256=rZ3g2AhDKFQTPajgh6Fz5S5TTf0qM4Ie6RGxPhp32D8,3301
|
67
67
|
skfolio/uncertainty_set/_bootstrap.py,sha256=Rig8ZGc8rEOnYygPyTaDSu5lp3wGuZrQVRoJTm3UuAA,10301
|
@@ -71,11 +71,11 @@ skfolio/utils/bootstrap.py,sha256=3zY2kO_GQURKEcQMCasJOSByde9Mt2IAi3KJH0_a4mk,35
|
|
71
71
|
skfolio/utils/equations.py,sha256=w0HsYjA7cS0mHYsI9MpixHLkof3HN26nc14ZfqFrHlE,11047
|
72
72
|
skfolio/utils/sorting.py,sha256=lSjMvH2L-sSj-06B3MlwBrH1rtjCeGEe4hG894W7TE0,3504
|
73
73
|
skfolio/utils/stats.py,sha256=IP36nMc5j5Hcqjbg7lvDIsGp1GWRdOh5jU3W6Z8nkYs,13132
|
74
|
-
skfolio/utils/tools.py,sha256=
|
74
|
+
skfolio/utils/tools.py,sha256=xa42f7U3Ki8-CJS6g8w7bKCLI_QMJ8D6LxLBjlEM7Ok,15374
|
75
75
|
skfolio/utils/fixes/__init__.py,sha256=knHau8PRZP07XDHR59CW8VWxkpTP0gdr6RAHJrO-zaA,95
|
76
|
-
skfolio/utils/fixes/_dendrogram.py,sha256=
|
77
|
-
skfolio-0.
|
78
|
-
skfolio-0.
|
79
|
-
skfolio-0.
|
80
|
-
skfolio-0.
|
81
|
-
skfolio-0.
|
76
|
+
skfolio/utils/fixes/_dendrogram.py,sha256=9aIhSnMwpQHJhQx7IpXC3jlw6YJ3H4XQnnx_d4nMllQ,13551
|
77
|
+
skfolio-0.2.0.dist-info/LICENSE,sha256=F6Gi-ZJX5BlVzYK8R9NcvAkAsKa7KO29xB1OScbrH6Q,1526
|
78
|
+
skfolio-0.2.0.dist-info/METADATA,sha256=mTx4X6Oymgb_VzpoAXdel_WnafJFUR6Q5rKuL9Td4l8,19607
|
79
|
+
skfolio-0.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
80
|
+
skfolio-0.2.0.dist-info/top_level.txt,sha256=NXEaoS9Ms7t32gxkb867nV0OKlU0KmssL7IJBVo0fJs,8
|
81
|
+
skfolio-0.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|