Skip to content

Commit e76b08c

Browse files
committed
docs: update docs for EfficientFrontier
1 parent 5455d36 commit e76b08c

File tree

1 file changed

+82
-95
lines changed

1 file changed

+82
-95
lines changed

okama/frontier/multi_period.py

Lines changed: 82 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ class EfficientFrontier(asset_list.AssetList):
2525
"""
2626
Efficient Frontier with multi-period optimization.
2727
28-
In multi-period optimization portfolios are rebalanced with a given frequency.
28+
In multi-period optimization, portfolios are rebalanced according to a rebalancing strategy.
2929
30-
Rebalancing is the process by which an investor restores their portfolio to its target allocation
31-
by selling and buying assets. After rebalancing all the assets have original weights.
30+
Rebalancing is the process by which an investor restores a portfolio to its target allocation
31+
by selling and buying assets. After rebalancing, the portfolio assets have their target weights.
3232
3333
Parameters
3434
----------
35-
assets : list, default None
36-
List of assets. Could include tickers or asset like objects (Asset, Portfolio).
37-
If None a single asset list with a default ticker is used.
35+
assets : list[str]
36+
List of assets. Could include tickers or asset-like objects (`Asset`, `Portfolio`).
37+
Must contain at least two assets.
3838
3939
first_date : str, default None
4040
First date of monthly return time series.
@@ -47,23 +47,19 @@ class EfficientFrontier(asset_list.AssetList):
4747
ccy : str, default 'USD'
4848
Base currency for the list of assets. All risk metrics and returns are adjusted to the base currency.
4949
50-
bounds: tuple of ((float, float),...)
51-
Bounds for the assets weights. Each asset can have weights limitation from 0 to 1.0.
52-
If an asset has limitation for 10 to 20%, bounds are defined as (0.1, 0.2).
53-
bounds = ((0, .5), (0, 1)) shows that in Portfolio with two assets first one has weight limitations
54-
from 0 to 50%. The second asset has no limitations.
50+
bounds : tuple[tuple[float, float], ...] or None, default None
51+
Bounds for asset weights. Each asset weight is constrained within the corresponding (min, max) pair.
52+
For example, `((0.0, 0.5), (0.0, 1.0))` restricts the first asset to [0%, 50%] and leaves the second
53+
asset unconstrained.
5554
56-
inflation : bool, default True
55+
inflation : bool, default False
5756
Defines whether to take inflation data into account in the calculations.
58-
Including inflation could limit available data (last_date, first_date)
59-
as the inflation data is usually published with a one-month delay.
57+
Including inflation could limit available data (`first_date`, `last_date`) as the inflation data is
58+
usually published with a one-month delay.
6059
61-
rebalancing_strategy : Rebalance, default Rebalance(period='year', abs_deviation=None, rel_deviation=None)
62-
Rebalancing strategy for an investment portfolio. The rebalancing strategy si defined by:
63-
-period (rebalancing frequency): predetermined time intervals when the investor rebalances the portfolio.
64-
If 'none' assets weights are not rebalanced.
65-
-abs_deviation: the absolute deviation allowed for the assets weights in the portfolio.
66-
-rel_deviation: the relative deviation allowed for the assets weights in the portfolio.
60+
rebalancing_strategy : Rebalance, default Rebalance(period='year')
61+
Rebalancing strategy used to generate portfolio return series during optimization.
62+
Only `rebalancing_strategy.period` is used; `abs_deviation` and `rel_deviation` are ignored.
6763
6864
n_points : int, default 20
6965
Number of points in the Efficient Frontier.
@@ -80,7 +76,7 @@ class EfficientFrontier(asset_list.AssetList):
8076
8177
Notes
8278
-----
83-
For monthly rebalanced portfolios okama.EfficientFrontier class could be used.
79+
For classic single-period (monthly rebalanced, constant-weight) optimization, use `EfficientFrontierSingle`.
8480
"""
8581

8682
_FTOL = (1e-06, 1e-05, 1e-3, 1e-02) # possible tolerance values for the optimizer
@@ -246,24 +242,23 @@ def _get_portfolio_ror_ts(self, weights: np.ndarray) -> pd.Series:
246242
@property
247243
def rebalancing_strategy(self) -> Rebalance:
248244
"""
249-
Return or set rebalancing period for multi-period Efficient Frontier.
245+
Rebalancing strategy used to compute portfolio return series during optimization.
250246
251-
Rebalancing periods could be:
252-
'year' - one Year (default)
253-
'none' - not rebalanced portfolios
247+
Only `rebalancing_strategy.period` is used in the optimization; `abs_deviation` and `rel_deviation`
248+
are ignored.
254249
255250
Returns
256251
-------
257-
str
258-
Rebalancing period value.
252+
Rebalance
253+
Rebalancing strategy.
259254
260255
Examples
261256
--------
262257
>>> frontier = ok.EfficientFrontier(['SPY.US', 'BND.US'])
263-
>>> frontier.rebalancing_strategy # default rebalancing period is one year
258+
>>> frontier.rebalancing_strategy.period
264259
'year'
265260
266-
>>> frontier.rebalancing_strategy = 'none' # change for not rebalanced portfolios
261+
>>> frontier.rebalancing_strategy = ok.Rebalance(period='none')
267262
"""
268263
return self._rebalancing_strategy
269264

@@ -322,25 +317,22 @@ def get_most_diversified_portfolio(
322317
target_return: Optional[float] = None,
323318
) -> Dict[str, float]:
324319
"""
325-
Calculate assets weights, annualized values for risk and return, Diversification ratio
326-
for the most diversified portfolio given the target CAGR within given bounds.
320+
Calculate assets weights and portfolio metrics for the most diversified portfolio within bounds.
327321
328-
The most diversified portfolio has the largest Diversification Ratio.
322+
The most diversified portfolio is defined as the portfolio with the maximum Diversification Ratio.
329323
330-
The Diversification Ratio is the ratio of the weighted average of assets risks divided by the portfolio risk.
331-
In this case risk is the annualized standard deviation for the rate of return.
324+
Parameters
325+
----------
326+
target_return : float, default None
327+
Target Compound Annual Growth Rate (CAGR) for the portfolio. If provided, the optimizer searches for a
328+
portfolio with the target CAGR and the maximum Diversification Ratio. If None, the global most diversified
329+
portfolio is returned.
332330
333331
Returns
334332
-------
335-
dict
336-
Weights of assets and annualized values for risk, CAGR and Diversification ratio of the most diversified portfolio.
337-
338-
Parameters
339-
----------
340-
target_return : float, optional
341-
Target Compound Annual Growth Rate (CAGR) for the portfolio. The optimization process looks for a portfolio
342-
with the target_return and largest Diversification ratio. If not specified global most diversified portfolio
343-
is obtained.
333+
dict[str, float]
334+
Mapping with asset weights (keys are tickers or asset names depending on `ticker_names`) and portfolio
335+
metrics: 'CAGR', 'Risk', and 'Diversification ratio'.
344336
345337
Examples
346338
--------
@@ -430,12 +422,15 @@ def get_tangency_portfolio(self, rf_return: float = 0, rate_of_return: str = "ca
430422
431423
Parameters
432424
----------
433-
rate_of_return : {cagr, mean_return}, default cagr
434-
Use CAGR (Compound annual growth rate) or arithmetic mean of return to calculate Sharpe Ratio.
435-
436425
rf_return : float, default 0
437426
Risk-free rate of return.
438427
428+
rate_of_return : {'cagr', 'mean_return'}, default 'cagr'
429+
Return definition used to calculate Sharpe ratio.
430+
431+
- 'cagr': Compound Annual Growth Rate.
432+
- 'mean_return': Arithmetic mean return (annualized).
433+
439434
Returns
440435
-------
441436
dict
@@ -909,9 +904,8 @@ def _min_ratio_asset(self) -> dict:
909904
@property
910905
def _max_ratio_asset_right_to_max_cagr(self) -> Optional[dict]:
911906
"""
912-
The asset with the maximum ratio between the CAGR
913-
(Compound Annual Growth Rate) and the risk for assets that are “to the right”
914-
of the portfolio with the maximum CAGR on the efficiency frontier.
907+
The asset with the maximum ratio between CAGR and risk among assets that are to the right
908+
of the portfolio with the maximum CAGR on the Efficient Frontier.
915909
"""
916910
cagr = helpers.Frame.get_cagr(self.assets_ror)
917911
risk_monthly = self.assets_ror.std()
@@ -1052,11 +1046,11 @@ def ef_points(self):
10521046
... first_date='2004-12',
10531047
... last_date='2020-10',
10541048
... ccy=curr,
1055-
... rebalancing_strategy=ok.Rebalnce(period='year'),
1056-
... ticker_names=True, # use tickers in DataFrame column names (can be set to False to show full assets names instead tickers)
1049+
... rebalancing_strategy=ok.Rebalance(period='year'),
1050+
... ticker_names=True, # use tickers in DataFrame column names (set to False to show full asset names instead of tickers)
10571051
... n_points=20, # number of points in the Efficient Frontier
10581052
... full_frontier=False, # draw the frontier to the global CAGR max only
1059-
... verbose=False) # verbose mode is False to skip the progress while the EF points are calcualted
1053+
... verbose=False) # verbose mode is False to skip progress while EF points are calculated
10601054
>>> df_reb_year = y.ef_points
10611055
>>> df_reb_year.head(5)
10621056
Risk CAGR GLD.US SPY.US
@@ -1067,7 +1061,7 @@ def ef_points(self):
10671061
4 0.150615 0.089397 0.059713 0.940287
10681062
10691063
To compare the Efficient Frontiers of annually rebalanced portfolios with not rebalanced portfolios it's possible to draw 2 charts:
1070-
rebalancing_strategy=ok.Rebalance(period='year') and period='none'.
1064+
rebalancing_strategy=ok.Rebalance(period='year') and ok.Rebalance(period='none').
10711065
10721066
>>> import matplotlib.pyplot as plt
10731067
>>> y.rebalancing_strategy = ok.Rebalance(period='none')
@@ -1201,21 +1195,20 @@ def mdp_points(self) -> pd.DataFrame:
12011195

12021196
def get_monte_carlo(self, n: int = 100) -> pd.DataFrame:
12031197
"""
1204-
Generate N random rebalanced portfolios with Monte Carlo simulation.
1198+
Generate random rebalanced portfolios with Monte Carlo simulation.
12051199
1206-
Risk (annualized standard deviation) and Return (CAGR) are calculated for a set of random weights.
1207-
1208-
Returns
1209-
-------
1210-
DataFrame
1211-
Table with Return (CAGR) and Risk values for random portfolios
1212-
(portfolios with random asset weights).
1200+
Risk (annualized standard deviation) and return (CAGR) are calculated for random weights within `bounds`.
12131201
12141202
Parameters
12151203
----------
12161204
n : int, default 100
12171205
Number of random portfolios to generate with Monte Carlo simulation.
12181206
1207+
Returns
1208+
-------
1209+
DataFrame
1210+
Table with Return (CAGR) and Risk values for random portfolios (portfolios with random asset weights).
1211+
12191212
Examples
12201213
--------
12211214
>>> ls_m = ['SPY.US', 'GLD.US', 'PGJ.US', 'RGBITR.INDX', 'MCFTR.INDX']
@@ -1236,7 +1229,7 @@ def get_monte_carlo(self, n: int = 100) -> pd.DataFrame:
12361229
3 0.185500 0.168739
12371230
4 0.176748 0.192657
12381231
1239-
Monte Carlo simulation results can be plotted togeather with the optimized portfolios on the Efficient Frontier.
1232+
Monte Carlo simulation results can be plotted together with the optimized portfolios on the Efficient Frontier.
12401233
12411234
>>> import matplotlib.pyplot as plt
12421235
>>> df_reb_year = x.ef_points # optimize portfolios for EF. Calculations will take some time ...
@@ -1271,32 +1264,32 @@ def get_monte_carlo(self, n: int = 100) -> pd.DataFrame:
12711264

12721265
def plot_pair_ef(self, tickers="tickers", figsize: Optional[tuple] = None) -> Axes:
12731266
"""
1274-
Plot Efficient Frontier of every pair of assets.
1267+
Plot Efficient Frontier for every pair of assets.
12751268
12761269
Efficient Frontier is a set of portfolios which satisfy the condition that no other portfolio exists
12771270
with a higher expected return but with the same risk (standard deviation of return).
12781271
12791272
Arithmetic mean (expected return) is used for optimized portfolios.
12801273
1281-
Returns
1282-
-------
1283-
Axes : 'matplotlib.axes._subplots.AxesSubplot'
1284-
12851274
Parameters
12861275
----------
1287-
tickers : {'tickers', 'names'} or list of str, default 'tickers'
1276+
tickers : {'tickers', 'names'} or list[str], default 'tickers'
12881277
Annotation type for assets.
12891278
'tickers' - assets symbols are shown in form of 'SPY.US'
12901279
'names' - assets names are used like - 'SPDR S&P 500 ETF Trust'
12911280
To show custom annotations for each asset pass the list of names.
12921281
1293-
figsize: (float, float), optional
1294-
Figure size: width, height in inches.
1295-
If None default matplotlib size is taken: [6.4, 4.8]
1282+
figsize : tuple[float, float], default None
1283+
Figure size (width, height) in inches. If `None`, matplotlib default is used.
1284+
1285+
Returns
1286+
-------
1287+
Axes
1288+
Matplotlib axes with the plot.
12961289
12971290
Notes
12981291
-----
1299-
It should be at least 3 assets.
1292+
At least 3 assets are required.
13001293
13011294
Examples
13021295
--------
@@ -1308,13 +1301,13 @@ def plot_pair_ef(self, tickers="tickers", figsize: Optional[tuple] = None) -> Ax
13081301
>>> ef.plot_pair_ef()
13091302
>>> plt.show()
13101303
1311-
It can be useful to plot the full Efficent Frontier (EF) with optimized 4 assets portfolios
1304+
It can be useful to plot the full Efficient Frontier (EF) with optimized 4 asset portfolios
13121305
together with the EFs for each pair of assets.
13131306
13141307
>>> ef4 = ok.EfficientFrontier(assets=ls4, ccy=curr, n_points=100)
13151308
>>> df4 = ef4.ef_points
13161309
>>> fig = plt.figure()
1317-
>>> # Plot Efficient Frontier of every pair of assets. Optimized portfolios will have 2 assets.
1310+
>>> # Plot Efficient Frontier for every pair of assets. Optimized portfolios will have 2 assets.
13181311
>>> ef4.plot_pair_ef() # mean return is used for optimized portfolios.
13191312
>>> ax = plt.gca()
13201313
>>> # Plot the full Efficient Frontier for 4 asset portfolios.
@@ -1359,21 +1352,21 @@ def plot_cml(self, rf_return: float = 0, figsize: Optional[tuple] = None):
13591352
rf_return : float, default 0
13601353
Risk-free rate of return.
13611354
1362-
figsize : (float, float), optional
1363-
Figure size: width, height in inches.
1364-
If None default matplotlib size is taken: [6.4, 4.8]
1355+
figsize : tuple[float, float], default None
1356+
Figure size (width, height) in inches. If `None`, matplotlib default is used.
13651357
13661358
Returns
13671359
-------
1368-
Axes : 'matplotlib.axes._subplots.AxesSubplot'
1360+
Axes
1361+
Matplotlib axes with the plot.
13691362
13701363
Examples
13711364
--------
13721365
>>> import matplotlib.pyplot as plt
13731366
>>> three_assets = ['MCFTR.INDX', 'RGBITR.INDX', 'GC.COMM']
13741367
>>> ef = ok.EfficientFrontier(assets=three_assets, ccy='USD', full_frontier=True)
13751368
>>> ef.plot_cml(rf_return=0.05) # Risk-Free return is 5%
1376-
>>> plt.show
1369+
>>> plt.show()
13771370
"""
13781371
ef = self.ef_points
13791372
tg = self.get_tangency_portfolio(rf_return=rf_return, rate_of_return="cagr")
@@ -1409,29 +1402,24 @@ def plot_transition_map(self, x_axe: str = "risk", figsize: Optional[tuple] = No
14091402
"""
14101403
Plot Transition Map for optimized portfolios on the Efficient Frontier.
14111404
1412-
Transition Map shows the relation between asset weights and optimized portfolios properties:
1405+
Transition Map shows the relation between asset weights and optimized portfolio properties:
14131406
14141407
- CAGR (Compound annual growth rate)
14151408
- Risk (annualized standard deviation of return)
14161409
1417-
Wights are displayed on the y-axis.
1418-
CAGR or Risk - on the x-axis.
1419-
1420-
Constrained optimization with weights bounds is available.
1421-
1422-
Returns
1423-
-------
1424-
Axes : 'matplotlib.axes._subplots.AxesSubplot'
1425-
14261410
Parameters
14271411
----------
14281412
x_axe : {'risk', 'cagr'}, default 'risk'
14291413
Show the relation between weights and CAGR (if 'cagr') or between weights and Risk (if 'risk').
14301414
CAGR or Risk are displayed on the x-axis.
14311415
1432-
figsize : (float, float), optional
1433-
Figure size: width, height in inches.
1434-
If None default matplotlib size is taken: [6.4, 4.8]
1416+
figsize : tuple[float, float], default None
1417+
Figure size (width, height) in inches. If `None`, matplotlib default is used.
1418+
1419+
Returns
1420+
-------
1421+
Axes
1422+
Matplotlib axes with the plot.
14351423
14361424
Examples
14371425
--------
@@ -1440,9 +1428,8 @@ def plot_transition_map(self, x_axe: str = "risk", figsize: Optional[tuple] = No
14401428
>>> x.plot_transition_map()
14411429
>>> plt.show()
14421430
1443-
Transition Map with default setting show the relation between Risk (stanrd deviation) and assets weights for
1444-
optimized portfolios.
1445-
The same relation for CAGR can be shown setting x_axe='cagr'.
1431+
Transition Map with default settings shows the relation between risk (standard deviation) and asset weights
1432+
for optimized portfolios. The same relation for CAGR can be shown by setting `x_axe='cagr'`.
14461433
14471434
>>> x.plot_transition_map(x_axe='cagr')
14481435
>>> plt.show()

0 commit comments

Comments
 (0)