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', 'AMZN', 'CMCSA', 'CPB', 'MO', 'APA', 'MMC', 'JPM',
'ZION', 'AAPL', '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].iloc[-300:,:].pct_change().dropna()
display(Y.head())
AAPL | AMZN | APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | ... | MO | MSFT | NI | PCAR | SEE | T | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||||||
2018-10-19 | 1.5230% | -0.3778% | 0.0475% | -0.8599% | -1.4332% | -3.0011% | 0.1113% | 1.2968% | 3.4361% | -0.8763% | ... | 1.6740% | 0.1475% | 0.6339% | -0.1823% | -0.7728% | 1.1385% | -1.1145% | -1.2872% | 0.4575% | -0.8025% |
2018-10-22 | 0.6110% | 1.4325% | -1.9240% | -0.0786% | -0.6334% | -6.2983% | -0.6392% | -1.1024% | 0.0528% | -0.3221% | ... | -1.0331% | 0.8927% | -0.8661% | 0.4483% | -2.8972% | -0.6084% | -0.6075% | -0.8634% | 0.1457% | -3.4490% |
2018-10-23 | 0.9427% | -1.1513% | -3.6570% | -1.6658% | -0.4202% | -0.4520% | -0.2797% | -0.5034% | 0.1844% | -3.9948% | ... | 0.8808% | -1.3956% | 0.4766% | -5.1239% | -0.0321% | 1.0713% | -1.0807% | -1.8308% | 4.0560% | 4.0353% |
2018-10-24 | -3.4301% | -5.9083% | -4.5501% | 1.3141% | -1.8041% | -3.5933% | -4.2917% | 0.8674% | 0.9994% | -4.1109% | ... | 0.7437% | -5.3469% | 3.5178% | -4.2683% | -1.3479% | -8.0557% | -1.2403% | -4.2187% | 0.3671% | -3.3065% |
2018-10-25 | 2.1898% | 7.0887% | 0.4741% | 2.5716% | 0.5186% | 0.7782% | 5.0410% | -0.5733% | -1.1718% | 2.1585% | ... | 1.3641% | 5.8444% | -1.0309% | 0.4914% | 0.9109% | -1.2516% | 4.3662% | 1.3800% | -1.7241% | 3.3538% |
5 rows × 25 columns
The Relativistic Value at Risk (RLVaR) portfolio model proposed by Cajas (2023) shows how to optimize the RLVaR of portfolio returns in a similar way than portfolio Entropic Value at Risk (EVaR). The RLVaR is a coherent risk measure that is a generalization of EVaR based on Kaniadakis entropy.
It is recommended to use MOSEK to optimize RLVaR due to RLVaR model use the power cone.
Instructions to install MOSEK are in this link, is better to install using Anaconda. Also you will need a license, I recommend you that ask for an academic license here.
import riskfolio as rp
import mosek
# Building the portfolio object
port = rp.Portfolio(returns=Y)
# Calculating optimum 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,
)
# Estimate optimal portfolio:
port.solvers = ['MOSEK'] # It is recommended to use mosek when optimizing GMD
port.sol_params = {'MOSEK': {'mosek_params': {mosek.iparam.num_threads: 2}}}
kappa = 0.3
alpha = 0.05
port.kappa = kappa
port.alpha = alpha
model ='Classic' # Could be Classic (historical), BL (Black Litterman) or FM (Factor Model)
rm = 'RLVaR' # Risk measure used, this time will be Tail Gini Range
obj = 'Sharpe' # Objective function, could be MinRisk, MaxRet, Utility or Sharpe
hist = True # 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)
AAPL | AMZN | APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | ... | MO | MSFT | NI | PCAR | SEE | T | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
weights | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 7.9135% | 0.0000% | ... | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 6.5204% | 0.0000% | 15.5150% | 0.0000% | 0.0000% | 0.0000% |
1 rows × 25 columns
# Plotting the composition of the portfolio
ax = rp.plot_pie(w=w,
title='Sharpe Mean - RLVaR',
others=0.05,
nrow=25,
cmap = "tab20",
height=6,
width=10,
ax=None)
ax = rp.plot_hist(returns=Y,
w=w,
alpha=alpha,
kappa=kappa,
solver='MOSEK',
bins=50,
height=6,
width=10,
ax=None)
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())
AAPL | AMZN | APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | ... | MO | MSFT | NI | PCAR | SEE | T | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 10.6568% | 4.7594% | 0.0000% | 0.0000% | 0.0000% | ... | 23.5629% | 0.0000% | 2.0761% | 0.0000% | 17.4821% | 0.0000% | 0.0000% | 0.0000% | 29.5792% | 5.5921% |
1 | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 12.5234% | 18.4972% | 0.0000% | 0.0000% | 0.0000% | ... | 16.0178% | 0.0000% | 0.0000% | 0.0000% | 12.8789% | 0.0000% | 0.0000% | 0.0000% | 27.7307% | 0.2894% |
2 | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 11.5903% | 17.3380% | 0.0000% | 0.0000% | 0.0000% | ... | 11.2680% | 0.0000% | 0.0000% | 0.0000% | 10.6772% | 0.0000% | 0.0000% | 0.0000% | 25.7019% | 0.0000% |
3 | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 10.6053% | 14.3683% | 0.0000% | 0.0000% | 0.0000% | ... | 6.8918% | 0.0000% | 0.0000% | 0.0000% | 12.2660% | 0.0000% | 0.0000% | 0.0000% | 25.7177% | 0.0000% |
4 | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 0.0000% | 9.7406% | 11.2255% | 0.0000% | 0.0000% | 0.0000% | ... | 2.9212% | 0.0000% | 0.0000% | 0.0000% | 14.0441% | 0.0000% | 0.0000% | 0.0000% | 26.1732% | 0.0000% |
5 rows × 25 columns
# Plotting the efficient frontier
label = 'Max Risk Adjusted Return Portfolio' # Title of point
mu = port.mu # Expected returns
cov = port.cov # Covariance matrix
returns = port.returns # Returns of the assets
ax = rp.plot_frontier(w_frontier=frontier,
mu=mu,
cov=cov,
returns=returns,
rm=rm,
rf=rf,
alpha=alpha,
kappa=kappa,
solver='MOSEK',
cmap='viridis',
w=w,
label=label,
marker='*',
s=16,
c='r',
height=6,
width=10,
ax=None)
# Plotting efficient frontier composition
ax = rp.plot_frontier_area(w_frontier=frontier, cmap="tab20", height=6, width=10, ax=None)
b = None # Risk contribution constraints vector
w_rp = port.rp_optimization(model=model, rm=rm, rf=rf, b=b, hist=hist)
display(w_rp.T)
AAPL | AMZN | APA | BA | BAX | BMY | CMCSA | CNP | CPB | DE | ... | MO | MSFT | NI | PCAR | SEE | T | TMO | TXT | VZ | ZION | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
weights | 2.9693% | 2.9937% | 2.5752% | 3.2822% | 4.3364% | 5.1403% | 4.2069% | 4.7291% | 3.5354% | 2.3535% | ... | 5.9295% | 3.1232% | 4.7398% | 3.8010% | 4.5658% | 3.9265% | 3.7370% | 3.5493% | 7.1582% | 3.6839% |
1 rows × 25 columns
ax = rp.plot_pie(w=w_rp,
title='Risk Parity RLVaR',
others=0.05,
nrow=25,
cmap="tab20",
height=6,
width=10,
ax=None)
ax = rp.plot_risk_con(w_rp,
cov=port.cov,
returns=port.returns,
rm=rm,
rf=0,
alpha=alpha,
kappa=kappa,
solver='MOSEK',
color="tab:blue", height=6, width=10, ax=None)
# Plotting the efficient frontier
ws = pd.concat([w, w_rp],axis=1)
ws.columns = ["Max Return/RLVaR", "Risk Parity RLVaR"]
mu = port.mu # Expected returns
cov = port.cov # Covariance matrix
returns = port.returns # Returns of the assets
ax = rp.plot_frontier(w_frontier=frontier,
mu=mu,
cov=cov,
returns=returns,
rm=rm,
rf=rf,
alpha=alpha,
kappa=kappa,
solver='MOSEK',
cmap='viridis',
w=ws,
marker='*',
s=16,
height=6,
width=10,
ax=None)