QuantConnect/Lean · error · AssertionError
Expected 3 liquidation events, found {self.liquidated}
Error message
Expected 3 liquidation events, found {self.liquidated} What it means
Asserts self.liquidated == 2 (one future liquidation + one FOP exercise). NOTE: the message text says 'Expected 3 liquidation events' but the code checks '!= 2' — the message is wrong; the intended count is 2. Failing means too few or too many SELL/FILLED order events fired.
Source
Thrown at Algorithm.Python/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.py:102
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
- Log each order_event that increments self.liquidated (symbol + security_type) to see what is extra/missing.
- If an extra underlying-future liquidation appears, investigate why the breakeven assumption broke.
- Trust the code (== 2) over the message text ('3') when reconciling.
- Update the count if engine behaviour intentionally changed, and fix the misleading message.
Example fix
// before
if self.liquidated != 2:
raise AssertionError(f"Expected 3 liquidation events, found {self.liquidated}")
// after - correct the message
if self.liquidated != 2:
raise AssertionError(f"Expected 2 liquidation events, found {self.liquidated}") Defensive patterns
Strategy: validation
Validate before calling
# Track liquidation events with detail
if order_event.direction == OrderDirection.SELL and order_event.status == OrderStatus.FILLED:
self.liquidated += 1
self.debug(f"Liquidation #{self.liquidated}: {order_event.symbol.security_type} {order_event.symbol}") Type guard
def is_liquidation_fill(order_event) -> bool:
return order_event.direction == OrderDirection.SELL and order_event.status == OrderStatus.FILLED Prevention
- Trust the code (== 2) over the message text ('3').
- Log each liquidation's security_type to spot unexpected underlying sales.
- Investigate any extra FUTURE liquidation — the comment says it should not happen.
- Fix the misleading message when touching this assertion.
When it happens
Trigger: Extra liquidation events from an unexpected underlying future sale; missing the FOP exercise event; duplicate fills inflating the counter.
Common situations: Engine changes introducing an additional auto-liquidation (the very case the comment says should NOT happen); data changes causing the FOP not to exercise; order-event duplication after a refactor.
Related errors
- Expected 4 delisting events received, found: {self.delisting
- [{UtcTime}] We hold a delisted securities: {string.Join(",",
- Expected at least two future contracts with option chains, b
- Expiry warning with time {delisting.time} but is expected to
- Delisting warning received at an unexpected date: {self.time
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/1193bb74b56a496b.
Report an issue: GitHub.