{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Riskfolio-Lib Tutorial: \n", "
__[Financionerioncios](https://financioneroncios.wordpress.com)__\n", "
__[Orenji](https://www.orenj-i.net)__\n", "
__[Riskfolio-Lib](https://riskfolio-lib.readthedocs.io/en/latest/)__\n", "
__[Dany Cajas](https://www.linkedin.com/in/dany-cajas/)__\n", "Buy Me a Coffee at ko-fi.com \n", "\n", "## Tutorial 37: OWA Portfolio Optimization\n", "\n", "## 1. Downloading the data:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[*********************100%%**********************] 25 of 25 completed\n" ] } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "import yfinance as yf\n", "import warnings\n", "\n", "warnings.filterwarnings(\"ignore\")\n", "pd.options.display.float_format = '{:.4%}'.format\n", "\n", "# Date range\n", "start = '2016-01-01'\n", "end = '2019-12-30'\n", "\n", "# Tickers of assets\n", "assets = ['JCI', 'TGT', 'CMCSA', 'CPB', 'MO', 'APA', 'MMC', 'JPM',\n", " 'ZION', 'PSA', 'BAX', 'BMY', 'LUV', 'PCAR', 'TXT', 'TMO',\n", " 'DE', 'MSFT', 'HPQ', 'SEE', 'VZ', 'CNP', 'NI', 'T', 'BA']\n", "assets.sort()\n", "\n", "# Downloading data\n", "data = yf.download(assets, start = start, end = end)\n", "data = data.loc[:,('Adj Close', slice(None))]\n", "data.columns = assets" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
APABABAXBMYCMCSACNPCPBDEHPQJCI...NIPCARPSASEETTGTTMOTXTVZZION
Date
2018-10-190.0475%-0.8599%-1.4333%-3.0011%0.1113%1.2968%3.4360%-0.8763%0.2945%-0.7834%...0.6339%-0.1823%0.9185%-0.7728%1.1385%-1.6075%-1.1145%-1.2872%0.4575%-0.8025%
2018-10-22-1.9240%-0.0786%-0.6335%-6.2983%-0.6393%-1.1024%0.0527%-0.3221%1.1325%-0.8199%...-0.8662%0.4483%-1.6953%-2.8972%-0.6085%1.4752%-0.6075%-0.8634%0.1457%-3.4490%
2018-10-23-3.6571%-1.6658%-0.4201%-0.4520%-0.2797%-0.5034%0.1844%-3.9948%-0.7051%-0.2449%...0.4766%-5.1240%0.5341%-0.0321%1.0713%-0.6728%-1.0807%-1.8308%4.0560%4.0353%
2018-10-24-4.5500%1.3141%-1.8042%-3.5933%-4.2918%0.8674%0.9995%-4.1109%-3.6759%-3.7140%...3.5178%-4.2683%1.5636%-1.3479%-8.0557%-0.4838%-1.2403%-4.2187%0.3671%-3.3065%
2018-10-250.4741%2.5715%0.5186%0.7782%5.0411%-0.5733%-1.1719%2.1585%3.1657%2.3271%...-1.0309%0.4914%0.5082%0.9109%-1.2517%1.8962%4.3662%1.3800%-1.7241%3.3538%
\n", "

5 rows × 25 columns

\n", "
" ], "text/plain": [ " APA BA BAX BMY CMCSA CNP CPB \\\n", "Date \n", "2018-10-19 0.0475% -0.8599% -1.4333% -3.0011% 0.1113% 1.2968% 3.4360% \n", "2018-10-22 -1.9240% -0.0786% -0.6335% -6.2983% -0.6393% -1.1024% 0.0527% \n", "2018-10-23 -3.6571% -1.6658% -0.4201% -0.4520% -0.2797% -0.5034% 0.1844% \n", "2018-10-24 -4.5500% 1.3141% -1.8042% -3.5933% -4.2918% 0.8674% 0.9995% \n", "2018-10-25 0.4741% 2.5715% 0.5186% 0.7782% 5.0411% -0.5733% -1.1719% \n", "\n", " DE HPQ JCI ... NI PCAR PSA \\\n", "Date ... \n", "2018-10-19 -0.8763% 0.2945% -0.7834% ... 0.6339% -0.1823% 0.9185% \n", "2018-10-22 -0.3221% 1.1325% -0.8199% ... -0.8662% 0.4483% -1.6953% \n", "2018-10-23 -3.9948% -0.7051% -0.2449% ... 0.4766% -5.1240% 0.5341% \n", "2018-10-24 -4.1109% -3.6759% -3.7140% ... 3.5178% -4.2683% 1.5636% \n", "2018-10-25 2.1585% 3.1657% 2.3271% ... -1.0309% 0.4914% 0.5082% \n", "\n", " SEE T TGT TMO TXT VZ ZION \n", "Date \n", "2018-10-19 -0.7728% 1.1385% -1.6075% -1.1145% -1.2872% 0.4575% -0.8025% \n", "2018-10-22 -2.8972% -0.6085% 1.4752% -0.6075% -0.8634% 0.1457% -3.4490% \n", "2018-10-23 -0.0321% 1.0713% -0.6728% -1.0807% -1.8308% 4.0560% 4.0353% \n", "2018-10-24 -1.3479% -8.0557% -0.4838% -1.2403% -4.2187% 0.3671% -3.3065% \n", "2018-10-25 0.9109% -1.2517% 1.8962% 4.3662% 1.3800% -1.7241% 3.3538% \n", "\n", "[5 rows x 25 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Calculating returns\n", "\n", "Y = data[assets].iloc[-300:,:].pct_change().dropna()\n", "\n", "display(Y.head())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Estimating OWA Portfolios\n", "\n", "The OWA portfolio model proposed by __[Cajas (2021)](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3988927)__ . This model gives an alternative formulation to risk measures that can be expressed using the OWA operator.\n", "\n", "It is recommended to use MOSEK to optimize OWA portfolios, due to it requires more computing power for the number of constraints and variables the model use.\n", "\n", "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/)__.\n", "\n", "### 2.1 Comparing Classical formulations vs OWA formulations.\n", "\n", "In this case we are going to compare the optimal weights using classical formulations and owa formulation of two risk measures: Conditional Value at Risk (CVaR) and Worst Realization (WR or Minimax model)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
 Classic MinRisk CVaROWA MinRisk CVaRdiff MinRisk CVaRClassic Sharpe CVaROWA Sharpe CVaRdiff Sharpe CVaRClassic MinRisk WROWA MinRisk WRdiff MinRisk WRClassic Sharpe WROWA Sharpe WRdiff Sharpe WR
APA0.00%0.00%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
BA3.79%3.79%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
BAX0.00%0.00%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
BMY9.47%9.47%-0.00%1.84%1.84%-0.00%12.88%12.88%-0.00%0.00%0.00%0.00%
CMCSA0.87%0.87%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
CNP9.42%9.42%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
CPB6.95%6.95%-0.00%28.53%28.53%-0.00%0.00%0.00%0.00%33.71%33.71%0.00%
DE0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
HPQ0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
JCI0.00%0.00%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
JPM0.00%0.00%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
LUV0.00%0.00%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
MMC0.00%0.00%0.00%24.42%24.42%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%
MO0.00%0.00%-0.00%0.00%0.00%0.00%21.61%21.61%0.00%0.00%0.00%0.00%
MSFT0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
NI0.00%0.00%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
PCAR0.00%0.00%-0.00%11.95%11.95%0.00%0.00%0.00%0.00%13.79%13.79%0.00%
PSA25.40%25.40%0.00%10.28%10.28%-0.00%40.27%40.27%0.00%0.00%0.00%0.00%
SEE2.27%2.27%0.00%0.00%0.00%0.00%8.76%8.76%0.00%0.00%0.00%0.00%
T4.52%4.52%-0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
TGT4.85%4.85%-0.00%22.28%22.28%0.00%16.48%16.48%-0.00%40.40%40.40%0.00%
TMO0.00%0.00%-0.00%0.70%0.70%0.00%0.00%0.00%0.00%12.09%12.09%-0.00%
TXT0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
VZ32.46%32.46%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
ZION0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
\n" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import riskfolio as rp\n", "import mosek\n", "\n", "# Building the portfolio object\n", "port = rp.Portfolio(returns=Y)\n", "\n", "# Calculating optimum portfolio\n", "\n", "# Select method and estimate input parameters:\n", "\n", "method_mu='hist' # Method to estimate expected returns based on historical data.\n", "method_cov='hist' # Method to estimate covariance matrix based on historical data.\n", "\n", "port.assets_stats(method_mu=method_mu, method_cov=method_cov, d=0.94)\n", "\n", "# Estimate optimal portfolios:\n", "\n", "port.solvers = ['MOSEK'] # It is recommended to use mosek when optimizing GMD\n", "port.sol_params = {'MOSEK': {'mosek_params': {mosek.iparam.num_threads: 2}}}\n", "alpha = 0.05\n", "\n", "port.alpha = alpha\n", "model ='Classic' # Could be Classic (historical), BL (Black Litterman) or FM (Factor Model)\n", "rms = ['CVaR', 'WR'] # Risk measure used, this time will be CVaR and Worst Realization\n", "objs = ['MinRisk', 'Sharpe'] # Objective function, could be MinRisk, MaxRet, Utility or Sharpe\n", "hist = True # Use historical scenarios for risk measures that depend on scenarios\n", "rf = 0 # Risk free rate\n", "l = 0 # Risk aversion factor, only useful when obj is 'Utility'\n", "\n", "ws = pd.DataFrame([])\n", "for rm in rms:\n", " for obj in objs:\n", " # Using Classical models\n", " w = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist)\n", " # Using OWA model\n", " if rm == \"CVaR\":\n", " owa_w = rp.owa_cvar(len(Y), alpha=alpha)\n", " elif rm == 'WR':\n", " owa_w = rp.owa_wr(len(Y))\n", " w1 = port.owa_optimization(obj=obj, owa_w=owa_w, rf=rf, l=l)\n", " ws1 = pd.concat([w, w1], axis=1)\n", " ws1.columns = ['Classic ' + obj + ' ' + rm, 'OWA ' + obj + ' ' + rm]\n", " ws1['diff ' + obj + ' ' + rm] = ws1['Classic ' + obj + ' ' + rm] - ws1['OWA ' + obj + ' ' + rm]\n", " ws = pd.concat([ws, ws1], axis=1)\n", "\n", "ws.style.format(\"{:.2%}\").background_gradient(cmap='YlGn', vmin=0, vmax=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see, classical and OWA formulations give us the same returns because both problem are equivalent." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.13" } }, "nbformat": 4, "nbformat_minor": 4 }