#!/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/)__
#
#
# ## 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)