In this notebook we show how to use the power cone and its application in portfolio optimization.
Cajas (2023) proposed a generalization of Entropic Value at Risk (EVaR) Ahmadi-Javid (2012) based on Kaniadakis entropy Kaniadakis (2001). We can pose the minimization of Relativistic Value at Risk as the following convex programming problem:
$$ \begin{equation} \begin{aligned} & \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 ) \\ & \text{s.t.} & & -r_{j} x - t + \varepsilon_{j} + \omega_{j} \leq 0 \; ; \; \forall j =1, \ldots ,T \\ & & & z \geq 0 \\ & & & \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)} \\ & & & \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} \\ & & & \sum_{i=1}^{n} x_i = 1 \\ & & & x_i \geq 0 \; ; \; \forall \; i =1, \ldots, n \\ \end{aligned} \end{equation} $$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.
####################################
# Downloading Data
####################################
import numpy as np
import pandas as pd
import yfinance as yf
import warnings
warnings.filterwarnings("ignore")
yf.pdr_override()
pd.options.display.float_format = '{:.4%}'.format
# Date range
start = '2016-01-01'
end = '2019-12-30'
# Tickers of assets
assets = ['TGT', 'CMCSA', 'CPB', 'MO', 'T', 'BAX', 'BMY',
'MSFT', 'SEE', 'VZ', 'CNP', 'NI', 'GE', 'GOOG']
assets.sort()
# Downloading data
data = yf.download(assets, start = start, end = end)
data = data.loc[:,('Adj Close', slice(None))]
data.columns = assets
# Calculating returns
Y = data[assets].pct_change().dropna()
display(Y.head())
[*********************100%***********************] 14 of 14 completed
BAX | BMY | CMCSA | CNP | CPB | GE | GOOG | MO | MSFT | NI | SEE | T | TGT | VZ | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||
2016-01-05 | 0.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-06 | 0.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% |
####################################
# Auxiliary functions
####################################
# Function that calculates the Kaniadakis logarithm
def ln_k(x, kappa=0.3):
return (x**kappa - x**(-kappa))/(2*kappa)
####################################
# Finding the Min EVaR Portfolio
####################################
import cvxpy as cp
# Defining initial parameters
returns = Y.to_numpy()
T, n = Y.shape
alpha = 0.05
kappa = 0.3
# Defining initial variables
w = cp.Variable((n, 1))
X = returns @ w
t = cp.Variable((1, 1))
z = cp.Variable((1, 1), nonneg=True)
omega = cp.Variable((T, 1))
psi = cp.Variable((T, 1))
theta = cp.Variable((T, 1))
epsilon = cp.Variable((T, 1))
onesvec = np.ones((T, 1))
constraints = [
cp.PowCone3D(
z * (1 + kappa) / (2 * kappa) * onesvec,
psi * (1 + kappa) / kappa,
epsilon,
1 / (1 + kappa),
),
cp.PowCone3D(
omega / (1 - kappa),
theta / kappa,
-z / (2 * kappa) * onesvec,
(1 - kappa),
),
-X * 1000 - t * 1000 + epsilon * 1000 + omega * 1000 <= 0,
cp.sum(w) == 1,
w >= 0
]
# Defining risk objective
risk = t + z * ln_k(1 / (alpha * T), kappa) + cp.sum(psi + theta)
objective = cp.Minimize(risk)
# Solving problem
prob = cp.Problem(objective, constraints)
prob.solve()
# Showing Optimal Weights
weights = pd.DataFrame(w.value, index=assets, columns=['Weights'])
display(weights)
Weights | |
---|---|
BAX | 0.0000% |
BMY | 9.9856% |
CMCSA | 6.9171% |
CNP | 12.0874% |
CPB | 15.2284% |
GE | 0.0000% |
GOOG | 5.4628% |
MO | 0.0000% |
MSFT | 0.0000% |
NI | 17.1999% |
SEE | 14.5753% |
T | 0.0000% |
TGT | 18.5435% |
VZ | 0.0000% |
This portfolio optimization model is available in the CVXPY based library Riskfolio-Lib.
Finally, I hope you liked this example.