diff --git a/garpar/core/portfolio.py b/garpar/core/portfolio.py index cbc4cc9..a7f6c88 100644 --- a/garpar/core/portfolio.py +++ b/garpar/core/portfolio.py @@ -55,89 +55,6 @@ def _as_float_array(arr): class Portfolio: """ Represents a financial portfolio with utilities for analysis and manipulation. - - Attributes - ---------- - _prices_df : pd.DataFrame - DataFrame containing the prices of the assets. - _weights : np.ndarray - Array of asset weights in the portfolio. - _entropy : np.ndarray - Array of entropy values associated with the assets. - _window_size : int or None - Window size for rolling calculations, if applicable. - _metadata : Bunch - Additional metadata related to the portfolio. - - plot : PortfolioPlotter - Accessor for plotting portfolio data. - prices : PricesAccessor - Accessor for price-related operations. - ereturns : ExpectedReturnsAccessor - Accessor for expected returns calculations. - covariance : CovarianceAccessor - Accessor for covariance matrix calculations. - correlation : CorrelationAccessor - Accessor for correlation matrix calculations. - risk : RiskAccessor - Accessor for risk-related operations. - utilities : UtilitiesAccessor - Accessor for utility-related operations. - diversification : DiversificationAccessor - Accessor for diversification-related operations. - - Methods - ------- - __attrs_post_init__() - Initialize additional attributes and performs validation. - from_dfkws(prices, weights=None, entropy=None, window_size=None, stocks=None, **metadata) - Alternative constructor to create a Portfolio instance from various inputs. - __len__() - Return the number of days in the price DataFrame. - __eq__(other) - Check equality with another Portfolio instance. - __ne__(other) - Check inequality with another Portfolio instance. - __getitem__(key) - Slice the Portfolio by the given key. - weights() - Return the weights as a pandas Series. - entropy() - Return the entropy values as a pandas Series. - stocks() - Return the stocks in the portfolio. - stocks_number() - Return the number of stocks in the portfolio. - metadata() - Return the metadata as a Bunch object. - window_size() - Return the window size for rolling calculations. - delisted() - Return a Series indicating if a stock has been delisted. - shape() - Return the shape of the price DataFrame. - copy(prices=None, weights=None, entropy=None, window_size=None, stocks=None, preserve_old_metadata=True, **metadata) - Create a copy of the Portfolio with optional modifications. - to_hdf5(stream_or_buff, **kwargs) - Save the Portfolio to an HDF5 file. - to_dataframe() - Convert the Portfolio to a pandas DataFrame. - as_returns(**kwargs) - Convert the price DataFrame to returns. - as_prices() - Return a copy of the price DataFrame. - weights_prune(threshold=1e-4) - Prune the Portfolio by removing assets with weights below the threshold. - delisted_prune() - Prune the Portfolio by removing delisted assets. - scale_weights(scaler="proportion") - Scale the weights to a range of [0, 1]. - refresh_entropy(entropy="shannon", entropy_kws=None) - Recalculate the entropy values for the Portfolio. - __repr__() - Return a string representation of the Portfolio. - _repr_html_() - Return an HTML representation of the Portfolio for IPython notebooks. """ _prices_df = attr.ib(validator=vldt.instance_of(pd.DataFrame)) @@ -703,7 +620,7 @@ def scale_weights(self, *, scaler="proportion"): return self.copy(weights=scaled_weights) # CALCULATE ENTROPY ======================================================= - # TODO + # TODO Risso def refresh_entropy(self, *, entropy="shannon", entropy_kws=None): """Refresh entropy values using a specified entropy calculation method. diff --git a/garpar/datasets/__init__.py b/garpar/datasets/__init__.py index 974e449..13b099b 100644 --- a/garpar/datasets/__init__.py +++ b/garpar/datasets/__init__.py @@ -8,7 +8,7 @@ """Different utilities to create or load portfolios.""" -from .base import PortfolioMakerABC, RandomEntropyPortfolioMakerABC +from .ds_base import PortfolioMakerABC, RandomEntropyPortfolioMakerABC from .data import load_MERVAL from .multisector import MultiSector, make_multisector from .risso import ( diff --git a/garpar/datasets/base.py b/garpar/datasets/ds_base.py similarity index 100% rename from garpar/datasets/base.py rename to garpar/datasets/ds_base.py diff --git a/garpar/datasets/multisector.py b/garpar/datasets/multisector.py index 1c5a5af..e36a309 100644 --- a/garpar/datasets/multisector.py +++ b/garpar/datasets/multisector.py @@ -11,7 +11,7 @@ import pandas as pd -from .base import PortfolioMakerABC +from .ds_base import PortfolioMakerABC from ..core.portfolio import Portfolio from ..utils import Bunch, mabc, unique_names diff --git a/garpar/datasets/risso.py b/garpar/datasets/risso.py index ae56f19..1d0842b 100644 --- a/garpar/datasets/risso.py +++ b/garpar/datasets/risso.py @@ -17,7 +17,7 @@ import scipy.stats -from .base import RandomEntropyPortfolioMakerABC +from .ds_base import RandomEntropyPortfolioMakerABC from ..utils import mabc diff --git a/garpar/optimize/__init__.py b/garpar/optimize/__init__.py index 6c6d24f..4e1bfb3 100644 --- a/garpar/optimize/__init__.py +++ b/garpar/optimize/__init__.py @@ -15,4 +15,4 @@ from . import mean_variance -__all__ = [ "mean_variance"] +__all__ = [ "mean_variance" ] diff --git a/garpar/optimize/mean_variance.py b/garpar/optimize/mean_variance.py index 09ac88c..8a02d51 100644 --- a/garpar/optimize/mean_variance.py +++ b/garpar/optimize/mean_variance.py @@ -9,7 +9,7 @@ import attr -from .base import OptimizerABC, MeanVarianceFamilyMixin +from .opt_base import OptimizerABC, MeanVarianceFamilyMixin from ..utils import mabc @@ -83,7 +83,7 @@ def _calculate_weights(self, pf): return optimization_methods[method](optimizer, pf) - def _min_volatiliy(self, optimizer, pf): + def _min_volatility(self, optimizer, pf): weights_dict = optimizer.min_volatility() weights = [weights_dict[stock] for stock in pf.stocks] return weights, {"name": "min_volatility"} diff --git a/garpar/optimize/base.py b/garpar/optimize/opt_base.py similarity index 100% rename from garpar/optimize/base.py rename to garpar/optimize/opt_base.py diff --git a/pyproject.toml b/pyproject.toml index 6f2d489..258fb96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,13 @@ [tool.black] line-length = 80 -target-version = ['py38', 'py39', 'py310', 'py311'] +target-version = ['py38', 'py39', 'py310', 'py311', 'py312'] + +[tool.pytest.ini_options] +markers = [ + "slow: marks tests that require significant execution time (deselect with '-m \"not slow\"')", + "plot: marks tests related to matplotlib integration (deselect with '-m \"not plot\"')", +] +testpaths = [ + "tests", +] +addopts = "-m 'not slow' -n auto" diff --git a/tests/core/test_plot_acc.py b/tests/core/test_plot_acc.py index 498207b..5e33bb6 100644 --- a/tests/core/test_plot_acc.py +++ b/tests/core/test_plot_acc.py @@ -14,6 +14,8 @@ import seaborn as sns +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("returns", [True, False]) @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) @@ -30,6 +32,8 @@ def test_PortFolioPlotter_line(risso_portfolio, fig_test, fig_ref, returns, pric ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("returns", [True, False]) @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) @@ -46,6 +50,8 @@ def test_PortFolioPlotter_heatmap(risso_portfolio, fig_test, fig_ref, returns, p ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) def test_PortFolioPlotter_wheatmap(risso_portfolio, fig_test, fig_ref, price_distribution): @@ -62,6 +68,8 @@ def test_PortFolioPlotter_wheatmap(risso_portfolio, fig_test, fig_ref, price_dis ax_ref.set_xlabel("Stocks") +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("returns", [True, False]) @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) @@ -78,6 +86,8 @@ def test_PortFolioPlotter_hist(risso_portfolio, fig_test, fig_ref, returns, pric ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) def test_PortFolioPlotter_whist(risso_portfolio, fig_test, fig_ref, price_distribution): @@ -93,6 +103,8 @@ def test_PortFolioPlotter_whist(risso_portfolio, fig_test, fig_ref, price_distri ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("returns", [True, False]) @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) @@ -109,6 +121,8 @@ def test_PortFolioPlotter_box(risso_portfolio, fig_test, fig_ref, returns, price ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) def test_PortFolioPlotter_wbox(risso_portfolio, fig_test, fig_ref, price_distribution): @@ -124,6 +138,8 @@ def test_PortFolioPlotter_wbox(risso_portfolio, fig_test, fig_ref, price_distrib ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("returns", [True, False]) @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) @@ -140,6 +156,8 @@ def test_PortFolioPlotter_kde(risso_portfolio, fig_test, fig_ref, returns, price ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) def test_PortFolioPlotter_wkde(risso_portfolio, fig_test, fig_ref, price_distribution): @@ -155,6 +173,8 @@ def test_PortFolioPlotter_wkde(risso_portfolio, fig_test, fig_ref, price_distrib ax_ref.set_title(title) +@pytest.mark.slow +@pytest.mark.plot @check_figures_equal() @pytest.mark.parametrize("returns", [True, False]) @pytest.mark.parametrize("price_distribution", pytest.DISTRIBUTIONS) diff --git a/tests/optimize/test_base_optimize.py b/tests/optimize/test_base_optimize.py index 25e1a65..f38a67f 100644 --- a/tests/optimize/test_base_optimize.py +++ b/tests/optimize/test_base_optimize.py @@ -13,7 +13,7 @@ from garpar import Portfolio -from garpar.optimize import base +from garpar.optimize import opt_base as base import numpy as np diff --git a/tests/optimize/test_mean_variance.py b/tests/optimize/test_mean_variance.py index 1d90611..25226da 100644 --- a/tests/optimize/test_mean_variance.py +++ b/tests/optimize/test_mean_variance.py @@ -24,7 +24,7 @@ import pytest # ============================================================================= -# MARKOWITS TEST +# MARKOWITZ TEST # ============================================================================= def test_Markowitz_optimize(): diff --git a/tox.ini b/tox.ini index 7188e58..ef1279e 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ envlist = py39, py310, py311, - #py312, no funca en arch (?) FIXME + py312, coverage, @@ -22,7 +22,7 @@ usedevelop = False deps = flake8 flake8-import-order flake8-black - #flake8-builtins + flake8-builtins commands = flake8 setup.py garpar/ tests/ {posargs} @@ -60,8 +60,9 @@ usedevelop = True deps = ipdb pytest + pytest-xdist commands = - pytest tests/ {posargs} + pytest tests/ -vm '' {posargs} [testenv:coverage] usedevelop = True @@ -71,5 +72,5 @@ deps = pytest-cov commands = - coverage erase # - => Segui en el que sigue - pytest -v tests/ --cov garpar --cov-fail-under 80 --cov-report term-missing --ignore=tests/core/test_plot_acc.py + pytest tests/ -m '' --cov garpar --cov-fail-under 91 --cov-report term-missing