{ "cells": [ { "attachments": {}, "cell_type": "markdown", "id": "d760e6e6", "metadata": {}, "source": [ "# Relativistic Value at Risk Portfolio Optimization\n", "\n", "In this notebook we show how to use the power cone and its application in portfolio optimization.\n", "\n", "## 1. Relativistic Value at Risk (RLVaR) Optimization\n", "\n", "__[Cajas (2023)](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3792520)__ proposed a generalization of Entropic Value at Risk (EVaR) __[Ahmadi-Javid (2012)](https://link.springer.com/article/10.1007/s10957-011-9968-2?r=1&l=ri&fst=0&error=cookies_not_supported&code=ccfb8a5e-692b-43d1-b76e-ae596c7f0bed)__ based on Kaniadakis entropy __[Kaniadakis (2001)](https://www.sciencedirect.com/science/article/pii/S0378437101001844)__. We can pose the minimization of Relativistic Value at Risk as the following convex programming problem: \n", "\n", "$$\n", "\\begin{equation}\n", "\\begin{aligned}\n", "& \\underset{x, z, t, \\psi, \\theta, \\varepsilon, \\omega}{\\text{min}} & & t + z \\ln_{\\kappa} \\left ( \\frac{1}{\\alpha T} \\right ) + \\sum^T_{j=1} \\left ( \\psi_{j} + \\theta_{j} \\right ) \\\\\n", "& \\text{s.t.} & & -r_{j} x - t + \\varepsilon_{j} + \\omega_{j} \\leq 0 \\; ; \\; \\forall j =1, \\ldots ,T \\\\\n", "& & & z \\geq 0 \\\\\n", "& & & \\left ( z \\left ( \\frac{1+\\kappa}{2\\kappa} \\right ), \\psi_{j} \\left ( \\frac{1+\\kappa}{\\kappa} \\right ), \\varepsilon_{j} \\right) \\in \\mathcal{P}_3^{1/(1+\\kappa),\\, \\kappa/(1+\\kappa)} \\\\ \n", "& & & \\left ( \\omega_{j}\\left ( \\frac{1}{1-\\kappa} \\right ), \\theta_{j}\\left ( \\frac{1}{\\kappa} \\right), -z \\left ( \\frac{1}{2\\kappa} \\right ) \\right ) \\in \\mathcal{P}_3^{1-\\kappa,\\, \\kappa} \\\\\n", "& & & \\sum_{i=1}^{n} x_i = 1 \\\\\n", "& & & x_i \\geq 0 \\; ; \\; \\forall \\; i =1, \\ldots, n \\\\\n", "\\end{aligned}\n", "\\end{equation}\n", "\n", "$$\n", "\n", "Where $t$, $z$ $\\psi$, $\\theta$, $\\epsilon$ and $\\omega$ are auxiliary variables, $x$ are the weights of portfolio assets of size $n \\times 1$, $\\mathcal{P}^{\\alpha, 1-\\alpha}$ is a power cone, $r$ is the matrix of observed returns and $\\kappa$ is the deformation parameter of Kaniadakis logarithm." ] }, { "cell_type": "code", "execution_count": 1, "id": "2a19278a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[*********************100%***********************] 14 of 14 completed\n" ] }, { "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", "
BAXBMYCMCSACNPCPBGEGOOGMOMSFTNISEETTGTVZ
Date
2016-01-050.4035%1.9693%0.0180%0.9305%0.3678%0.0977%0.0998%2.0213%0.4562%1.5881%0.9759%0.6987%1.7539%1.3734%
2016-01-060.2412%-1.7557%-0.7727%-1.2473%-0.1736%-1.5940%0.1400%1.0589%-1.8165%0.5547%-1.5647%0.3108%-1.0155%-0.9034%
2016-01-07-1.6573%-2.7699%-1.1047%-1.9770%-1.2206%-4.2314%-2.3170%-1.7407%-3.4783%-2.2067%-3.1557%-1.6148%-0.2700%-0.5492%
2016-01-08-1.6037%-2.5425%0.1099%-0.2241%0.5706%-1.7949%-1.6410%0.1720%0.3067%-0.1538%-0.1448%0.0896%-3.3839%-0.9719%
2016-01-11-1.6851%-1.0215%0.0914%-1.1791%0.5674%0.4569%0.2183%2.0947%-0.0573%1.6436%-0.1450%1.2224%1.4570%0.5799%
\n", "
" ], "text/plain": [ " BAX BMY CMCSA CNP CPB GE GOOG \\\n", "Date \n", "2016-01-05 0.4035% 1.9693% 0.0180% 0.9305% 0.3678% 0.0977% 0.0998% \n", "2016-01-06 0.2412% -1.7557% -0.7727% -1.2473% -0.1736% -1.5940% 0.1400% \n", "2016-01-07 -1.6573% -2.7699% -1.1047% -1.9770% -1.2206% -4.2314% -2.3170% \n", "2016-01-08 -1.6037% -2.5425% 0.1099% -0.2241% 0.5706% -1.7949% -1.6410% \n", "2016-01-11 -1.6851% -1.0215% 0.0914% -1.1791% 0.5674% 0.4569% 0.2183% \n", "\n", " MO MSFT NI SEE T TGT VZ \n", "Date \n", "2016-01-05 2.0213% 0.4562% 1.5881% 0.9759% 0.6987% 1.7539% 1.3734% \n", "2016-01-06 1.0589% -1.8165% 0.5547% -1.5647% 0.3108% -1.0155% -0.9034% \n", "2016-01-07 -1.7407% -3.4783% -2.2067% -3.1557% -1.6148% -0.2700% -0.5492% \n", "2016-01-08 0.1720% 0.3067% -0.1538% -0.1448% 0.0896% -3.3839% -0.9719% \n", "2016-01-11 2.0947% -0.0573% 1.6436% -0.1450% 1.2224% 1.4570% 0.5799% " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "####################################\n", "# Downloading Data\n", "####################################\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import yfinance as yf\n", "import warnings\n", "\n", "warnings.filterwarnings(\"ignore\")\n", "\n", "yf.pdr_override()\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 = ['TGT', 'CMCSA', 'CPB', 'MO', 'T', 'BAX', 'BMY',\n", " 'MSFT', 'SEE', 'VZ', 'CNP', 'NI', 'GE', 'GOOG']\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\n", "\n", "# Calculating returns\n", "Y = data[assets].pct_change().dropna()\n", "\n", "display(Y.head())" ] }, { "cell_type": "code", "execution_count": 2, "id": "94d0f382", "metadata": {}, "outputs": [], "source": [ "####################################\n", "# Auxiliary functions\n", "####################################\n", "\n", "# Function that calculates the Kaniadakis logarithm\n", "def ln_k(x, kappa=0.3):\n", " return (x**kappa - x**(-kappa))/(2*kappa)" ] }, { "cell_type": "code", "execution_count": 3, "id": "583f7d9a", "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", "
Weights
BAX0.0000%
BMY9.9856%
CMCSA6.9171%
CNP12.0874%
CPB15.2284%
GE0.0000%
GOOG5.4628%
MO0.0000%
MSFT0.0000%
NI17.1999%
SEE14.5753%
T0.0000%
TGT18.5435%
VZ0.0000%
\n", "
" ], "text/plain": [ " Weights\n", "BAX 0.0000%\n", "BMY 9.9856%\n", "CMCSA 6.9171%\n", "CNP 12.0874%\n", "CPB 15.2284%\n", "GE 0.0000%\n", "GOOG 5.4628%\n", "MO 0.0000%\n", "MSFT 0.0000%\n", "NI 17.1999%\n", "SEE 14.5753%\n", "T 0.0000%\n", "TGT 18.5435%\n", "VZ 0.0000%" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "####################################\n", "# Finding the Min EVaR Portfolio\n", "####################################\n", "\n", "import cvxpy as cp\n", "\n", "# Defining initial parameters\n", "returns = Y.to_numpy()\n", "T, n = Y.shape\n", "alpha = 0.05\n", "kappa = 0.3\n", "\n", "# Defining initial variables\n", "w = cp.Variable((n, 1))\n", "X = returns @ w\n", "t = cp.Variable((1, 1))\n", "z = cp.Variable((1, 1), nonneg=True)\n", "omega = cp.Variable((T, 1))\n", "psi = cp.Variable((T, 1))\n", "theta = cp.Variable((T, 1))\n", "epsilon = cp.Variable((T, 1))\n", "onesvec = np.ones((T, 1))\n", "\n", "constraints = [\n", " cp.PowCone3D(\n", " z * (1 + kappa) / (2 * kappa) * onesvec,\n", " psi * (1 + kappa) / kappa,\n", " epsilon,\n", " 1 / (1 + kappa),\n", " ),\n", " cp.PowCone3D(\n", " omega / (1 - kappa),\n", " theta / kappa,\n", " -z / (2 * kappa) * onesvec,\n", " (1 - kappa),\n", " ),\n", " -X * 1000 - t * 1000 + epsilon * 1000 + omega * 1000 <= 0,\n", " cp.sum(w) == 1,\n", " w >= 0\n", "]\n", "\n", "# Defining risk objective\n", "risk = t + z * ln_k(1 / (alpha * T), kappa) + cp.sum(psi + theta)\n", "objective = cp.Minimize(risk)\n", "\n", "# Solving problem\n", "prob = cp.Problem(objective, constraints)\n", "prob.solve()\n", "\n", "# Showing Optimal Weights\n", "weights = pd.DataFrame(w.value, index=assets, columns=['Weights'])\n", "display(weights)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "de8cc7f7", "metadata": {}, "source": [ "This portfolio optimization model is available in the CVXPY based library __[Riskfolio-Lib](https://github.com/dcajasn/Riskfolio-Lib)__.\n", "\n", "Finally, I hope you liked this example." ] } ], "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.9" } }, "nbformat": 4, "nbformat_minor": 5 }