QuantConnect/Lean · error · AssertionError

Expected no holdings at end of algorithm

Error message

Expected no holdings at end of algorithm

What it means

On algorithm end, Lean's OptionStrategyFactoryMethodsBaseAlgorithm asserts the portfolio is flat (self.portfolio.invested is False) after liquidate_strategy() was called in on_data. A failure means the liquidation orders did not fully close every leg of the option strategy position group, leaving residual holdings. This guards the engine's group-liquidation and fill logic.

Source

Thrown at Algorithm.Python/OptionStrategyFactoryMethodsBaseAlgorithm.py:58

            # Verify that the strategy was traded
            position_group = list(self.portfolio.positions.groups)[0]

            buying_power_model = position_group.buying_power_model
            if not isinstance(buying_power_model, OptionStrategyPositionGroupBuyingPowerModel):
                raise AssertionError("Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel. "
                                f"Actual: {type(position_group.buying_power_model).__name__}")

            self.assert_strategy_position_group(position_group, self._option_symbol)

            # Now we should be able to close the position
            self.liquidate_strategy()

            # We can quit now, no more testing required
            self.quit()

    def on_end_of_algorithm(self):
        if self.portfolio.invested:
            raise AssertionError("Expected no holdings at end of algorithm")

        orders_count = len(list(self.transactions.get_orders(lambda order: order.status == OrderStatus.FILLED)))
        if orders_count != self.expected_orders_count():
            raise AssertionError(f"Expected {self.expected_orders_count()} orders to have been submitted and filled, "
                            f"half for buying the strategy and the other half for the liquidation. Actual {orders_count}")

    def expected_orders_count(self) -> int:
        raise NotImplementedError("ExpectedOrdersCount method is not implemented")

    def trade_strategy(self, chain: OptionChain, option_symbol: Symbol) -> None:
        raise NotImplementedError("TradeStrategy method is not implemented")

    def assert_strategy_position_group(self, position_group: IPositionGroup, option_symbol: Symbol) -> None:
        raise NotImplementedError("AssertStrategyPositionGroup method is not implemented")

    def liquidate_strategy(self) -> None:
        raise NotImplementedError("LiquidateStrategy method is not implemented")

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Do not call self.quit() until after all liquidation fills are confirmed; instead wait for portfolio.invested to be False in on_data before quitting.
  2. Verify the subclass liquidate_strategy() closes every leg of the strategy (mirror the open trades).
  3. Check order tickets from liquidation for rejections or partial fills and resubmit if needed.
  4. Ensure the algorithm end date leaves enough time after liquidation for fills to process.

Example fix

# before
self.liquidate_strategy()
self.quit()  # fills may not have settled
# after
self.liquidate_strategy()
# defer quit until flat, checked in on_data:
if not self.portfolio.invested:
    self.quit()
Defensive patterns

Strategy: validation

Validate before calling

# Confirm flat before ending; defer quit until settled
self.liquidate_strategy()
# in on_data, only quit when truly flat:
if not self.portfolio.invested and self._liquidation_sent:
    self.quit()

Prevention

When it happens

Trigger: on_end_of_algorithm fires while self.portfolio.invested is still True. Happens when liquidate_strategy() did not submit/close all legs, when fill events were pending at quit(), when quit() was called before fills settled, or when a leg order was rejected/partially filled.

Common situations: Calling self.quit() immediately after liquidate_strategy() so the algorithm terminates before fill events settle; a subclass liquidate_strategy that omits a leg; a brokerage/fill model rejecting a closing order; running with a data slice that had no quotes to fill the close.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/de2f0475a5ca5924. Report an issue: GitHub.