QuantConnect/Lean · error · AssertionError

Expected {expected_chain_count} options in chain on {date},

Error message

Expected {expected_chain_count} options in chain on {date}, but got {actual_chain_count}

What it means

For each date level in the history DataFrame, the regression compares the number of contracts returned by history (actual_chain_count) against the count from option_chain_provider.get_option_contract_list(option, date) (expected_chain_count). A mismatch means the OptionUniverse history provider and the option chain provider disagree on the universe for that date — a consistency bug in Lean's option universe generation/caching.

Source

Thrown at Algorithm.Python/OptionUniverseHistoryRegressionAlgorithm.py:42

        self.set_end_date(2015, 12, 25)

        option = self.add_option("GOOG").symbol

        historical_options_data_df = self.history(option, 3, flatten=True)

        # Level 0 of the multi-index is the date, we expect 3 dates, 3 option chains
        if historical_options_data_df.index.levshape[0] != 3:
            raise AssertionError(f"Expected 3 option chains from history request, but got {historical_options_data_df.index.levshape[1]}")

        for date in historical_options_data_df.index.levels[0]:
            expected_chain = list(self.option_chain_provider.get_option_contract_list(option, date))
            expected_chain_count = len(expected_chain)

            actual_chain = historical_options_data_df.loc[date]
            actual_chain_count = len(actual_chain)

            if expected_chain_count != actual_chain_count:
                raise AssertionError(f"Expected {expected_chain_count} options in chain on {date}, but got {actual_chain_count}")

            for i, symbol in enumerate(actual_chain.index):
                expected_symbol = expected_chain[i]
                if symbol != expected_symbol:
                    raise AssertionError(f"Expected symbol {expected_symbol} at index {i} on {date}, but got {symbol}")

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Diff the OptionUniverse history provider and the option_chain_provider code paths to ensure they enumerate the same contracts.
  2. Re-generate or re-download the option universe data so both providers read identical rows.
  3. Log both lists for the failing date to see which contracts differ.
  4. Verify no implicit filter (e.g. strikes/expiry window) is applied in one path but not the other.

Example fix

# diagnostic: log the divergence before asserting
missing = set(map(str, expected_chain)) - set(map(str, actual_chain.index))
extra = set(map(str, actual_chain.index)) - set(map(str, expected_chain))
self.log(f'date={date} missing={missing} extra={extra}')
if expected_chain_count != actual_chain_count:
    raise AssertionError(...)
Defensive patterns

Strategy: validation

Validate before calling

# Compare counts and log divergence before asserting
expected_chain = list(self.option_chain_provider.get_option_contract_list(option, date))
actual_chain = self.history(option, 3, flatten=True).loc[date]
if len(expected_chain) != len(actual_chain):
    self.log(f'date={date} provider={len(expected_chain)} history={len(actual_chain)}')
    raise AssertionError('chain count mismatch')

Prevention

When it happens

Trigger: len(historical_options_data_df.loc[date]) != len(option_chain_provider.get_option_contract_list(option, date)) for some date in the history window. Triggered by a change in how the history provider vs the chain provider enumerate/filter the option universe, or by data version skew between the two code paths.

Common situations: Refactoring option universe generation so history and chain provider use different filtering; a caching stale-data issue; a data package where the two providers read different files for the same date.

Related errors


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