QuantConnect/Lean · error · AssertionError

Expected no holdings at end of algorithm, but are invested…

Error message

Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.id) for i in self.portfolio.keys()])}

What it means

FutureOptionShortPutOTMExpiryRegressionAlgorithm.on_end_of_algorithm asserts the portfolio is fully flat at the end: a worthless OTM put that was opened and closed should leave no holdings. If self.portfolio.invested is true, it lists the offending position IDs.

Solutions

  1. Read the printed position IDs to see exactly which symbol is still invested.
  2. Ensure every opened position has a matching close before algorithm end (the short put should be bought back or expire worthless flat).
  3. Investigate assignment/liquidation events (see errors 133/137) that may have created residual holdings.
Defensive patterns

Strategy: validation

Validate before calling

# Before end-of-algorithm, flatten any residual positions
for symbol, holding in self.portfolio.items():
    if holding.quantity != 0:
        self.market_order(symbol, -holding.quantity)
        algorithm.Debug(f'Flattened residual {symbol} qty={holding.quantity}')

Type guard

def portfolio_is_flat(portfolio) -> bool:
    return all(h.quantity == 0 for h in portfolio.Values)

Prevention

When it happens

Trigger: At algorithm termination, Portfolio.Invested is true — some symbol still has a non-zero holdings quantity. The error message enumerates which security IDs remain invested.

Common situations: A position was never closed (open short never bought back), an assignment/exercise created a residual future or option position, or a corporate-action/cash adjustment left holdings.

Related errors


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

Appendix: source

Thrown at Algorithm.Python/FutureOptionShortPutOTMExpiryRegressionAlgorithm.py:104

        else:
            raise AssertionError(f"Received order event for unknown Symbol: {order_event.symbol}")

        self.log(f"{order_event}")

    def assert_future_option_contract_order(self, order_event: OrderEvent, option_contract: Security):
        if order_event.direction == OrderDirection.SELL and option_contract.holdings.quantity != -1:
            raise AssertionError(f"No holdings were created for option contract {option_contract.symbol}")

        if order_event.direction == OrderDirection.BUY and option_contract.holdings.quantity != 0:
            raise AssertionError("Expected no options holdings after closing position")

        if order_event.is_assignment:
            raise AssertionError(f"Assignment was not expected for {order_event.symbol}")

    def on_end_of_algorithm(self):
        if self.portfolio.invested:
            raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.id) for i in self.portfolio.keys()])}")

View on GitHub (pinned to d2c3659f87)