From 5b8f1a5526f306e02f1b786fea7aa7d6879b21df Mon Sep 17 00:00:00 2001 From: JVthearchitect <120519530+JVthearchitect@users.noreply.github.com> Date: Mon, 8 Jan 2024 08:48:58 -0500 Subject: [PATCH 1/2] closes #65 add dividends_annual property to Portfolio includes test file "t_test_portfolio_dividends_annual.py" Please let me know if there are any errors or follow ups. --- okama/portfolio.py | 28 ++++++++++++++++++++++++++++ t_test_portfolio_dividends_annual.py | 21 +++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 t_test_portfolio_dividends_annual.py diff --git a/okama/portfolio.py b/okama/portfolio.py index fe7dbb2..1303f91 100644 --- a/okama/portfolio.py +++ b/okama/portfolio.py @@ -820,6 +820,34 @@ def dividend_yield(self) -> pd.Series: div_yield_series.rename(self.symbol, inplace=True) return div_yield_series + @property + def dividends_annual(self) -> pd.DataFrame: + """ + Return calendar year dividends sum time series for each asset. + + Returns + ------- + DataFrame + Annual dividends time series for each asset. + + See Also + -------- + dividend_yield : Dividend yield time series. + dividend_yield_annual : Calendar year dividend yield time series. + dividend_paying_years : Number of years of consecutive dividend payments. + dividend_growing_years : Number of years when the annual dividend was growing. + get_dividend_mean_yield : Arithmetic mean for annual dividend yield. + get_dividend_mean_growth_rate : Geometric mean of annual dividends growth rate. + + Examples + -------- + >>> import matplotlib.pyplot as plt + >>> x = ok.AssetList(['T.US', 'XOM.US'], first_date='2010-01', last_date='2020-12') + >>> x.dividends_annual.plot(kind='bar') + >>> plt.show() + """ + return self._get_assets_dividends().resample("Y").sum() + @property def assets_dividend_yield(self): """ diff --git a/t_test_portfolio_dividends_annual.py b/t_test_portfolio_dividends_annual.py new file mode 100644 index 0000000..a6eec66 --- /dev/null +++ b/t_test_portfolio_dividends_annual.py @@ -0,0 +1,21 @@ +import pytest +import okama as ok + +tickers = [ + "BND.US", + "VTI.US", + "VXUS.US" +] + +w = [0.34, 0.33, 0.33] + +portfolio = ok.Portfolio(tickers, weights=w, rebalancing_period="none", inflation=False) + +def test_dividend_yield(): + return portfolio.assets_dividend_yield + +def test_dividends_annual(): + return portfolio.dividends_annual + +# portfolio.assets_dividend_yield +# portfolio.dividends_annual \ No newline at end of file From 3d6a85b9d2967ae0417f460a1c23187a2c5ae9c2 Mon Sep 17 00:00:00 2001 From: Sourcery AI <> Date: Mon, 8 Jan 2024 19:12:32 +0000 Subject: [PATCH 2/2] 'Refactored by Sourcery' --- okama/portfolio.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/okama/portfolio.py b/okama/portfolio.py index 1303f91..38ecf1e 100644 --- a/okama/portfolio.py +++ b/okama/portfolio.py @@ -1118,9 +1118,7 @@ def recovery_period(self) -> int: s1 = s.where(s == 0).notnull().astype(int) s1_1 = s.where(s == 0).isnull().astype(int).cumsum() s2 = s1.groupby(s1_1).cumsum() - # Max recovery period date should not be in the border (means it's not recovered) - max_period = s2.max() if s2.idxmax().to_timestamp() != self.last_date else np.NAN - return max_period + return s2.max() if s2.idxmax().to_timestamp() != self.last_date else np.NAN def describe(self, years: Tuple[int] = (1, 5, 10)) -> pd.DataFrame: """ @@ -1332,7 +1330,7 @@ def percentile_inverse_cagr( """ if distr == "hist": cagr_distr = self.get_rolling_cagr(years * settings._MONTHS_PER_YEAR).loc[:, [self.symbol]].squeeze() - elif distr in ["norm", "lognorm"]: + elif distr in {"norm", "lognorm"}: if not n: n = 1000 cagr_distr = self._get_cagr_distribution(distr=distr, years=years, n=n) @@ -1381,7 +1379,7 @@ def percentile_history_cagr(self, years: int, percentiles: List[int] = [10, 50, self.get_rolling_cagr(years * 12).loc[:, self.symbol].quantile(percentile / 100) for years in period_range ] - returns_dict.update({percentile: percentile_returns_list}) + returns_dict[percentile] = percentile_returns_list df = pd.DataFrame(returns_dict, index=list(period_range)) df.index.rename("years", inplace=True) return df @@ -1613,7 +1611,7 @@ def percentile_distribution_cagr( results = {} for percentile in percentiles: value = cagr_distr.quantile(percentile / 100) - results.update({percentile: value}) + results[percentile] = value return results def percentile_wealth( @@ -1669,12 +1667,12 @@ def percentile_wealth( """ if distr == "hist": results = self.percentile_wealth_history(years=years, percentiles=percentiles).iloc[-1].to_dict() - elif distr in ["norm", "lognorm"]: + elif distr in {"norm", "lognorm"}: results = {} wealth_indexes = self._monte_carlo_wealth(distr=distr, years=years, n=n) for percentile in percentiles: value = wealth_indexes.iloc[-1, :].quantile(percentile / 100) - results.update({percentile: value}) + results[percentile] = value else: raise ValueError('distr should be "norm", "lognorm" or "hist".') if today_value: