A is capped
Asset A sits at 0.70, exactly its cap. Its slack is zero, so its upper-bound dual can be positive.
Turn the dual-value diagnostic into a concrete two-asset CVXPY model.
In Lesson 0006, you read a solver table. Now build the table yourself from a tiny quadratic program.
The model is intentionally small: choose weights for assets A and B, reward expected return, penalize variance, require full investment, and cap each asset.
minimize 0.5 * w' Sigma w - mu' w
subject to sum(w) = 1
0 <= w
w <= cap
This is the minimization form used by many solvers. The return term is negative because higher expected return should reduce the objective.
Run this after installing CVXPY in a Python environment. The official CVXPY examples use the same pattern: create variables, build constraints, solve, then inspect each constraint's dual_value.
import cvxpy as cp
import numpy as np
mu = np.array([0.12, 0.02])
Sigma = np.diag([0.10, 0.20])
cap = np.array([0.70, 0.80])
w = cp.Variable(2)
risk = 0.5 * cp.quad_form(w, Sigma)
ret = mu @ w
objective = cp.Minimize(risk - ret)
constraints = [
w >= 0,
w <= cap,
cp.sum(w) == 1,
]
problem = cp.Problem(objective, constraints)
problem.solve()
print("status:", problem.status)
print("weights:", np.round(w.value, 4))
print("upper slack:", np.round(cap - w.value, 4))
print("upper duals:", np.round(constraints[1].dual_value, 4))
print("budget dual:", round(float(constraints[2].dual_value), 4))
The numbers are chosen so that the unconstrained optimizer wants more asset A than the cap allows. After solving, you should see this shape:
status: optimal weights: [0.7000 0.3000] upper slack: [0.0000 0.5000] upper duals: [0.0900 0.0000]
Asset A sits at 0.70, exactly its cap. Its slack is zero, so its upper-bound dual can be positive.
Asset B sits at 0.30, below its 0.80 cap. Its slack is positive, so its upper-bound dual should be zero.
The full-investment constraint is an equality. Its dual is not read as slack times pressure and can have either sign.
Because w_B = 1 - w_A, the unconstrained one-dimensional objective has its minimum at w_A = 1.00. That is infeasible because w_A <= 0.70.
unconstrained wants: w_A = 1.00 cap allows only: w_A = 0.70 therefore: A cap binds w_B = 1 - w_A = 0.30 B cap slack = 0.80 - 0.30 = 0.50
This hand check is the habit you want before trusting larger portfolio optimizers. Solver output should match the economic story.
- mu' w in this model?w_A = 0.70 and cap_A = 0.70, what is A's upper slack?0.50, what should B's upper-cap dual be?Use CVXPY's official quadratic-program example for the modeling pattern and its dual variables guide for reading dual_value. Keep the CVXPY portfolio template open while modifying the example.