Risk Budget Optimization Requires Solving Before Weight Normalization
Summary
The document examines a convex optimization formulation for constructing a portfolio with a target risk budget. The stated objective combines portfolio variance with a logarithmic penalty weighted by each asset’s desired budget. The questioner imposes a full-investment constraint during optimization and finds that the resulting contributions to portfolio variance do not match the target budgets. A Lagrange multiplier argument is used to show why the sum-to-one constraint changes the relationship between the objective’s budget parameters and the resulting risk contributions.
The accepted answer recommends solving the optimization without the full-investment constraint, then dividing the resulting weights by their sum. In the supplied example, this procedure produces risk contributions close to equal target shares, whereas the constrained solution does not. This provides a practical correction for the formulation discussed, supported by a numerical example. The material does not establish conditions for every covariance matrix or portfolio constraint, and the result depends on valid positive weights and a suitable covariance input; broader constraints may require a different formulation.
Key ideas
- The logarithmic risk budget objective can be formulated as a convex optimization over positive portfolio weights.
- Imposing a sum-to-one constraint during the described optimization can prevent the resulting risk contributions from matching the target budget.
- The Lagrange multiplier links the constrained solution’s risk contributions to both the budget parameters and portfolio weights.
- The accepted approach optimizes without the full-investment equality and normalizes the weights afterward.
- A numerical example reports that post-optimization normalization recovers equal risk contributions within numerical tolerance.
Tags
Full text
# How to understand this convex optimization method to find risk budget portfolio
# How to understand this convex optimization method to find risk budget portfolio
Both the short course material coded by the CVXPY developers and an answer on Quant SE suggest that given a desired risk budget $b$, we can find the full-investment portfolio with weights $w$ that has the risk budget (as defined in those materials) equal to $b$ by performing the following convex optimization:
$$\begin{align}\text{Minimize}\;&\frac{1}{2}w'\Sigma w - \sum_i b_i\,\text{log}w_i \\ \text{subject to} \;& 1'w=1\end{align}$$
However, having done this myself in Python with CVXPY, I found the resulting risk budgets were not the same as the desired $b$.
I then tried to calculate this minimization by hand, and I found that the solution $w$ has
$$(\Sigma w)_i - \frac{b_i}{w_i} = \lambda\;\;\;\text{for all }i$$
where $\lambda$ is the Lagrangian multiplier.
In other words, since we can show that the $i$-th risk budget ${b_w}_i$ by definition is equal to $\frac{w_i(\Sigma w)_i}{w'\Sigma w}$, the solution to that optimization problem has:
$${b_w}_i = \dfrac{b_i+\lambda w_i}{w'\Sigma w} = \dfrac{b_i+\lambda w_i}{1 + \lambda}$$
and this in general is not equal to $b_i$ (otherwise, $w_i=b_i$). This has been verified by the optimization that I ran in Python.
But I'm sure this method is not wrong--there's an academic paper written to explain it by Spinu (2013), which is beyond my capabilities. So, I'd really appreciate anyone who can explain this formulation!
### Update:
Here's the Python code that I wrote. It's an exercise that's part of the CVXPY short course.
```
import numpy as np
import cvxpy as cp
#input data
Sigma = np.array([[6.1, 2.9, -0.8, 0.1],
[2.9, 4.3, -0.3, 0.9],
[-0.8, -0.3, 1.2, -0.7],
[0.1, 0.9, -0.7, 2.3]])
b = np.ones(4)/4 #risk parity
# optimization
w = cp.Variable(4) #portfolio weight
obj = 0.5 * cp.quad_form(w, Sigma) - cp.sum(cp.multiply(b, cp.log(w))) #objective
constr = [cp.sum(w) == 1, w >= 0] # constraint
prob = cp.Problem(cp.Minimize(obj), constr)
prob.solve()
# print the solution weight and solution risk budget
b_w = cp.multiply(w, Sigma @ w) / cp.quad_form(w, Sigma) #solution risk budget
print("The solution weight is", w.value)
print("The solution risk budget is", b_w.value)
```
And the printed outputs are:
`The solution weight is [0.16073365 0.14918463 0.42056612 0.2695156 ]`
`The solution risk budget is [0.32355772 0.33307394 0.10944985 0.23391849]`
## Answer by nbbo2 (score 2, accepted)
https://quant.stackexchange.com/a/71316
I believe the problem should be solved in two steps: the optimization without the condition that the sum equals 1, followed by a normalization step which divides w by sum(w) to produce the desired solution.
Here is the modified code:
```
import numpy as np
import cvxpy as cp
#input data
Sigma = np.array([[6.1, 2.9, -0.8, 0.1],
[2.9, 4.3, -0.3, 0.9],
[-0.8, -0.3, 1.2, -0.7],
[0.1, 0.9, -0.7, 2.3]])
b = np.ones(4)/4 #risk parity
# optimization
w = cp.Variable(4) #portfolio weight
obj = 0.5 * cp.quad_form(w, Sigma) - cp.sum(cp.multiply(b, cp.log(w)))
#objective
constr = [w >= 0] # constraint
prob = cp.Problem(cp.Minimize(obj), constr)
prob.solve()
# normalize
w = w/cp.sum(w)
# print the solution weight and solution risk budget
b_w = cp.multiply(w, Sigma @ w) / cp.quad_form(w, Sigma) #solution risk budget
print("The solution weight is", w.value)
print("The solution risk budget is", b_w.value)
```
The result
```
The solution weight is [0.13765302 0.11336252 0.4758825 0.27310195]
The solution risk budget is [0.25000012 0.25000013 0.24999967 0.25000007]
```
The marginal values are equal (within numerical error).Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.