Financionerioncios
Orenji
Riskfolio-Lib
Dany Cajas
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()
# Downloading data
data = yf.download(assets, start = start, end = end)
data = data.loc[:,('Adj Close', slice(None))]
data.columns = assets
[*********************100%***********************] 25 of 25 completed
# Calculating returns
Y = data[assets].pct_change().dropna()
display(Y.head())
APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | HPQ | JCI | ... | NI | PCAR | PSA | SEE | T | TGT | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||||||
2016-01-05 | -2.0256% | 0.4057% | 0.4036% | 1.9693% | 0.0180% | 0.9305% | 0.3678% | 0.5783% | 0.9483% | -1.1953% | ... | 1.5881% | 0.0212% | 2.8236% | 0.9758% | 0.6987% | 1.7539% | -0.1730% | 0.2410% | 1.3734% | -1.0857% |
2016-01-06 | -11.4863% | -1.5879% | 0.2411% | -1.7557% | -0.7727% | -1.2473% | -0.1736% | -1.1238% | -3.5867% | -0.9551% | ... | 0.5547% | 0.0212% | 0.1592% | -1.5647% | 0.3108% | -1.0155% | -0.7653% | -3.0048% | -0.9034% | -2.9144% |
2016-01-07 | -5.1389% | -4.1922% | -1.6573% | -2.7699% | -1.1047% | -1.9769% | -1.2206% | -0.8855% | -4.6059% | -2.5394% | ... | -2.2066% | -3.0309% | -1.0410% | -3.1557% | -1.6148% | -0.2700% | -2.2844% | -2.0570% | -0.5492% | -3.0020% |
2016-01-08 | 0.2736% | -2.2705% | -1.6037% | -2.5425% | 0.1099% | -0.2241% | 0.5706% | -1.6403% | -1.7642% | -0.1649% | ... | -0.1538% | -1.1366% | -0.7308% | -0.1448% | 0.0895% | -3.3839% | -0.1116% | -1.1387% | -0.9719% | -1.1254% |
2016-01-11 | -4.3383% | 0.1693% | -1.6851% | -1.0215% | 0.0914% | -1.1791% | 0.5674% | 0.5287% | 0.6617% | 0.0330% | ... | 1.6436% | 0.0000% | 0.9870% | -0.1451% | 1.2224% | 1.4570% | 0.5366% | -0.4607% | 0.5800% | -1.9919% |
5 rows × 25 columns
import riskfolio as rp
# Plotting Assets Clusters
ax = rp.plot_clusters(returns=Y,
codependence='pearson',
linkage='ward',
k=None,
max_k=10,
leaf_order=True,
dendrogram=True,
#linecolor='tab:purple',
ax=None)
The graph above suggest that optimal number of clusters are four.
This is the original model proposed by López de Prado (2019). Riskfolio-Lib expand this model to 13 risk measures and for objective functions: "Minimize Risk", "Maximize Utility Function", "Maximize Return/Risk Ratio" and "Equal Risk Contribution".
# Building the portfolio object
port = rp.HCPortfolio(returns=Y)
# Estimate optimal portfolio:
model='NCO' # Could be HRP, HERC or NCO
codependence = 'pearson' # Correlation matrix used to group assets in clusters
covariance = 'hist' # Covariance estimation technique
obj = "MinRisk" # Posible values are "MinRisk", "Utility", "Sharpe" and "ERC"
rm = 'MV' # Risk measure used, this time will be variance
rf = 0 # Risk free rate
l = 2 # Risk aversion factor, only usefull with "Utility" objective
linkage = 'ward' # Linkage method used to build clusters
max_k = 10 # Max number of clusters used in two difference gap statistic
leaf_order = True # Consider optimal order of leafs in dendrogram
w = port.optimization(model=model,
codependence=codependence,
covariance=covariance,
obj=obj,
rm=rm,
rf=rf,
l=l,
linkage=linkage,
max_k=max_k,
leaf_order=leaf_order)
display(w.T)
APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | HPQ | JCI | ... | NI | PCAR | PSA | SEE | T | TGT | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
weights | 0.0000% | 1.1729% | 5.1827% | 2.8237% | 5.0008% | 10.9737% | 3.7087% | 0.7664% | 0.3468% | 2.4530% | ... | 7.0051% | 0.1997% | 12.0253% | 2.2920% | 4.8848% | 3.0260% | 1.1106% | 0.0000% | 8.6248% | 0.0000% |
1 rows × 25 columns
# Plotting the composition of the portfolio
ax = rp.plot_pie(w=w,
title='NCO MinRisk',
others=0.05,
nrow=25,
cmap="tab20",
height=8,
width=10,
ax=None)
# Plotting the risk contribution per asset
mu = Y.mean()
cov = Y.cov() # Covariance matrix
returns = Y # Returns of the assets
ax = rp.plot_risk_con(w=w,
cov=cov,
returns=returns,
rm=rm,
rf=0,
alpha=0.05,
color="tab:blue",
height=6,
width=10,
t_factor=252,
ax=None)
asset_classes = {'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'],
'Industry': ['Consumer Discretionary','Consumer Discretionary',
'Consumer Discretionary', 'Consumer Staples',
'Consumer Staples','Energy','Financials',
'Financials','Financials','Financials',
'Health Care','Health Care','Industrials','Industrials',
'Industrials','Health care','Industrials',
'Information Technology','Information Technology',
'Materials','Telecommunications Services','Utilities',
'Utilities','Telecommunications Services','Financials']}
asset_classes = pd.DataFrame(asset_classes)
asset_classes = asset_classes.sort_values(by=['Assets'])
constraints = {'Disabled': [False, False, False, False, False],
'Type': ['Assets', 'Assets', 'All Assets',
'Each asset in a class', 'Each asset in a class'],
'Set': ['', '', '','Industry', 'Industry'],
'Position': ['HPQ', 'PSA', '', 'Financials', 'Information Technology'],
'Sign': ['>=', '<=', '<=', '<=', '<='],
'Weight': [0.01, 0.05, 0.06, 0.04, 0.02]}
constraints = pd.DataFrame(constraints)
display(constraints)
Disabled | Type | Set | Position | Sign | Weight | |
---|---|---|---|---|---|---|
0 | False | Assets | HPQ | >= | 1.0000% | |
1 | False | Assets | PSA | <= | 5.0000% | |
2 | False | All Assets | <= | 6.0000% | ||
3 | False | Each asset in a class | Industry | Financials | <= | 4.0000% |
4 | False | Each asset in a class | Industry | Information Technology | <= | 2.0000% |
# Estimate optimal portfolio with constraints:
w_max, w_min = rp.hrp_constraints(constraints, asset_classes)
port.w_max = w_max
port.w_min = w_min
w_1 = port.optimization(model=model,
codependence=codependence,
covariance=covariance,
obj=obj,
rm=rm,
rf=rf,
l=l,
linkage=linkage,
max_k=max_k,
leaf_order=leaf_order)
display(w_1.T)
APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | HPQ | JCI | ... | NI | PCAR | PSA | SEE | T | TGT | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
weights | 0.0000% | 3.3662% | 6.0000% | 6.0000% | 6.0000% | 6.0000% | 6.0000% | 2.1995% | 1.0000% | 6.0000% | ... | 6.0000% | 0.5731% | 4.0000% | 6.0000% | 6.0000% | 6.0000% | 3.1874% | 0.0000% | 6.0000% | 0.0000% |
1 rows × 25 columns
# Plotting the composition of the portfolio with constraints
ax = rp.plot_pie(w=w_1,
title='NCO MinRisk with Constraints',
others=0.05,
nrow=25,
cmap="tab20",
height=8,
width=10,
ax=None)
# Plotting the risk contribution per asset
mu = Y.mean()
cov = Y.cov() # Covariance matrix
returns = Y # Returns of the assets
ax = rp.plot_risk_con(w=w_1,
cov=cov,
returns=returns,
rm=rm,
rf=0,
alpha=0.05,
color="tab:blue",
height=6,
width=10,
t_factor=252,
ax=None)