I think what may get you closer to what you want is this
if context.account.leverage < 0.9:
holds = ((universe & current_positions)-exits) | longs
if context.account.leverage > 0.9:
holds = ((universe & current_positions)-exits)
If the leverage is greater than .9, that will keep the current positions but exit anything you want to exit or isn't in the current universe. It won't add any new positions.
While this will function as you stated, it may not get you what you want. What is the motivation for 'freezing' the addition of new stocks? Is it to let the current holdings alone until they are explicitly closed? If so, maybe try simply adding a Frozen
constraint. Something like this
freeze_current_holdings = opt.Frozen(current_positions)
leverage_of_1 = opt.MaxGrossExposure(1)
order_optimal_portfolio(
objective=opt.TargetWeights(target_weights),
constraints=[freeze_current_holdings, leverage_of_1],
)
While this is maybe closer to what you want it's not ideal. The issue you will have in both these cases is position sizing. As an example, if the algo starts off with 10 'longs' then each will be equally weighted with a position of .1. Now, one of those positions closes and there happens to be 10 more 'longs'. Because the existing positions are frozen, there is .9 of the portfolio in current holdings. The algo will allocate the remaining .1 to those 10 stocks. Each of those new positions would therefore get .01 weight.
You may consider a common approach and target a fixed number of holdings. As an example, assume you choose to always have a target of 10 holdings. Using the above example, after one of those 10 closes, only fill it with 1 long and not all the longs. Often there is some factor which is proportional to expected returns or the probability of success. Take the stock with this largest factor value to fill the 'hole' .
Just some ideas. Good luck.