|
6 | 6 | import pandas as pd |
7 | 7 | import matplotlib.pyplot as plt |
8 | 8 | from joblib import Parallel, delayed |
| 9 | +from matplotlib.axes import Axes |
9 | 10 |
|
10 | 11 | from scipy.optimize import minimize |
11 | 12 |
|
@@ -1284,7 +1285,7 @@ def get_monte_carlo(self, n: int = 100) -> pd.DataFrame: |
1284 | 1285 | random_portfolios = pd.concat([random_portfolios, pd.DataFrame(row, index=[0])], ignore_index=True) |
1285 | 1286 | return random_portfolios |
1286 | 1287 |
|
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: |
1288 | 1289 | """ |
1289 | 1290 | Plot Efficient Frontier of every pair of assets. |
1290 | 1291 |
|
@@ -1419,3 +1420,73 @@ def plot_cml(self, rf_return: float = 0, figsize: Optional[tuple] = None): |
1419 | 1420 | # plot the assets |
1420 | 1421 | self.plot_assets(kind="cagr") |
1421 | 1422 | 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