skfolio 0.0.10__py3-none-any.whl → 0.1.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/cluster/_hierarchical.py +5 -4
- skfolio/datasets/_base.py +5 -0
- skfolio/distance/_base.py +1 -0
- skfolio/distance/_distance.py +1 -0
- skfolio/exceptions.py +1 -0
- skfolio/measures/_enums.py +1 -0
- skfolio/measures/_measures.py +28 -0
- skfolio/metrics/_scorer.py +5 -0
- skfolio/model_selection/_combinatorial.py +5 -0
- skfolio/model_selection/_validation.py +5 -0
- skfolio/model_selection/_walk_forward.py +6 -1
- skfolio/moments/covariance/_base.py +7 -0
- skfolio/moments/covariance/_covariance.py +7 -0
- skfolio/moments/expected_returns/_base.py +4 -0
- skfolio/moments/expected_returns/_expected_returns.py +5 -0
- skfolio/optimization/_base.py +8 -0
- skfolio/optimization/cluster/_nco.py +5 -0
- skfolio/optimization/cluster/hierarchical/_base.py +4 -0
- skfolio/optimization/cluster/hierarchical/_herc.py +3 -1
- skfolio/optimization/cluster/hierarchical/_hrp.py +4 -0
- skfolio/optimization/convex/_base.py +4 -1
- skfolio/optimization/convex/_distributionally_robust.py +1 -0
- skfolio/optimization/convex/_maximum_diversification.py +2 -1
- skfolio/optimization/convex/_mean_risk.py +5 -2
- skfolio/optimization/convex/_risk_budgeting.py +3 -0
- skfolio/optimization/ensemble/_base.py +4 -0
- skfolio/optimization/ensemble/_stacking.py +4 -0
- skfolio/population/_population.py +1 -0
- skfolio/portfolio/_base.py +9 -9
- skfolio/portfolio/_multi_period_portfolio.py +9 -5
- skfolio/portfolio/_portfolio.py +3 -2
- skfolio/pre_selection/_pre_selection.py +1 -0
- skfolio/preprocessing/_returns.py +2 -1
- skfolio/prior/_base.py +1 -0
- skfolio/prior/_black_litterman.py +4 -0
- skfolio/prior/_empirical.py +2 -0
- skfolio/prior/_factor_model.py +5 -0
- skfolio/typing.py +1 -0
- skfolio/uncertainty_set/_base.py +1 -0
- skfolio/uncertainty_set/_bootstrap.py +5 -0
- skfolio/uncertainty_set/_empirical.py +5 -0
- skfolio/utils/bootstrap.py +3 -0
- skfolio/utils/equations.py +1 -0
- skfolio/utils/fixes/__init__.py +3 -0
- skfolio/utils/fixes/_dendrogram.py +389 -0
- skfolio/utils/sorting.py +1 -0
- skfolio/utils/stats.py +4 -1
- skfolio/utils/tools.py +4 -0
- {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/METADATA +18 -7
- skfolio-0.1.0.dist-info/RECORD +81 -0
- skfolio-0.0.10.dist-info/RECORD +0 -79
- {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/LICENSE +0 -0
- {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/WHEEL +0 -0
- {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/top_level.txt +0 -0
skfolio/cluster/_hierarchical.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Hierarchical Clustering estimators."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
5
6
|
|
@@ -7,13 +8,13 @@ from enum import auto
|
|
7
8
|
|
8
9
|
import numpy as np
|
9
10
|
import numpy.typing as npt
|
10
|
-
import plotly.figure_factory as ff
|
11
11
|
import plotly.graph_objects as go
|
12
12
|
import scipy.cluster.hierarchy as sch
|
13
13
|
import scipy.spatial.distance as scd
|
14
14
|
import sklearn.base as skb
|
15
15
|
import sklearn.utils.validation as skv
|
16
16
|
|
17
|
+
from skfolio.utils.fixes import create_dendrogram
|
17
18
|
from skfolio.utils.stats import assert_is_distance, compute_optimal_n_clusters
|
18
19
|
from skfolio.utils.tools import AutoEnum, default_asset_names
|
19
20
|
|
@@ -244,7 +245,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
|
|
244
245
|
asset_names = default_asset_names(n_assets=n_assets)
|
245
246
|
|
246
247
|
if not heatmap:
|
247
|
-
fig =
|
248
|
+
fig = create_dendrogram(
|
248
249
|
np.ones(1),
|
249
250
|
distfun=lambda x: None,
|
250
251
|
linkagefun=lambda x: linkage_matrix,
|
@@ -263,7 +264,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
|
|
263
264
|
return fig
|
264
265
|
|
265
266
|
# Initialize figure by creating upper dendrogram
|
266
|
-
fig =
|
267
|
+
fig = create_dendrogram(
|
267
268
|
np.ones(1),
|
268
269
|
orientation="bottom",
|
269
270
|
distfun=lambda x: None,
|
@@ -276,7 +277,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
|
|
276
277
|
fig["data"][i]["yaxis"] = "y2"
|
277
278
|
|
278
279
|
# Create Side Dendrogram
|
279
|
-
side_dendrogram =
|
280
|
+
side_dendrogram = create_dendrogram(
|
280
281
|
np.ones(1),
|
281
282
|
orientation="right",
|
282
283
|
distfun=lambda x: None,
|
skfolio/datasets/_base.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Datasets module."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
import gzip
|
7
12
|
import os
|
skfolio/distance/_base.py
CHANGED
skfolio/distance/_distance.py
CHANGED
skfolio/exceptions.py
CHANGED
skfolio/measures/_enums.py
CHANGED
skfolio/measures/_measures.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
"""Module that includes all Measures functions used across `skfolio`."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Gini mean difference and OWA GMD weights features are derived
|
7
|
+
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
5
8
|
|
6
9
|
|
7
10
|
import numpy as np
|
@@ -605,3 +608,28 @@ def gini_mean_difference(returns: np.ndarray) -> float:
|
|
605
608
|
"""
|
606
609
|
w = owa_gmd_weights(len(returns))
|
607
610
|
return float(w @ np.sort(returns, axis=0))
|
611
|
+
|
612
|
+
|
613
|
+
def effective_number_assets(weights: np.ndarray) -> float:
|
614
|
+
r"""
|
615
|
+
Computes the effective number of assets, defined as the inverse of the Herfindahl index [1]_:
|
616
|
+
.. math:: N_{eff} = \frac{1}{\Vert w \Vert_{2}^{2}}
|
617
|
+
|
618
|
+
It quantifies portfolio concentration, with a higher value indicating a more diversified portfolio.
|
619
|
+
|
620
|
+
Parameters
|
621
|
+
----------
|
622
|
+
weights : ndarray of shape (n_assets,)
|
623
|
+
Weights of the assets.
|
624
|
+
|
625
|
+
Returns
|
626
|
+
-------
|
627
|
+
value : float
|
628
|
+
Effective number of assets.
|
629
|
+
|
630
|
+
References
|
631
|
+
----------
|
632
|
+
.. [1] "Banking and Financial Institutions Law in a Nutshell".
|
633
|
+
Lovett, William Anthony (1988)
|
634
|
+
"""
|
635
|
+
return 1.0 / (np.power(weights, 2).sum())
|
skfolio/metrics/_scorer.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Scorer module"""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
from collections.abc import Callable
|
7
12
|
|
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Combinatorial module"""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
import itertools
|
7
12
|
import math
|
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Model validation module."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
import numpy as np
|
7
12
|
import numpy.typing as npt
|
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Walk Forward cross-validator"""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
from collections.abc import Iterator
|
7
12
|
|
@@ -39,7 +44,7 @@ class WalkForward(skm.BaseCrossValidator):
|
|
39
44
|
|
40
45
|
reduce_test : bool, default=False
|
41
46
|
If this is set to True, the last train/test split will be returned even if the
|
42
|
-
test set is partial (if it
|
47
|
+
test set is partial (if it contains less observations than `test_size`),
|
43
48
|
otherwise it will be ignored.
|
44
49
|
The default is `False`
|
45
50
|
|
@@ -1,5 +1,12 @@
|
|
1
1
|
"""Base Covariance Estimators."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
4
|
+
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
+
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
8
|
+
# Grisel Licensed under BSD 3 clause.
|
9
|
+
|
3
10
|
from abc import ABC, abstractmethod
|
4
11
|
|
5
12
|
import numpy as np
|
@@ -1,5 +1,12 @@
|
|
1
1
|
"""Covariance Estimators."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
4
|
+
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
5
|
+
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
8
|
+
# Grisel Licensed under BSD 3 clause.
|
9
|
+
|
3
10
|
import numpy as np
|
4
11
|
import numpy.typing as npt
|
5
12
|
import pandas as pd
|
@@ -1,7 +1,11 @@
|
|
1
1
|
"""Base Expected returns estimators."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
8
|
+
# Grisel Licensed under BSD 3 clause.
|
5
9
|
|
6
10
|
from abc import ABC, abstractmethod
|
7
11
|
|
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Expected returns estimators."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
from enum import auto
|
7
12
|
|
skfolio/optimization/_base.py
CHANGED
@@ -14,6 +14,14 @@ from skfolio.measures import RatioMeasure
|
|
14
14
|
from skfolio.population import Population
|
15
15
|
from skfolio.portfolio import Portfolio
|
16
16
|
|
17
|
+
# Copyright (c) 2023
|
18
|
+
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
19
|
+
# License: BSD 3 clause
|
20
|
+
# Implementation derived from:
|
21
|
+
# scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
|
22
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
23
|
+
# Grisel Licensed under BSD 3 clause.
|
24
|
+
|
17
25
|
|
18
26
|
class BaseOptimization(skb.BaseEstimator, ABC):
|
19
27
|
"""Base class for all portfolio optimizations in skfolio.
|
@@ -1,7 +1,12 @@
|
|
1
1
|
"""Nested Clusters Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
|
+
# Grisel Licensed under BSD 3 clause.
|
5
10
|
|
6
11
|
from copy import deepcopy
|
7
12
|
|
@@ -1,7 +1,11 @@
|
|
1
1
|
"""Base Hierarchical Clustering Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
5
9
|
|
6
10
|
from abc import ABC, abstractmethod
|
7
11
|
|
@@ -1,8 +1,10 @@
|
|
1
1
|
"""Hierarchical Equal Risk Contribution estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
5
|
-
|
6
|
+
# The risk measure generalization and constraint features are derived
|
7
|
+
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
6
8
|
|
7
9
|
import numpy as np
|
8
10
|
import numpy.typing as npt
|
@@ -1,7 +1,11 @@
|
|
1
1
|
"""Hierarchical Risk Parity Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# The risk measure generalization and constraint features are derived
|
7
|
+
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
|
+
|
5
9
|
|
6
10
|
import numpy as np
|
7
11
|
import numpy.typing as npt
|
@@ -1,7 +1,10 @@
|
|
1
1
|
"""Base Convex Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# The optimization features are derived
|
7
|
+
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
5
8
|
|
6
9
|
import warnings
|
7
10
|
from abc import ABC, abstractmethod
|
@@ -43,7 +46,7 @@ class ObjectiveFunction(AutoEnum):
|
|
43
46
|
MAXIMIZE_UTILITY : str
|
44
47
|
Maximize the utility :math:`w^T\mu - \lambda \times risk(w)`.
|
45
48
|
|
46
|
-
|
49
|
+
MAXIMIZE_RATIO : str
|
47
50
|
Maximize the ratio :math:`\frac{w^T\mu - R_{f}}{risk(w)}`.
|
48
51
|
"""
|
49
52
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Maximum Diversification Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
5
6
|
|
@@ -21,7 +22,7 @@ class MaximumDiversification(MeanRisk):
|
|
21
22
|
|
22
23
|
It is a special case of the :class:`~skfolio.optimization.MeanRisk` estimator where
|
23
24
|
the expected return from the objective function is replaced by the weighted
|
24
|
-
|
25
|
+
volatilities.
|
25
26
|
|
26
27
|
Parameters
|
27
28
|
----------
|
@@ -1,7 +1,10 @@
|
|
1
1
|
"""Mean Risk Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# The optimization features are derived
|
7
|
+
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
5
8
|
|
6
9
|
import cvxpy as cp
|
7
10
|
import numpy as np
|
@@ -931,7 +934,7 @@ class MeanRisk(ConvexOptimization):
|
|
931
934
|
case ObjectiveFunction.MAXIMIZE_RATIO:
|
932
935
|
if expected_return.is_affine():
|
933
936
|
# Charnes-Cooper's variable transformation for Fractional
|
934
|
-
#
|
937
|
+
# Programming problem :Max(f1/f2) with f2 linear
|
935
938
|
constraints += [
|
936
939
|
expected_return * self._scale_constraints
|
937
940
|
- cp.Constant(self.risk_free_rate)
|
@@ -941,7 +944,7 @@ class MeanRisk(ConvexOptimization):
|
|
941
944
|
]
|
942
945
|
else:
|
943
946
|
# Schaible's generalization of Charnes-Cooper's variable
|
944
|
-
# transformation for Fractional
|
947
|
+
# transformation for Fractional Programming problem :Max(f1/f2)
|
945
948
|
# with f1 concave instead of linear: Schaible,"Parameter-free
|
946
949
|
# Convex Equivalent and Dual Programs of Fractional Programming
|
947
950
|
# Problems".
|
@@ -1,7 +1,10 @@
|
|
1
1
|
"""Risk Budgeting Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# The optimization features are derived
|
7
|
+
# from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
5
8
|
|
6
9
|
import cvxpy as cp
|
7
10
|
import numpy as np
|
@@ -2,8 +2,12 @@
|
|
2
2
|
Follow same implementation as Base composition from sklearn
|
3
3
|
"""
|
4
4
|
|
5
|
+
# Copyright (c) 2023
|
5
6
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
6
7
|
# License: BSD 3 clause
|
8
|
+
# Implementation derived from:
|
9
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
10
|
+
# Grisel Licensed under BSD 3 clause.
|
7
11
|
|
8
12
|
from abc import ABC, abstractmethod
|
9
13
|
from contextlib import suppress
|
@@ -1,7 +1,11 @@
|
|
1
1
|
"""Stacking Optimization estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
8
|
+
# Grisel Licensed under BSD 3 clause.
|
5
9
|
|
6
10
|
from copy import deepcopy
|
7
11
|
|
skfolio/portfolio/_base.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Base Portfolio module"""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
5
6
|
|
@@ -97,7 +98,7 @@ class BasePortfolio:
|
|
97
98
|
compute domination.
|
98
99
|
The default (`None`) is to use the list [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
|
99
100
|
|
100
|
-
annualized_factor : float, default=
|
101
|
+
annualized_factor : float, default=252.0
|
101
102
|
Factor used to annualize the below measures using the square-root rule:
|
102
103
|
|
103
104
|
* Annualized Mean = Mean * factor
|
@@ -368,7 +369,6 @@ class BasePortfolio:
|
|
368
369
|
_read_only_attrs: ClassVar[set] = {
|
369
370
|
"returns",
|
370
371
|
"observations",
|
371
|
-
"n_observations",
|
372
372
|
}
|
373
373
|
|
374
374
|
# Arguments globally used in measures computation
|
@@ -400,7 +400,6 @@ class BasePortfolio:
|
|
400
400
|
# public read-only
|
401
401
|
"returns",
|
402
402
|
"observations",
|
403
|
-
"n_observations",
|
404
403
|
# private
|
405
404
|
"_loaded",
|
406
405
|
# custom getter and setter
|
@@ -484,7 +483,7 @@ class BasePortfolio:
|
|
484
483
|
observations: np.ndarray | list,
|
485
484
|
name: str | None = None,
|
486
485
|
tag: str | None = None,
|
487
|
-
annualized_factor: float =
|
486
|
+
annualized_factor: float = 252.0,
|
488
487
|
fitness_measures: list[skt.Measure] | None = None,
|
489
488
|
risk_free_rate: float = 0.0,
|
490
489
|
compounded: bool = False,
|
@@ -520,7 +519,6 @@ class BasePortfolio:
|
|
520
519
|
self._fitness_measures = [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
|
521
520
|
else:
|
522
521
|
self._fitness_measures = fitness_measures
|
523
|
-
self.n_observations = len(observations)
|
524
522
|
self._loaded = True
|
525
523
|
|
526
524
|
def __reduce__(self):
|
@@ -530,9 +528,6 @@ class BasePortfolio:
|
|
530
528
|
[getattr(self, arg) for arg in args_names(self.__init__)]
|
531
529
|
)
|
532
530
|
|
533
|
-
def __len__(self) -> int:
|
534
|
-
return len(self.observations)
|
535
|
-
|
536
531
|
def __repr__(self) -> str:
|
537
532
|
return f"<{type(self).__name__} {self.name}>"
|
538
533
|
|
@@ -678,6 +673,11 @@ class BasePortfolio:
|
|
678
673
|
return mt.get_drawdowns(returns=self.returns, compounded=self.compounded)
|
679
674
|
|
680
675
|
# Classic property
|
676
|
+
@property
|
677
|
+
def n_observations(self) -> int:
|
678
|
+
"""Number of observations"""
|
679
|
+
return len(self.observations)
|
680
|
+
|
681
681
|
@property
|
682
682
|
def returns_df(self) -> pd.Series:
|
683
683
|
"""Portfolio returns DataFrame."""
|
@@ -705,7 +705,7 @@ class BasePortfolio:
|
|
705
705
|
return self.__copy__()
|
706
706
|
|
707
707
|
def clear(self) -> None:
|
708
|
-
"""
|
708
|
+
"""Clear all measures, fitness, cumulative returns and drawdowns in slots"""
|
709
709
|
attrs = ["_fitness", "_cumulative_returns", "_drawdowns"]
|
710
710
|
for attr in attrs + list(_MEASURES_VALUES):
|
711
711
|
delattr(self, attr)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
`MultiPeriodPortfolio` is a list of `Portfolio`.
|
4
4
|
"""
|
5
5
|
|
6
|
+
# Copyright (c) 2023
|
6
7
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
7
8
|
# License: BSD 3 clause
|
8
9
|
|
@@ -45,7 +46,7 @@ class MultiPeriodPortfolio(BasePortfolio):
|
|
45
46
|
compute domination.
|
46
47
|
The default (`None`) is to use the list [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
|
47
48
|
|
48
|
-
annualized_factor : float, default=
|
49
|
+
annualized_factor : float, default=252.0
|
49
50
|
Factor used to annualize the below measures using the square-root rule:
|
50
51
|
|
51
52
|
* Annualized Mean = Mean * factor
|
@@ -332,7 +333,7 @@ class MultiPeriodPortfolio(BasePortfolio):
|
|
332
333
|
name: str | None = None,
|
333
334
|
tag: str | None = None,
|
334
335
|
risk_free_rate: float = 0,
|
335
|
-
annualized_factor: float =
|
336
|
+
annualized_factor: float = 252.0,
|
336
337
|
fitness_measures: list[skt.Measure] | None = None,
|
337
338
|
compounded: bool = False,
|
338
339
|
min_acceptable_return: float | None = None,
|
@@ -368,6 +369,9 @@ class MultiPeriodPortfolio(BasePortfolio):
|
|
368
369
|
self.check_observations_order = check_observations_order
|
369
370
|
self._set_portfolios(portfolios=portfolios)
|
370
371
|
|
372
|
+
def __len__(self) -> int:
|
373
|
+
return len(self.portfolios)
|
374
|
+
|
371
375
|
def __getitem__(self, key: int | slice) -> Portfolio | list[Portfolio]:
|
372
376
|
return self._portfolios[key]
|
373
377
|
|
@@ -570,12 +574,12 @@ class MultiPeriodPortfolio(BasePortfolio):
|
|
570
574
|
"""
|
571
575
|
df = super().summary(formatted=formatted)
|
572
576
|
portfolios_number = len(self)
|
573
|
-
avg_assets_per_portfolio = np.mean([
|
577
|
+
avg_assets_per_portfolio = np.mean([p.n_assets for p in self])
|
574
578
|
if formatted:
|
575
579
|
portfolios_number = str(int(portfolios_number))
|
576
580
|
avg_assets_per_portfolio = f"{avg_assets_per_portfolio:0.1f}"
|
577
|
-
df["Portfolios
|
578
|
-
df["Avg nb of
|
581
|
+
df["Portfolios Number"] = portfolios_number
|
582
|
+
df["Avg nb of Assets per Portfolio"] = avg_assets_per_portfolio
|
579
583
|
return df
|
580
584
|
|
581
585
|
# Public methods
|
skfolio/portfolio/_portfolio.py
CHANGED
@@ -4,6 +4,7 @@ It needs to be homogenous to the convex optimization problems meaning that `Port
|
|
4
4
|
is the dot product of the assets weights with the assets returns.
|
5
5
|
"""
|
6
6
|
|
7
|
+
# Copyright (c) 2023
|
7
8
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
8
9
|
# License: BSD 3 clause
|
9
10
|
|
@@ -132,7 +133,7 @@ class Portfolio(BasePortfolio):
|
|
132
133
|
compute domination.
|
133
134
|
The default (`None`) is to use the list [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
|
134
135
|
|
135
|
-
annualized_factor : float, default=
|
136
|
+
annualized_factor : float, default=252.0
|
136
137
|
Factor used to annualize the below measures using the square-root rule:
|
137
138
|
|
138
139
|
* Annualized Mean = Mean * factor
|
@@ -439,7 +440,7 @@ class Portfolio(BasePortfolio):
|
|
439
440
|
risk_free_rate: float = 0,
|
440
441
|
name: str | None = None,
|
441
442
|
tag: str | None = None,
|
442
|
-
annualized_factor: float =
|
443
|
+
annualized_factor: float = 252,
|
443
444
|
fitness_measures: list[skt.Measure] | None = None,
|
444
445
|
compounded: bool = False,
|
445
446
|
min_acceptable_return: float | None = None,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Preprocessing module to transform X to returns."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
5
6
|
|
@@ -81,7 +82,7 @@ def prices_to_returns(
|
|
81
82
|
else:
|
82
83
|
if not isinstance(y, pd.DataFrame):
|
83
84
|
raise TypeError("`y` must be a DataFrame")
|
84
|
-
df =
|
85
|
+
df = X.join(y, how="outer")
|
85
86
|
|
86
87
|
n_observations, n_assets = X.shape
|
87
88
|
|
skfolio/prior/_base.py
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
"""Black & Litterman Prior Model estimator."""
|
2
2
|
|
3
|
+
# Copyright (c) 2023
|
3
4
|
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
5
|
# License: BSD 3 clause
|
6
|
+
# Implementation derived from:
|
7
|
+
# Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
|
8
|
+
# PyPortfolioOpt, Copyright (c) 2018 Robert Andrew Martin, Licensed under MIT Licence.
|
5
9
|
|
6
10
|
import numpy as np
|
7
11
|
import numpy.typing as npt
|