QuantConnect/Lean · error · AssertionError
Algorithm should have been invested at the end of the…
Error message
Algorithm should have been invested at the end of the algorithm
What it means
Assertion in PEP8StyleBasicAlgorithm.on_end_of_algorithm that the portfolio holds a position at algorithm end. The algorithm calls self.set_holdings(self.spy, 1) once in on_data when not invested, so if the strategy never took (and kept) a position, this fails. It is a sanity check that the single intended trade actually executed and stuck.
Solutions
- Confirm the security has data for the backtest window: check self.securities[self.spy].has_data and that history is non-empty for the start/end dates.
- Verify market hours and that the backtest range spans at least one open session for the symbol's exchange.
- Check the order events (self.transactions.get_orders()) for rejections or invalid-status fills and inspect their messages.
- Ensure set_holdings weight and leverage permit the trade (lower the weight or call self.set_security(self.spy) / adjust BuyingPowerModel).
- Confirm the symbol/market resolves (e.g. add_equity('SPY') maps to the expected market) and that data is present on disk for that range.
Example fix
# before
def on_data(self, slice):
if not self.portfolio.invested:
self.set_holdings(self.spy, 1)
self.debug("Purchased Stock")
# after (guard against no-data and verify the order was accepted)
def on_data(self, slice):
if not self.portfolio.invested and self.securities[self.spy].has_data:
ticket = self.set_holdings(self.spy, 1)
if ticket.status == OrderStatus.INVALID:
self.error(f"Order invalid: {ticket.tag}")
else:
self.debug("Submitted purchase") Defensive patterns
Strategy: validation
Validate before calling
# Before asserting invested, confirm a fill actually occurred
orders = self.transactions.get_orders(lambda o: o.symbol == self.spy)
filled = any(o.status == OrderStatus.FILLED for o in orders)
if not self.portfolio.invested and not filled:
self.debug("No SPY fill occurred; will not assert invested")
else:
assert self.portfolio.invested, "Expected to be invested" Type guard
def security_has_data(algorithm, symbol):
"""True when the security has received data and can be traded."""
sec = algorithm.securities[symbol]
return sec.has_data and sec.is_tradable Prevention
- Check self.securities[symbol].has_data before placing the entry order.
- Inspect the order ticket status after set_holdings; log OrderStatus.INVALID tags.
- Ensure the backtest range spans a trading session for the symbol's exchange.
When it happens
Trigger: self.portfolio.invested is False at on_end_of_algorithm. This happens when on_data's set_holdings call never filled: no minute bars were received for SPY, the order was rejected/invalid, buying power was insufficient, the fill never arrived before backtest end, or the position was opened then fully closed by a later action.
Common situations: Backtest date range has no data for the security (wrong symbol/market, delisted, holiday-only range). Set_holdings with weight 1 fails due to leverage/buying-power limits. The security was added but data feed produced no slices. A fill model or brokerage rejected the order. Market hours meant no fills occurred in the short window.
Related errors
- OrderEvent LimitPrice is Not expected to be 0 for…
- OrderEvent.ORDER_ID and order_event.ticket.order_id do not…
- OrderEvent StopPrice is Not expected to be 0 for…
- OrderEvent Ticket was not set
- Could not unstack df. Columns
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/4d12ec405ec92dc4.
Report an issue: GitHub.
Appendix: source
Thrown at Algorithm.Python/PEP8StyleBasicAlgorithm.py:40
self.spy = self.add_equity("SPY", Resolution.MINUTE, extended_market_hours=False, fill_forward=True).symbol
# Test accessing a constant (QCAlgorithm.MaxTagsCount)
self.debug("MaxTagsCount: " + str(self.MAX_TAGS_COUNT))
def on_data(self, slice):
if not self.portfolio.invested:
self.set_holdings(self.spy, 1)
self.debug("Purchased Stock")
def on_order_event(self, order_event):
self.log(f"{self.time} :: {order_event}")
def on_end_of_algorithm(self):
self.log("Algorithm ended!")
if not self.portfolio.invested:
raise AssertionError("Algorithm should have been invested at the end of the algorithm")
# let's do some logging to do more pep8 style testing
self.log("-----------------------------------------------------------------------------------------")
self.log(f"{self.spy.value} last price: {self.securities[self.spy].price}")
self.log(f"{self.spy.value} holdings: "
f"{self.securities[self.spy].holdings.quantity}@{self.securities[self.spy].holdings.price}="
f"{self.securities[self.spy].holdings.holdings_value}")
self.log("-----------------------------------------------------------------------------------------")
View on GitHub (pinned to d2c3659f87)