QuantConnect/Lean · error · AssertionError

Expected 4 delisting events received, found: {self.delisting

Error message

Expected 4 delisting events received, found: {self.delistings_received}

What it means

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.

Source

Thrown at Algorithm.Python/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.py:99

        # * Future Liquidation
        # * Future Option Exercise
        # * We expect NO Underlying Future Liquidation because we already hold a Long future position so the FOP Put selling leaves us breakeven
        self.liquidated += 1
        if order_event.symbol.security_type == SecurityType.FUTURE_OPTION and self.expected_liquidation_time != self.time:
            raise AssertionError(f"Expected to liquidate option {order_event.symbol} at {self.expected_liquidation_time}, instead liquidated at {self.time}")

        if order_event.symbol.security_type == SecurityType.FUTURE and \
            (self.expected_liquidation_time - timedelta(minutes=1)) != self.time and \
            self.expected_liquidation_time != self.time:

            raise AssertionError(f"Expected to liquidate future {order_event.symbol} at {self.expected_liquidation_time} (+1 minute), instead liquidated at {self.time}")

    def on_end_of_algorithm(self):
        if not self.invested:
            raise AssertionError("Never invested in ES futures and FOPs")

        if self.delistings_received != 4:
            raise AssertionError(f"Expected 4 delisting events received, found: {self.delistings_received}")

        if self.liquidated != 2:
            raise AssertionError(f"Expected 3 liquidation events, found {self.liquidated}")

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Log every delisting received (symbol + type + time) to see which are missing/extra.
  2. Verify both the future and FOP data files contain delisting metadata.
  3. Update the expected count if the engine's delisting-event semantics intentionally changed.
  4. Filter out canonical-symbol delistings if they are now double-counted.
Defensive patterns

Strategy: validation

Validate before calling

# Track delisting events with detail
seen = []
for d in data.delistings.values():
    seen.append((d.symbol, d.type, d.time))
if len(seen) != 4:
    self.debug(f"Delisting count {len(seen)} != 4: {seen}")

Type guard

def delisting_count_is(data, expected: int) -> bool:
    return len(list(data.delistings.values())) == expected

Prevention

When it happens

Trigger: Engine emitting duplicate or missing delisting events; only one of the two contracts getting delisted; data refreshes removing/adding delisting notices.

Common situations: Engine changes to delisting emission count; data issues suppressing a delisting event; symbol-mapping producing extra canonical delistings.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/9dab2bb1bb213bf6. Report an issue: GitHub.