{"record":{"id":"5e416e4051dd6d01","repo":"QuantConnect/Lean","slug":"expected-the-portfolio-to-have-holdings-and-to-hav","errorCode":null,"errorMessage":"Expected the portfolio to have holdings and to have {self.trade_count} trades, but had {self.transactions.orders_count}","messagePattern":"Expected the portfolio to have holdings and to have (.+?) trades, but had (.+?)","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"Algorithm.Python/OrderTicketAssignmentDemoAlgorithm.py","lineNumber":53,"sourceCode":"        self.ticket = self.market_order(self._symbol, 1, asynchronous=True)\n        self.debug(f\"{self.time}: Buy: Price {bar.price}, order_id: {self.ticket.order_id}\")\n        self.trade_count += 1\n\n    def on_order_event(self, order_event: OrderEvent):\n        # We cannot access self.ticket directly because it is assigned asynchronously:\n        # this order event could be triggered before self.ticket is assigned.\n        ticket = order_event.ticket\n        if ticket is None:\n            raise AssertionError(\"Expected order ticket in order event to not be null\")\n        if order_event.status == OrderStatus.SUBMITTED and self.ticket is not None:\n            raise AssertionError(\"Field self.ticket not expected no be assigned on the first order event\")\n\n        self.debug(ticket.to_string())\n\n    def on_end_of_algorithm(self):\n        # Just checking that orders were placed\n        if not self.portfolio.invested or self.trade_count != self.transactions.orders_count:\n            raise AssertionError(f\"Expected the portfolio to have holdings and to have {self.trade_count} trades, but had {self.transactions.orders_count}\")\n","sourceCodeStart":35,"sourceCodeEnd":54,"githubUrl":"https://github.com/QuantConnect/Lean/blob/d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892/Algorithm.Python/OrderTicketAssignmentDemoAlgorithm.py#L35-L54","documentation":"On algorithm end, OrderTicketAssignmentDemoAlgorithm asserts the portfolio holds a position (self.portfolio.invested) and that the number of orders submitted equals the number of trades the consolidator fired (self.trade_count == self.transactions.orders_count). A failure means either no position was ever opened (orders not filled) or the order count diverged (orders duplicated, rejected, or the consolidator fired a different number of times than expected).","triggerScenarios":"on_end_of_algorithm with self.portfolio.invested False, or self.trade_count != self.transactions.orders_count. Caused by asynchronous market orders not filling within the backtest window, the hourly consolidator firing fewer/more times than expected, or orders being rejected/canceled so orders_count differs from trade_count.","commonSituations":"Backtest end date too short for all hourly bars to fire; the equity (SPY) data missing bars so the consolidator under-fires; asynchronous orders pending/unfilled at termination; a duplicate order path inflating orders_count.","solutions":["Extend the backtest date range so every hourly consolidator bar fires and orders fill.","Confirm SPY minute/second data exists for the full period so consolidators trigger.","Check order tickets for rejections/cancellations inflating or deflating orders_count.","Ensure the consolidator registration and trade_count increment stay in sync (one increment per order)."],"exampleFix":"# before: end date too short -> under-filled\nself.set_end_date(2013, 10, 8)\n# after: enough bars for consolidator fills and invested portfolio\nself.set_end_date(2013, 10, 11)","handlingStrategy":"validation","validationCode":"# Verify holdings and order count with diagnostics before asserting\nif not self.portfolio.invested:\n    self.log(f'not invested; orders={self.transactions.orders_count} trades={self.trade_count}')\nif self.trade_count != self.transactions.orders_count:\n    pending = [o for o in self.transactions.get_orders() if o.status not in (OrderStatus.FILLED,)]\n    self.log(f'pending/unfilled orders: {pending}')\nif not self.portfolio.invested or self.trade_count != self.transactions.orders_count:\n    raise AssertionError(...)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the backtest window is long enough for all consolidator bars to fire and fill.","Confirm underlying data exists for the full period.","Check order tickets for rejections/cancellations.","Keep trade_count increments in sync with order submissions."],"tags":["orders","portfolio","consolidator","asynchronous","quantconnect","demo"],"backgroundTag":null,"analyzedSha":"d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892","analyzedAt":"2026-08-13T13:52:21.013Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}