2

Context: I'm the developer of PyPortfolioOpt, a python portfolio optimisation library, and I'm trying to allow users to add constraints to a maximum Sharpe ratio problem.

Currently, users can pass their constraints as a lambda function, e.g to make all weights greater than 1%:

ef = EfficientFrontier(mu, S)  # mu and S are expected return and covariance
ef.add_constraint(lambda w: w >= 0.01)  # example new constraint
ef.min_volatility()  # optimise with constraint

On the backend, I pass a cvxpy variable w = cp.Variable(n) to the constraint lambda function, to create a valid cvxpy constraint, then I pass this to cp.Problem and solve it.

The trouble I am having is that maximising the Sharpe ratio requires you to make a variable substitution. Constraints of the form Ax ~ b (where ~ denotes either equality or inequality) must become Ax ~ k * b where k is a nonnegative optimisation variable.

One thing I tried was to pass w / k into the lambda function. This would then result in a constraint w / k >= 0.01, which I hoped would be equivalent to w >= k * 0.01, but sadly this gives:

DCPError: Problem does not follow DCP rules. Specifically:
The following constraints are not DCP:
0.01 <= var2817 / Promote(var2818, (20,)) , because the following subexpressions are not:
|--  var2817 / Promote(var2818, (20,))

I then thought that I might be able to take the nonlinear constraint constr = (w / k >= 0.01) and multiply it by k to give k * constr = (w >= 0.01 * k), but you can't multiply constraints in cvxpy.

TL;DR: how can I convert the cvxpy constraint object (already instantiated) representing w / k >= 0.01 to a cvxpy constraint object representing w >= k * 0.01?

Or failing that, is there any way I can re-engineer this? I'd like to keep the lambda function API.

1 Answer 1

2

Perhaps there is some API for decomposing an already instantiated constraint so that I can put in a variable?

Constraints are immutable by design. Immutability simplifies much of CVXPY’s logic.

Why not construct a new constraint? You can certainly inspect the left and right hand sides of the constraint. Right now, that can be done by inspecting the args attribute (see https://github.com/cvxgrp/cvxpy/blob/master/cvxpy/constraints/nonpos.py#L97).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.