QuantConnect/Lean · error · AssertionError
Expected no holdings at end of algorithm, but are invested i
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.
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)
Solutions
- Read the printed position IDs to see exactly which symbol is still invested.
- Ensure every opened position has a matching close before algorithm end (the short put should be bought back or expire worthless flat).
- 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
- Close every opened position explicitly; don't rely solely on expiry to flatten.
- Run an end-of-algorithm flatness check during development to surface residual holdings early.
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
- We shouldn't be invested anymore
- No holdings were created for option contract {option_contrac
- Expected no options holdings after closing position
- {Time} - Portfolio should not be invested after the traded c
- Index is not tradable.
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/040c2feaae581804.
Report an issue: GitHub.