@@ -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