Skip to content

Commit 531a19a

Browse files
committed
feat: add mdp_points property to EfficientFrontier
mdp_points is a DataFrame with the Most diversified portfolios points.
1 parent 70fe96e commit 531a19a

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import okama as ok
22

3-
pf = ok.Portfolio(['SPY.US', 'AGG.US'], weights=[.7, .3])
3+
ls3 = ['MCFTR.INDX', 'RGBITR.INDX', 'GC.COMM']
4+
y = ok.EfficientFrontier(assets=ls3, ccy='USD', n_points=10)
45

5-
print(pf.diversification_ratio)
6+
print(y.mdp_points)

okama/frontier/single_period.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def __init__(
9393
self.n_points = n_points
9494
self.labels_are_tickers = ticker_names
9595
self._ef_points = pd.DataFrame(dtype=float)
96+
self._mdp_points = pd.DataFrame(dtype=float)
9697

9798
def __repr__(self):
9899
dic = {
@@ -707,6 +708,77 @@ def ef_points(self) -> pd.DataFrame:
707708
self._ef_points = df
708709
return self._ef_points
709710

711+
@property
712+
def mdp_points(self) -> pd.DataFrame:
713+
"""
714+
Generate Most diversified portfolios line.
715+
716+
Each point on the Most diversified portfolios line is a portfolio with optimized
717+
Diversification ratio for a given return.
718+
719+
The points are obtained through the constrained optimization process (optimization with bounds).
720+
Bounds are defined with 'bounds' property.
721+
722+
Returns
723+
-------
724+
DataFrame
725+
Table of weights and risk/return values for the Efficient Frontier.
726+
The columns:
727+
728+
- assets weights
729+
- CAGR (geometric mean)
730+
- Mean return (arithmetic mean)
731+
- Risk (standard deviation)
732+
- Diversification ratio
733+
734+
All the values are annualized.
735+
736+
Examples
737+
--------
738+
>>> ls4 = ['SP500TR.INDX', 'MCFTR.INDX', 'RGBITR.INDX', 'GC.COMM']
739+
>>> y = ok.EfficientFrontier(assets=ls4, ccy='RUB', last_date='2021-12', n_points=100)
740+
>>> y.mdp_points # print mdp weights, risk, mean return, CAGR and Diversification ratio
741+
Risk Mean return CAGR ... MCFTR.INDX RGBITR.INDX SP500TR.INDX
742+
0 0.066040 0.094216 0.092220 ... 2.081668e-16 1.000000e+00 0.000000e+00
743+
1 0.064299 0.095342 0.093451 ... 0.000000e+00 9.844942e-01 5.828671e-16
744+
2 0.062761 0.096468 0.094670 ... 0.000000e+00 9.689885e-01 1.110223e-16
745+
3 0.061445 0.097595 0.095874 ... 5.828671e-16 9.534827e-01 0.000000e+00
746+
4 0.060364 0.098724 0.097065 ... 3.191891e-16 9.379769e-01 0.000000e+00
747+
.. ... ... ... ... ... ... ...
748+
95 0.258857 0.205984 0.178346 ... 8.840844e-01 1.387779e-17 0.000000e+00
749+
96 0.266583 0.207214 0.177941 ... 9.130633e-01 3.469447e-18 0.000000e+00
750+
97 0.274594 0.208446 0.177432 ... 9.420422e-01 0.000000e+00 1.075529e-16
751+
98 0.282873 0.209678 0.176820 ... 9.710211e-01 2.428613e-17 6.938894e-18
752+
99 0.291402 0.210912 0.176103 ... 1.000000e+00 2.775558e-16 3.951094e-09
753+
[100 rows x 8 columns]
754+
755+
To plot the Most diversification portfolios line use the DataFrame with the points data.
756+
Additionaly 'Plot.plot_assets()' can be used to show the assets in the chart.
757+
758+
>>> import matplotlib.pyplot as plt
759+
>>> fig = plt.figure()
760+
>>> # Plot the assets points
761+
>>> y.plot_assets(kind='cagr') # kind should be set to "cagr" as we take "CAGR" column from the ef_points.
762+
>>> ax = plt.gca()
763+
>>> # Plot the Most diversified portfolios line
764+
>>> df = y.mdp_points
765+
>>> ax.plot(df['Risk'], df['CAGR']) # we chose to plot CAGR which is geometric mean of return series
766+
>>> # Set the axis labels and the title
767+
>>> ax.set_title('Most diversified portfolios line')
768+
>>> ax.set_xlabel('Risk (Standard Deviation)')
769+
>>> ax.set_ylabel('Return (CAGR)')
770+
>>> plt.show()
771+
"""
772+
if self._mdp_points.empty:
773+
target_rs = self.mean_return_range
774+
df = pd.DataFrame(dtype="float")
775+
for x in target_rs:
776+
row = self.get_most_diversified_portfolio(target_return=x, monthly_return=True)
777+
df = df.append(row, ignore_index=True)
778+
df = Frame.change_columns_order(df, ["Risk", "Mean return", "CAGR"])
779+
self._mdp_points = df
780+
return self._mdp_points
781+
710782
def get_monte_carlo(self, n: int = 100, kind: str = "mean") -> pd.DataFrame:
711783
"""
712784
Generate N random portfolios with Monte Carlo simulation.

tests/test_frontier.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,20 @@ def test_get_most_diversified_portfolio(init_efficient_frontier):
126126
assert_series_equal(df, df_expected, rtol=1e-03)
127127

128128

129+
@mark.frontier
130+
def test_mdp_points(init_efficient_frontier_three_assets):
131+
assert init_efficient_frontier_three_assets.mdp_points['Mean return'].iloc[10] == approx(0.12039, rel=1e-2)
132+
assert init_efficient_frontier_three_assets.mdp_points['Diversification ratio'].iloc[10] == approx(1.6050, rel=1e-2)
133+
134+
129135
@mark.frontier
130136
def test_plot_cml(init_efficient_frontier):
131137
rf_rate = 0.02
132138
axes_data = np.array(init_efficient_frontier.plot_cml(rf_return=rf_rate).lines[1].get_data())
133139
expected = np.array([[0, 0.11053], [0.02, 0.1578]])
134140
assert_allclose(axes_data, expected, atol=1e-2)
135141

142+
136143
@mark.frontier
137144
def test_plot_transition_map(init_efficient_frontier_three_assets):
138145
axes_data = np.array(init_efficient_frontier_three_assets.plot_transition_map(cagr=False).lines[0].get_data())

0 commit comments

Comments
 (0)