{"record":{"id":"4d12ec405ec92dc4","repo":"QuantConnect/Lean","slug":"algorithm-should-have-been-invested-at-the-end-of","errorCode":null,"errorMessage":"Algorithm should have been invested at the end of the algorithm","messagePattern":"Algorithm should have been invested at the end of the algorithm","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"Algorithm.Python/PEP8StyleBasicAlgorithm.py","lineNumber":40,"sourceCode":"\n        self.spy = self.add_equity(\"SPY\", Resolution.MINUTE, extended_market_hours=False, fill_forward=True).symbol\n\n        # Test accessing a constant (QCAlgorithm.MaxTagsCount)\n        self.debug(\"MaxTagsCount: \" + str(self.MAX_TAGS_COUNT))\n\n    def on_data(self, slice):\n        if not self.portfolio.invested:\n            self.set_holdings(self.spy, 1)\n            self.debug(\"Purchased Stock\")\n\n    def on_order_event(self, order_event):\n        self.log(f\"{self.time} :: {order_event}\")\n\n    def on_end_of_algorithm(self):\n        self.log(\"Algorithm ended!\")\n\n        if not self.portfolio.invested:\n            raise AssertionError(\"Algorithm should have been invested at the end of the algorithm\")\n\n        # let's do some logging to do more pep8 style testing\n        self.log(\"-----------------------------------------------------------------------------------------\")\n        self.log(f\"{self.spy.value} last price: {self.securities[self.spy].price}\")\n        self.log(f\"{self.spy.value} holdings: \"\n                 f\"{self.securities[self.spy].holdings.quantity}@{self.securities[self.spy].holdings.price}=\"\n                 f\"{self.securities[self.spy].holdings.holdings_value}\")\n        self.log(\"-----------------------------------------------------------------------------------------\")\n","sourceCodeStart":22,"sourceCodeEnd":49,"githubUrl":"https://github.com/QuantConnect/Lean/blob/d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892/Algorithm.Python/PEP8StyleBasicAlgorithm.py#L22-L49","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ndef on_data(self, slice):\n    if not self.portfolio.invested:\n        self.set_holdings(self.spy, 1)\n        self.debug(\"Purchased Stock\")\n\n# after (guard against no-data and verify the order was accepted)\ndef on_data(self, slice):\n    if not self.portfolio.invested and self.securities[self.spy].has_data:\n        ticket = self.set_holdings(self.spy, 1)\n        if ticket.status == OrderStatus.INVALID:\n            self.error(f\"Order invalid: {ticket.tag}\")\n        else:\n            self.debug(\"Submitted purchase\")","handlingStrategy":"validation","validationCode":"# Before asserting invested, confirm a fill actually occurred\norders = self.transactions.get_orders(lambda o: o.symbol == self.spy)\nfilled = any(o.status == OrderStatus.FILLED for o in orders)\nif not self.portfolio.invested and not filled:\n    self.debug(\"No SPY fill occurred; will not assert invested\")\nelse:\n    assert self.portfolio.invested, \"Expected to be invested\"","typeGuard":"def security_has_data(algorithm, symbol):\n    \"\"\"True when the security has received data and can be traded.\"\"\"\n    sec = algorithm.securities[symbol]\n    return sec.has_data and sec.is_tradable","tryCatchPattern":null,"preventionTips":["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."],"tags":["quantconnect","lean","portfolio","orders","set-holdings","no-data","python"],"backgroundTag":null,"analyzedSha":"d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892","analyzedAt":"2026-08-13T13:52:21.013Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}