QuantConnect/Lean · error · AssertionError

Expected {self.expected_orders_count()} orders to have been

Error message

Expected {self.expected_orders_count()} orders to have been submitted and filled, half for buying the strategy and the other half for the liquidation. Actual {orders_count}

What it means

This assertion compares the number of FILLED orders against expected_orders_count(), which a subclass defines as twice the number of strategy legs (one set to open, one set to liquidate). A mismatch means some legs were not filled, were filled twice, or the subclass's expected count constant is wrong relative to the strategy it trades. It runs in on_end_of_algorithm after the no-holdings check.

Source

Thrown at Algorithm.Python/OptionStrategyFactoryMethodsBaseAlgorithm.py:62

            if not isinstance(buying_power_model, OptionStrategyPositionGroupBuyingPowerModel):
                raise AssertionError("Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel. "
                                f"Actual: {type(position_group.buying_power_model).__name__}")

            self.assert_strategy_position_group(position_group, self._option_symbol)

            # Now we should be able to close the position
            self.liquidate_strategy()

            # We can quit now, no more testing required
            self.quit()

    def on_end_of_algorithm(self):
        if self.portfolio.invested:
            raise AssertionError("Expected no holdings at end of algorithm")

        orders_count = len(list(self.transactions.get_orders(lambda order: order.status == OrderStatus.FILLED)))
        if orders_count != self.expected_orders_count():
            raise AssertionError(f"Expected {self.expected_orders_count()} orders to have been submitted and filled, "
                            f"half for buying the strategy and the other half for the liquidation. Actual {orders_count}")

    def expected_orders_count(self) -> int:
        raise NotImplementedError("ExpectedOrdersCount method is not implemented")

    def trade_strategy(self, chain: OptionChain, option_symbol: Symbol) -> None:
        raise NotImplementedError("TradeStrategy method is not implemented")

    def assert_strategy_position_group(self, position_group: IPositionGroup, option_symbol: Symbol) -> None:
        raise NotImplementedError("AssertStrategyPositionGroup method is not implemented")

    def liquidate_strategy(self) -> None:
        raise NotImplementedError("LiquidateStrategy method is not implemented")

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Override expected_orders_count() in the subclass to return 2 * (number of legs in the traded strategy).
  2. Inspect self.transactions.get_orders(...) statuses to find non-FILLED (canceled/rejected/invalid) orders causing the shortfall.
  3. Confirm the strategy factory method produces the expected number of leg orders on both open and close.
  4. If a partial fill occurred, account for it in the expected count or ensure full fills before counting.

Example fix

# before: subclass uses base NotImplementedError or wrong count
# after: override for a 2-leg strategy
def expected_orders_count(self) -> int:
    return 4  # 2 legs open + 2 legs liquidate
Defensive patterns

Strategy: validation

Validate before calling

# Count filled orders and compare to expected only after all fills settle
filled = list(self.transactions.get_orders(lambda o: o.status == OrderStatus.FILLED))
expected = self.expected_orders_count()
if len(filled) != expected:
    non_filled = [o for o in self.transactions.get_orders() if o.status != OrderStatus.FILLED]
    raise AssertionError(f'filled={len(filled)} expected={expected} non_filled={non_filled}')

Prevention

When it happens

Trigger: len(list(self.transactions.get_orders(lambda o: o.status == OrderStatus.FILLED))) != self.expected_orders_count(). Triggered by a leg order not reaching FILLED (rejected/canceled/partial), an extra order being submitted, or a subclass reporting the wrong expected count for its strategy's leg count.

Common situations: Subclassing for a strategy with a different number of legs but forgetting to override expected_orders_count(); a liquidation order that split into partial fills counted differently; a combo order that the engine expanded into a different number of child orders than expected.

Related errors


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