#!/usr/bin/env python # coding: utf-8 # # Riskfolio-Lib Tutorial: #
__[Financionerioncios](https://financioneroncios.wordpress.com)__ #
__[Orenji](https://www.orenj-i.net)__ #
__[Riskfolio-Lib](https://riskfolio-lib.readthedocs.io/en/latest/)__ #
__[Dany Cajas](https://www.linkedin.com/in/dany-cajas/)__ # Buy Me a Coffee at ko-fi.com # # ## Tutorial 2: Portfolio Optimization with Risk Factors using Stepwise Regression # # ## 1. Downloading the data: # In[1]: import numpy as np import pandas as pd import yfinance as yf import warnings warnings.filterwarnings("ignore") pd.options.display.float_format = '{:.4%}'.format # Date range start = '2016-01-01' end = '2019-12-30' # Tickers of assets assets = ['JCI', 'TGT', 'CMCSA', 'CPB', 'MO', 'APA', 'MMC', 'JPM', 'ZION', 'PSA', 'BAX', 'BMY', 'LUV', 'PCAR', 'TXT', 'TMO', 'DE', 'MSFT', 'HPQ', 'SEE', 'VZ', 'CNP', 'NI', 'T', 'BA'] assets.sort() # Tickers of factors factors = ['MTUM', 'QUAL', 'VLUE', 'SIZE', 'USMV'] factors.sort() tickers = assets + factors tickers.sort() # Downloading data data = yf.download(tickers, start = start, end = end) data = data.loc[:,('Adj Close', slice(None))] data.columns = tickers # In[2]: # Calculating returns X = data[factors].pct_change().dropna() Y = data[assets].pct_change().dropna() display(X.head()) # ## 2. Estimating Mean Variance Portfolios # # ### 2.1 Estimating the loadings matrix. # # This part is just to visualize how Riskfolio-Lib calculates a loadings matrix. # In[3]: import riskfolio as rp step = 'Forward' # Could be Forward or Backward stepwise regression loadings = rp.loadings_matrix(X=X, Y=Y, stepwise=step) loadings.style.format("{:.4f}").background_gradient(cmap='RdYlGn') # ### 2.2 Calculating the portfolio that maximizes Sharpe ratio. # In[4]: # Building the portfolio object port = rp.Portfolio(returns=Y) # Calculating optimal portfolio # Select method and estimate input parameters: method_mu='hist' # Method to estimate expected returns based on historical data. method_cov='hist' # Method to estimate covariance matrix based on historical data. port.assets_stats(method_mu=method_mu, method_cov=method_cov, d=0.94) port.factors = X port.factors_stats(method_mu=method_mu, method_cov=method_cov, d=0.94) # Estimate optimal portfolio: port.alpha = 0.05 model='FM' # Factor Model rm = 'MV' # Risk measure used, this time will be variance obj = 'Sharpe' # Objective function, could be MinRisk, MaxRet, Utility or Sharpe hist = False # Use historical scenarios for risk measures that depend on scenarios rf = 0 # Risk free rate l = 0 # Risk aversion factor, only useful when obj is 'Utility' w = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist) display(w.T) # ### 2.3 Plotting portfolio composition # In[5]: # Plotting the composition of the portfolio ax = rp.plot_pie(w=w, title='Sharpe FM Mean Variance', others=0.05, nrow=25, cmap = "tab20", height=6, width=10, ax=None) # ### 2.3 Calculate efficient frontier # In[6]: points = 50 # Number of points of the frontier frontier = port.efficient_frontier(model=model, rm=rm, points=points, rf=rf, hist=hist) display(frontier.T.head()) # In[7]: # Plotting the efficient frontier label = 'Max Risk Adjusted Return Portfolio' # Title of point mu = port.mu_fm # Expected returns cov = port.cov_fm # Covariance matrix returns = port.returns_fm # Returns of the assets ax = rp.plot_frontier(w_frontier=frontier, mu=mu, cov=cov, returns=returns, rm=rm, rf=rf, alpha=0.05, cmap='viridis', w=w, label=label, marker='*', s=16, c='r', height=6, width=10, ax=None) # In[8]: # Plotting efficient frontier composition ax = rp.plot_frontier_area(w_frontier=frontier, cmap="tab20", height=6, width=10, ax=None) # ## 3. Optimization with Constraints on Risk Factors # ### 3.1 Statistics of Risk Factors # In[9]: # Displaying factors statistics display(loadings.min()) display(loadings.max()) display(X.corr()) # ### 3.2 Creating Constraints on Risk Factors # In[10]: # Creating risk factors constraints constraints = {'Disabled': [False, False, False, False, False], 'Factor': ['MTUM', 'QUAL', 'SIZE', 'USMV', 'VLUE'], 'Sign': ['<=', '<=', '<=', '>=', '<='], 'Value': [-0.3, 0.8, 0.4, 0.8 , 0.9], 'Relative Factor': ['', 'USMV', '', '', '']} constraints = pd.DataFrame(constraints) display(constraints) # In[11]: C, D = rp.factors_constraints(constraints, loadings) # In[12]: port.ainequality = C port.binequality = D w = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist) display(w.T) # To check if the constraints are verified, I will make a regression among the portfolio returns and risk factors: # In[13]: import statsmodels.api as sm X1 = sm.add_constant(X) y = np.matrix(returns) * np.matrix(w) results = sm.OLS(y, X1).fit() coefs = results.params print(coefs) # ### 3.3 Plotting portfolio composition # In[14]: ax = rp.plot_pie(w=w, title='Sharpe FM Mean Variance', others=0.05, nrow=25, cmap = "tab20", height=6, width=10, ax=None) # ### 3.4 Calculate efficient frontier # In[15]: points = 50 # Number of points of the frontier frontier = port.efficient_frontier(model=model, rm=rm, points=points, rf=rf, hist=hist) display(frontier.T.head()) # In[16]: # Plotting efficient frontier composition ax = rp.plot_frontier(w_frontier=frontier, mu=mu, cov=cov, returns=returns, rm=rm, rf=rf, alpha=0.05, cmap='viridis', w=w, label=label, marker='*', s=16, c='r', height=6, width=10, ax=None) # In[17]: # Plotting efficient frontier composition ax = rp.plot_frontier_area(w_frontier=frontier, cmap="tab20", height=6, width=10, ax=None) # In[18]: display(returns) # ## 4. Estimating Portfolios Using Risk Factors with Other Risk Measures # # In this part I will calculate optimal portfolios for several risk measures. I will find the portfolios that maximize the risk adjusted return for all available risk measures. # # ### 4.1 Calculate Optimal Portfolios for Several Risk Measures. # # I will mantain the constraints on risk factors. # In[19]: # Risk Measures available: # # 'MV': Standard Deviation. # 'MAD': Mean Absolute Deviation. # 'MSV': Semi Standard Deviation. # 'FLPM': First Lower Partial Moment (Omega Ratio). # 'SLPM': Second Lower Partial Moment (Sortino Ratio). # 'CVaR': Conditional Value at Risk. # 'EVaR': Entropic Value at Risk. # 'WR': Worst Realization (Minimax) # 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). # 'ADD': Average Drawdown of uncompounded cumulative returns. # 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. # 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. # 'UCI': Ulcer Index of uncompounded cumulative returns. # port.reset_linear_constraints() # To reset linear constraints (factor constraints) rms = ['MV', 'MAD', 'MSV', 'FLPM', 'SLPM', 'CVaR', 'EVaR', 'WR', 'MDD', 'ADD', 'CDaR', 'UCI', 'EDaR'] w_s = pd.DataFrame([]) # When we use hist = True the risk measures all calculated # using historical returns, while when hist = False the # risk measures are calculated using the expected returns # based on risk factor model: R = a + B * F hist = False for i in rms: w = port.optimization(model=model, rm=i, obj=obj, rf=rf, l=l, hist=hist) w_s = pd.concat([w_s, w], axis=1) w_s.columns = rms # In[20]: w_s.style.format("{:.2%}").background_gradient(cmap='YlGn') # In[21]: import matplotlib.pyplot as plt # Plotting a comparison of assets weights for each portfolio fig = plt.gcf() fig.set_figwidth(14) fig.set_figheight(6) ax = fig.subplots(nrows=1, ncols=1) w_s.plot.bar(ax=ax) # In[22]: w_s = pd.DataFrame([]) # When we use hist = True the risk measures all calculated # using historical returns, while when hist = False the # risk measures are calculated using the expected returns # based on risk factor model: R = a + B * F hist = True for i in rms: w = port.optimization(model=model, rm=i, obj=obj, rf=rf, l=l, hist=hist) w_s = pd.concat([w_s, w], axis=1) w_s.columns = rms # In[23]: w_s.style.format("{:.2%}").background_gradient(cmap='YlGn') # In[24]: import matplotlib.pyplot as plt # Plotting a comparison of assets weights for each portfolio fig = plt.gcf() fig.set_figwidth(14) fig.set_figheight(6) ax = fig.subplots(nrows=1, ncols=1) w_s.plot.bar(ax=ax)