Skip to content

Commit 946079a

Browse files
committed
feat: add plot_transition_map to EfficientFrontier
1 parent 91b7216 commit 946079a

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

okama/frontier/multi_period.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pandas as pd
77
import matplotlib.pyplot as plt
88
from joblib import Parallel, delayed
9+
from matplotlib.axes import Axes
910

1011
from scipy.optimize import minimize
1112

@@ -1284,7 +1285,7 @@ def get_monte_carlo(self, n: int = 100) -> pd.DataFrame:
12841285
random_portfolios = pd.concat([random_portfolios, pd.DataFrame(row, index=[0])], ignore_index=True)
12851286
return random_portfolios
12861287

1287-
def plot_pair_ef(self, tickers="tickers", figsize: Optional[tuple] = None) -> plt.axes:
1288+
def plot_pair_ef(self, tickers="tickers", figsize: Optional[tuple] = None) -> Axes:
12881289
"""
12891290
Plot Efficient Frontier of every pair of assets.
12901291
@@ -1419,3 +1420,73 @@ def plot_cml(self, rf_return: float = 0, figsize: Optional[tuple] = None):
14191420
# plot the assets
14201421
self.plot_assets(kind="cagr")
14211422
return ax
1423+
1424+
def plot_transition_map(self, x_axe: str = "risk", figsize: Optional[tuple] = None) -> Axes:
1425+
"""
1426+
Plot Transition Map for optimized portfolios on the Efficient Frontier.
1427+
1428+
Transition Map shows the relation between asset weights and optimized portfolios properties:
1429+
1430+
- CAGR (Compound annual growth rate)
1431+
- Risk (annualized standard deviation of return)
1432+
1433+
Wights are displayed on the y-axis.
1434+
CAGR or Risk - on the x-axis.
1435+
1436+
Constrained optimization with weights bounds is available.
1437+
1438+
Returns
1439+
-------
1440+
Axes : 'matplotlib.axes._subplots.AxesSubplot'
1441+
1442+
Parameters
1443+
----------
1444+
x_axe : {'risk', 'cagr'}, default 'risk'
1445+
Show the relation between weights and CAGR (if 'cagr') or between weights and Risk (if 'risk').
1446+
CAGR or Risk are displayed on the x-axis.
1447+
1448+
figsize : (float, float), optional
1449+
Figure size: width, height in inches.
1450+
If None default matplotlib size is taken: [6.4, 4.8]
1451+
1452+
Examples
1453+
--------
1454+
>>> import matplotlib.pyplot as plt
1455+
>>> x = ok.EfficientFrontier(['SPY.US', 'AGG.US', 'GLD.US'], ccy='USD', inflation=False)
1456+
>>> x.plot_transition_map()
1457+
>>> plt.show()
1458+
1459+
Transition Map with default setting show the relation between Risk (stanrd deviation) and assets weights for
1460+
optimized portfolios.
1461+
The same relation for CAGR can be shown setting x_axe='cagr'.
1462+
1463+
>>> x.plot_transition_map(x_axe='cagr')
1464+
>>> plt.show()
1465+
"""
1466+
ef = self.ef_points
1467+
linestyle = itertools.cycle(("-", "--", ":", "-."))
1468+
if x_axe.lower() == "cagr":
1469+
xlabel = "CAGR (Compound Annual Growth Rate)"
1470+
x_axe = "CAGR"
1471+
elif x_axe.lower() == "risk":
1472+
xlabel = "Risk (volatility)"
1473+
x_axe = "Risk"
1474+
else:
1475+
raise ValueError("x_axe parameter must be 'cagr' or 'risk'.")
1476+
fig, ax = plt.subplots(figsize=figsize)
1477+
for i in ef:
1478+
if i not in (
1479+
"Risk",
1480+
"Mean return",
1481+
"CAGR",
1482+
"Weights",
1483+
"iterations",
1484+
"init_guess",
1485+
): # select only columns with tickers
1486+
ax.plot(ef[x_axe], ef.loc[:, i], linestyle=next(linestyle), label=i)
1487+
ax.set_xlim(ef[x_axe].min(), ef[x_axe].max())
1488+
ax.set_xlabel(xlabel)
1489+
ax.set_ylabel("Weights of assets")
1490+
ax.legend(loc="upper left", frameon=False)
1491+
fig.tight_layout()
1492+
return ax

0 commit comments

Comments
 (0)