robertmartin8 / PyPortfolioOpt

Financial portfolio optimisation in python, including classical efficient frontier, Black-Litterman, Hierarchical Risk Parity
https://pyportfolioopt.readthedocs.io/
MIT License
4.38k stars 940 forks source link

Using Tracking error as a constraint in portfolio optimization #394

Closed gmulaku closed 2 years ago

gmulaku commented 2 years ago

What are you trying to do? I am trying to optimize my portfolio of 22 stocks but I have to limit the Tracking Error to 4%, with respect to MSCI World index.

What have you tried? I am able to obtain the optimal allocation without the TE using Scipy.optimize. You can find my code here :

mu = np.mean(dataframe).to_frame()
mu = np.array(mu)

corrMatrix = dataframe.corr()
corrMatrix = np.array(corrMatrix)

sigmaVec = dataframe.std().to_frame()
sigmaVec = np.array(sigmaVec)

Sigma = np.multiply(corrMatrix, np.dot(sigmaVec, sigmaVec.T))

numberOfAssets = sigmaVec.shape[0]
e = np.ones((numberOfAssets, 1))    # Alternatively: e = np.ones_like(sigmaVec)

# risk aversion parameter
lam = 1    # ("lambda" denotes a type of functions similar to Matlab's function handle @)

Aeq = e.T
beq = 1

# function to minimize (quadratic programming)
objective_1 = lambda x: 0.5 * lam * np.dot(np.dot(x.T, Sigma), x) - np.dot(x.T, mu)
# equality constraint
eq_constraint_1 = lambda x: np.dot(Aeq, x) - beq
cons_1 = ({'type': 'eq', 'fun': eq_constraint_1})
# starting point for the solver
x0 = np.ones((numberOfAssets, 1)) / numberOfAssets

bounds = [(-1 , 1) for i in range(0,22)]
# run the optimization
result = optimize.minimize(objective_1, x0, bounds = bounds, method='SLSQP', tol=1e-8, constraints=cons_1)
w_1 = result.x.reshape((numberOfAssets, 1))

exp_ret_1 = np.dot(w_1.T, mu)
print("\nExpected return: {0:.3f}".format(exp_ret_1[0][0]))

vol_1 = np.dot(np.dot(w_1.T, Sigma), w_1) ** 0.5
print("\nVolatility: {0:.3f}\n".format(vol_1[0][0]))

What I would like to do is to put the Tracking Error you defined in an other issue as a constraint in my solver so that my optimal weights are impacted by the TE constraint.

robertmartin8 commented 2 years ago

Does this answer your question?

(PS: if you fence your code with triple backticks it renders nicely!)

gmulaku commented 2 years ago

I am sorry but I still don't understand how to optimize my portfolio using the tracking error... I was thinking that if I write it as an objective, maybe I will be able to put it in this code instead of "objective_1"

result = optimize.minimize(objective_1, x0, bounds = bounds, method='SLSQP', tol=1e-8, constraints=cons_1)
w_1 = result.x.reshape((numberOfAssets, 1))
robertmartin8 commented 2 years ago

It looks like your script is built in scipy rather than PyPortfolioOpt. Could you please have a look at the User Guide, which should help you re-write the problem in PyPortfolioOpt. After which it should be simple enough to incorporate the tracking error constraint linked previously.

aloariza commented 2 years ago

Hi @robertmartin8 , I had exactly the same question as @gmulaku.

Unfortunately, the link you shared on the FAQs doesn't help me completely, because I don't know how I should add the weights, of the benchmark.

Maybe this is more of a financial question, but I don't understand why should I specify weights.

If I send equal weights to add_constraint() will that help?

Could you provide an example?

Thanks so much in advance!

robertmartin8 commented 2 years ago

Hi @aloariza,

As mentioned in the docs, you can pass a numpy array of the benchmark weights.

This is only relevant if you are trying to optimise a portfolio with a tracking error objective/constraint.

Best, Robert