Scenario Optimization Example
import pandas as pd
import numpy as np
import cvxpy as cp
Scenario Optimization¶
The following problem offers six independent bonds, each with two possible returns. In total they have $2^6=64$ scenarios of payoffs, and the associated probabilities can also be calculated.
We would like to build portfolios to meet a future liability while minimizing certain risk metrics.
bonddata = pd.read_csv('sixbonds.csv')
vals = bonddata.values
# r: bond returns in all scenarios
# probability of scenarios
r = vals[:,:6] ; p = vals[:,6]
r[:5,:]
array([[ 0.25, 0.24, 0.23, 0.2 , 0.19, 0.18], [ 0.25, 0.24, 0.23, 0.2 , 0.19, -0.2 ], [ 0.25, 0.24, 0.23, 0.2 , -0.2 , 0.18], [ 0.25, 0.24, 0.23, 0.2 , -0.2 , -0.2 ], [ 0.25, 0.24, 0.23, -0.2 , 0.19, 0.18]])
p[:5]
array([0.30754687, 0.10251562, 0.10251562, 0.03417187, 0.10251562])
Equally Weighted Portfolio Expected Shortfall¶
Calculate the expected shortfall for a equally weighted portfolio.
# number of bonds
N = 6
# initial capital
W = 100
# number of scenarios
S = 64
L = 115
# dollar holdings
v = np.ones(N) * W/N
shortfall = np.ones(S) * L - (1 + r)@v
shortfall = shortfall * (shortfall > 0)
# expected shortfall
p.T@shortfall
2.591140624999996
Minimize the Expected Shortfall¶
Find a portfolio such that it minimizes the expected shortfall.
For any scenario, the shortfall equals $$ s = \max(0, 115 - (1+r)\times W_0) $$ Which is none-linear, and we need to introduce $s$ as another variable and turn this into linear constraints, which is, $$ s \ge 0 \\ s >= 115 - (1+r) \times W_0 $$
v = cp.Variable(N)
shortfall = cp.Variable(S)
# sum of dollar values = 100, long only
# only consider the shortfalls (0 if positive returns)
constraints = [np.ones(6)@v == W, v>=0, shortfall >= L*np.ones(S) - (1+r)@v, shortfall>=0]
# minimize the expectation of expected shortfall
prob = cp.Problem(cp.Minimize(p.T@shortfall),constraints)
prob.solve()
print(prob.value)
1.798079221277825
max(shortfall.value)
35.00000067456137
Equal Portfolio - CVaR¶
Use the following problem setup, $Y$ is the loss function and $\beta \in (0,1)$
$$ \operatorname{VaR}_\beta(Y):=\min \{\gamma: \mathbb{P}(Y \geq \gamma) \leq 1-\beta\} \\ \operatorname{CVaR}_\beta(Y):=\mathbb{E}\left(Y \mid Y \geq \operatorname{VaR}_\beta(Y)\right) $$In another way, denote $\gamma$ as $\operatorname{VaR}_\beta(Y)$, then CVaR can be expressed as an optimization problem where
$$ \operatorname{CVaR}_\beta(Y)=\min _\gamma\left(\gamma+\frac{1}{1-\beta} \mathbb{E}[\max (Y-\gamma, 0)]\right) $$Our first goal is to find the 95% CVaR of the loss.
Again, the max function is non-linear, introduce the variable $z$ as the redundant and translate it into linear constraints.
beta = 0.95
z = cp.Variable(S)
gamma = cp.Variable(1)
# loss function
equalport_payoff = (1+r)@np.ones(N) * W/N
loss = L*np.ones(S) - equalport_payoff
# objective and constraints
objective = cp.Minimize(gamma + p.T@z/(1-beta))
constraints = [z >= loss - gamma*np.ones(S), z>= 0]
# solve the problem
prob = cp.Problem(objective, constraints)
prob.solve(solver=cp.GUROBI)
cvar = gamma.value[0] + p.T@z.value/(1-beta)
print("VaR = ", gamma.value[0])
print("CVaR = ", cvar)
VaR = 13.666666666666686 CVaR = 15.349114583333341
Minimize CVaR¶
Ignore the equal portfolio assumption, and make the dollar holdings as variables. Redo the problem.
beta = 0.95
z = cp.Variable(S)
gamma = cp.Variable(1)
# loss function
v = cp.Variable(N)
loss = L*np.ones(S) - (1 + r)@v
# objective and constraints
objective = cp.Minimize(gamma + p.T@z/(1-beta))
constraints = [z >= loss - gamma*np.ones(S), z>= 0, cp.sum(v) == W, v >= 0]
# solve the problem
prob = cp.Problem(objective, constraints)
prob.solve(solver=cp.GUROBI)
cvar = gamma.value[0] + p.T@z.value/(1-beta)
print(cvar)
14.078051233528184