I am Building a risk Parity Portfolio where the risk contribution ("RC") of each asset to Portfolio risk should be identical. Given the 3 asset example below, the constraint should be something like this:
def constraint2(x):
return RC[0] = RC[1] = RC[2]
The contribution of each asset to Portfolio risk should be equal for each element in the RC vector. How can I implement constraints like these using scipy.optimize?
Using Excel sover I got the following Portfolio weights: A = 15%, B = 73%, C = 12%.
Any help is highly appreciated! :) Please find my sample Code below.
def risk_contribution(w):
pf_var = np.matrix(w) * np.matrix(cova) * np.transpose(np.matrix(w)) #calculate Portfolio variance
MRC = np.matrix(cova) * np.transpose(np.matrix(w)) #Marginal risk contribution
RC = np.multiply(np.transpose(np.matrix(w)),np.matrix(MRC)) #Risk contribution
sum_RC = np.sum(np.matrix(RC))
return sum_RC
RW = np.divide(np.matrix(RC),np.matrix(pf_var))
def constraint1(w):
return np.sum(w) - 1
w0 = np.matrix([0.333, 0.333, 0.333])
cova = [0.04,0.002,0.03],[0.002,0.003,0.001],[0.03,0.001,0.063]
con1 = {"type": "eq", "fun": constraint1}
cons = [con1]
b = (0,None)
bnds = (b,b,b)
print(minimize(risk_contribution, w0, method="SLSQP", bounds=bnds, constraints=cons))
EDIT: So I came up with the following Code, which is slightly different from the previous Code since it minimizes the squared difference between each asset risk contribution and the risk target. I get the same result for the objective J as in Excel, but when running the optimization, the optimal weights are different then the solution in excel. I get x: array([3.46944695e-18, 5.00000000e-01, 5.00000000e-01]) in python as compared to the (correct) solution in Excel (15%,73%,12%). So I assume there must be something wrong with the optimization since the objective function itself seems to be correct. Can't figure out what it could be, however.
def objective(w, V, x_t):
vol = ((w*V)*np.transpose(w))[0,0] #Portfolio variance
sig = np.sqrt(vol) #stimmt
risk_target = np.matrix(np.multiply(sig,x_t))
MRC = V*np.transpose(w)
RC = np.multiply(MRC,np.transpose(w))
RC = RC/ sig
RC = (np.multiply(MRC,w.T))/ sig #bis hierhin stimmt alles überein
J = sum(np.square(RC - risk_target.T))[0, 0]
def constraint1(w):
return np.sum(w) - 1
w0 = np.matrix([1/3, 1/3, 1/3]) #initial weights
V = [0.04,0.0015,0.03],[0.0015,0.0025,0.000625],[0.03,0.000625,0.0625] #sample covariance matrix
x_t = [1/3, 1/3, 1/3] # risk budget percent of total portfolio risk
cons = ({"type":"eq", "fun": constraint1}) #constraint 1 that weights add up to 1
b = (0,None) #constraint 2 that weights >= 0
bnds = (b,b,b)
res = minimize(objective, w0, args=(V, x_t), method='SLSQP', bounds=bnds, constraints=cons)
print(res)