#!/usr/bin/env python
# coding: utf-8
# # Riskfolio-Lib Tutorial:
#
__[Financionerioncios](https://financioneroncios.wordpress.com)__
#
__[Orenji](https://www.linkedin.com/company/orenj-i/)__
#
__[Riskfolio-Lib](https://riskfolio-lib.readthedocs.io/en/latest/)__
#
__[Dany Cajas](https://www.linkedin.com/in/dany-cajas/)__
#
#
# ## Tutorial 39: Mean Semi Kurtosis Optimization
#
# ## 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', '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
# In[2]:
# Calculating returns
Y = data[assets].iloc[-300:,:].pct_change().dropna()
display(Y.head())
# ## 2. Estimating Mean Semi Kurtosis Portfolios
#
# The Semi Kurtosis portfolio model proposed by __[Cajas (2022)](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3988927)__ shows how to optimize the fourth lower moment of portfolio returns using an lower semi cokurtosis matrix as an heuristic in a similar way than lower semi covariance matrix.
#
# It is recommended to use MOSEK to optimize Kurtosis for a large number of assets due the model use semidefinite programming. Also, for a large number of assets is recommended to use the relaxed version of this model based only on second order cone programming. To use the relaxed version we have to use a number of assets higher than the property __n_max_kurt__, so for example if number of assets is 30 and we set __port.n_max_kurt = 25__, riskfolio-lib is going to use the relaxed version.
#
# Instructions to install MOSEK are in this __[link](https://docs.mosek.com/9.2/install/installation.html)__, is better to install using Anaconda. Also you will need a license, I recommend you that ask for an academic license __[here](https://www.mosek.com/products/academic-licenses/)__.
#
# ### 2.1 Calculating the portfolio that optimize return/semi kurtosis ratio.
# In[3]:
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.
method_kurt='hist' # Method to estimate cokurtosis square matrix based on historical data.
port.assets_stats(method_mu=method_mu,
method_cov=method_cov,
method_kurt=method_kurt,
)
# Estimate optimal portfolio:
port.solvers = ['MOSEK'] # It is recommended to use mosek when optimizing GMD
port.sol_params = {'MOSEK': {'mosek_params': {'MSK_IPAR_NUM_THREADS': 2}}}
model ='Classic' # Could be Classic (historical), BL (Black Litterman) or FM (Factor Model)
rm = 'SKT' # 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)
# ### 2.2 Plotting portfolio composition
# In[4]:
# Plotting the composition of the portfolio
ax = rp.plot_pie(w=w,
title='Sharpe Mean - Kurtosis',
others=0.05,
nrow=25,
cmap = "tab20",
height=6,
width=10,
ax=None)
# ### 2.3 Plotting risk measures
# In[5]:
ax = rp.plot_hist(returns=Y,
w=w,
alpha=0.05,
bins=50,
height=6,
width=10,
ax=None)
# ### 2.4 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 # 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=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. Estimating Risk Parity Portfolios for Square Root Semi Kurtosis
#
# ### 3.1 Calculating the risk parity portfolio for Square Root Semi Kurtosis.
# In[9]:
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)
# ### 3.2 Plotting portfolio composition
# In[10]:
ax = rp.plot_pie(w=w_rp,
title='Risk Parity Square Root Semi Kurtosis',
others=0.05,
nrow=25,
cmap="tab20",
height=6,
width=10,
ax=None)
# ### 3.3 Plotting Risk Composition
# In[11]:
ax = rp.plot_risk_con(w_rp, cov=port.cov, returns=port.returns, rm=rm, rf=0, alpha=0.05,
color="tab:blue", height=6, width=10, ax=None)
# Due to semi kurtosis use an heuristic based on lower semi cokurtosis matrix to approach portfolio lower semi kurtosis, the solution is only an approximation.
# In[12]:
# Plotting the efficient frontier
ws = pd.concat([w, w_rp],axis=1)
ws.columns = ["Max Return/ Semi Kurtosis", "Risk Parity Semi Kurtosis"]
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=0.05, cmap='viridis', w=ws,
marker='*', s=16, c='r', height=6, width=10, ax=None)