robertmartin8 / PyPortfolioOpt

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

Set minimum trade size #411

Closed iamagent001 closed 2 years ago

iamagent001 commented 2 years ago

Hi, I would like to ask how to add a "minimum x% trade size" constraint? The result should be either 0% (not holding it) or at least x% (if optimizer wants to hold it, hold it >= x%) I have tried with weight_bound=((x/100),1), but the result buys every stock at x% which is not what I want to achieve... I have also tried with custom constraints: dummy_ef.add_constraint(lambda w: w * (w - (x/100)) >= 0) but it gave me error 'Problem does not follow DCP rules.".

88d52bdba0366127fffca9dfa93895 commented 2 years ago

Theoretically: That makes the portfolio optimize on a non-continuous space, if you are not lucky then the optimization reach an invalid point and also cannot continue to go, i.e. it fails. You can take a look at keyword "Discontinuous Constraint", for example this paper: https://support.sas.com/resources/papers/proceedings16/1820-2016.pdf

Technically: You still want to do that, just mask that requirement as invalid area so the portfolio optimization will not choose any point in that area to optimize.

P/s: I talked about the previous convex-optimization version of this library, I didnt check a new version, so I cant help you to write the constraint. Another approach is global optimization, e.g. Bayesian Optimization which will check "all" possible and valid weights, therefore you will have a result and meet your requirement.

phschiele commented 2 years ago

@iamagent001 As mentioned by @88d52bdba0366127fffca9dfa93895, you're proposed constraint makes the problem nonconvex. We can still solve it using mixed-integer programming, as outlined here.

Please find below an example where every weight is either 0, or in [0.1, 1.0].

import cvxpy as cp

from tests.utilities_for_tests import setup_efficient_frontier

ef = setup_efficient_frontier()
z = cp.Variable(ef.n_assets, boolean=True)

lb = 0.1
ub = 1.0
ef.add_constraint(lambda w: lb * z <= w)
ef.add_constraint(lambda w: w <= ub * z)

ef.min_volatility()
print(ef.clean_weights().values())