{"record":{"id":"9dab2bb1bb213bf6","repo":"QuantConnect/Lean","slug":"expected-4-delisting-events-received-found-self","errorCode":null,"errorMessage":"Expected 4 delisting events received, found: {self.delistings_received}","messagePattern":"Expected 4 delisting events received, found: (.+?)","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"Algorithm.Python/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.py","lineNumber":99,"sourceCode":"        # * Future Liquidation\n        # * Future Option Exercise\n        # * We expect NO Underlying Future Liquidation because we already hold a Long future position so the FOP Put selling leaves us breakeven\n        self.liquidated += 1\n        if order_event.symbol.security_type == SecurityType.FUTURE_OPTION and self.expected_liquidation_time != self.time:\n            raise AssertionError(f\"Expected to liquidate option {order_event.symbol} at {self.expected_liquidation_time}, instead liquidated at {self.time}\")\n\n        if order_event.symbol.security_type == SecurityType.FUTURE and \\\n            (self.expected_liquidation_time - timedelta(minutes=1)) != self.time and \\\n            self.expected_liquidation_time != self.time:\n\n            raise AssertionError(f\"Expected to liquidate future {order_event.symbol} at {self.expected_liquidation_time} (+1 minute), instead liquidated at {self.time}\")\n\n    def on_end_of_algorithm(self):\n        if not self.invested:\n            raise AssertionError(\"Never invested in ES futures and FOPs\")\n\n        if self.delistings_received != 4:\n            raise AssertionError(f\"Expected 4 delisting events received, found: {self.delistings_received}\")\n\n        if self.liquidated != 2:\n            raise AssertionError(f\"Expected 3 liquidation events, found {self.liquidated}\")\n","sourceCodeStart":81,"sourceCodeEnd":103,"githubUrl":"https://github.com/QuantConnect/Lean/blob/d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892/Algorithm.Python/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.py#L81-L103","documentation":"Asserts exactly 4 delisting events were received (warning + delisted for the future, warning + delisted for the FOP). delistings_received is incremented per delisting object in data.delistings; any other count aborts.","triggerScenarios":"Engine emitting duplicate or missing delisting events; only one of the two contracts getting delisted; data refreshes removing/adding delisting notices.","commonSituations":"Engine changes to delisting emission count; data issues suppressing a delisting event; symbol-mapping producing extra canonical delistings.","solutions":["Log every delisting received (symbol + type + time) to see which are missing/extra.","Verify both the future and FOP data files contain delisting metadata.","Update the expected count if the engine's delisting-event semantics intentionally changed.","Filter out canonical-symbol delistings if they are now double-counted."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Track delisting events with detail\nseen = []\nfor d in data.delistings.values():\n    seen.append((d.symbol, d.type, d.time))\nif len(seen) != 4:\n    self.debug(f\"Delisting count {len(seen)} != 4: {seen}\")","typeGuard":"def delisting_count_is(data, expected: int) -> bool:\n    return len(list(data.delistings.values())) == expected","tryCatchPattern":null,"preventionTips":["Log every delisting (symbol + type + time) when debugging.","Verify both contracts' data files contain delisting metadata.","Filter canonical duplicates if double-counting appears.","Re-baseline the count after engine delisting-emission changes."],"tags":["quantconnect","futures","future-options","delisting","event-count","regression-test"],"backgroundTag":null,"analyzedSha":"d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892","analyzedAt":"2026-08-13T13:52:21.013Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}