QuantConnect/Lean · error · AssertionError

Expected filtered universe to have less contracts than origi

Error message

Expected filtered universe to have less contracts than original universe. Filtered contracts count ({filtered_contracts}) is equal to total contracts count ({total_contracts})

What it means

In OptionUniverseFilterGreeksRegressionAlgorithm, the option universe filter chains .delta().gamma().vega().theta().rho().implied_volatility().open_interest() on the OptionFilterUniverse. The assertion in the filter callback checks that the filtered universe actually shrank relative to the original — i.e. at least one contract was removed by the Greek/IV/OI predicates. Equality means the filter ranges were so wide that nothing was excluded, which would make the regression useless at proving the filter works.

Source

Thrown at Algorithm.Python/OptionUniverseFilterGreeksRegressionAlgorithm.py:56

        self._max_theta = -182.5
        self._min_rho = 0.5
        self._max_rho = 3.0
        self._min_iv = 1.0
        self._max_iv = 3.0
        self._min_open_interest = 100
        self._max_open_interest = 500

        option.set_filter(self.main_filter)
        self.option_chain_received = False

    def main_filter(self, universe: OptionFilterUniverse) -> OptionFilterUniverse:
        total_contracts = len(list(universe))

        filtered_universe = self.option_filter(universe)
        filtered_contracts = len(list(filtered_universe))

        if filtered_contracts == total_contracts:
            raise AssertionError(f"Expected filtered universe to have less contracts than original universe. "
                                 f"Filtered contracts count ({filtered_contracts}) is equal to total contracts count ({total_contracts})")

        return filtered_universe

    def option_filter(self, universe: OptionFilterUniverse) -> OptionFilterUniverse:
        # Contracts can be filtered by greeks, implied volatility, open interest:
        return universe \
            .delta(self._min_delta, self._max_delta) \
            .gamma(self._min_gamma, self._max_gamma) \
            .vega(self._min_vega, self._max_vega) \
            .theta(self._min_theta, self._max_theta) \
            .rho(self._min_rho, self._max_rho) \
            .implied_volatility(self._min_iv, self._max_iv) \
            .open_interest(self._min_open_interest, self._max_open_interest)

        # Note: there are also shortcuts for these filter methods:
        '''
        return universe \

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Tighten at least one filter range (delta, gamma, vega, theta, rho, IV, or open interest) so some contracts are excluded.
  2. If testing the engine, verify the OptionFilterUniverse filter methods actually remove non-matching contracts (diff the filter implementation).
  3. Print total vs filtered counts during development to tune ranges against the regression data.
  4. Ensure the regression data for GOOG on the test date contains contracts spanning the filter boundaries.

Example fix

# before: ranges too wide, nothing filtered
self._min_delta, self._max_delta = -10.0, 10.0
# after: a range that excludes some contracts
self._min_delta, self._max_delta = 0.5, 1.5
Defensive patterns

Strategy: validation

Validate before calling

# Validate the filter actually prunes before returning it
def main_filter(self, universe):
    total = len(list(universe))
    filtered = self.option_filter(universe)
    # option_filter returns an enumerable; materialize once
    filtered_list = list(filtered)
    if len(filtered_list) >= total and total > 0:
        # ranges too wide; log and proceed (or tighten ranges in config)
        self.log(f'warning: filter did not reduce universe ({total})')
    return filtered_list

Prevention

When it happens

Trigger: set_filter callback computes total_contracts = len(list(universe)), applies the chained Greek/IV/OI filter, then len(list(filtered_universe)) == total_contracts. Triggered when every contract in the universe already satisfies all filter bounds (ranges too permissive), or when the filter methods are no-ops due to a Lean engine regression.

Common situations: Widening the min/max Greek bounds so all contracts pass; a Lean change making OptionFilterUniverse greek/IV/OI filters not actually prune; running on a data package where the universe is tiny and happens to all fit the ranges.

Related errors


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