Skip to content

Add contract filters to futures chains and universes - #9791

Merged
jhonabreul merged 2 commits into
QuantConnect:masterfrom
jhonabreul:feature-future-chain-filters
Sep 14, 2026
Merged

jhonabreul merged 2 commits into
QuantConnect:masterfrom
jhonabreul:feature-future-chain-filters

Conversation

@jhonabreul

@jhonabreul jhonabreul commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Description

Follows #9779 and #9783: the futures universe selection and FuturesChain now share one set of filters, like the option universe and OptionChain do.

chain = self.futures_chain(self._es)
front = chain.expiring_after(self.time + timedelta(90)).front_month()
quarterlies = chain.contract_months(FutureExpirationCycles.MARCH)
liquid = chain.open_interest(100000, sys.maxsize).where(lambda x: x.volume > 0)

What changes:

  • IContractFilters<TSelf>: the filters every derivative has (expirations, front_month(), back_months(), farthest_expiration(), zero_dte(), type, open interest, volume).
  • IFutureContractFilters adds expiration_cycle(), now taking any IEnumerable<int>, and contract_months(); IOptionContractFilters keeps the strike, right, moneyness, greeks and strategy filters.
  • open_interest() and volume() move from the option base to ContractSecurityFilterUniverse, so futures universes get them. Future option universes still reject open_interest().
  • BaseFutureFilterUniverse<TUniverse, TData> mirrors the option base. FutureFilterUniverse keeps its API; FuturesChainFilterUniverse runs the same filters over a chain's contracts.
  • FuturesChain gets every filter, each returning a new chain. The wrappers shared with OptionChain live once in a BaseChain layer, with hooks to create the universe and the filtered chain.
  • New: contract_months(months) selects by the contract month, the month the contract is named after.
  • For products expiring the month before, like crude oil, contract_months([3]) includes the contract expiring in February.
  • expiration_cycle() is unchanged and keeps matching the expiration month.
  • BasicTemplateFuturesAlgorithm picks the contract with expiring_after(...).front_month() in both languages.
  • The Python template used to pick the farthest contract past 90 days; it now picks the nearest, like C#. Statistics unchanged, one contract qualifies.

Related Issue

N/A

Motivation and Context

Futures algorithms hand-roll the front contract past a roll date, the quarterlies, a contract by month code and the liquid ones. The option side got these as filters; this gives futures the same grammar.

Requires Documentation Change

Yes: the FuturesChain filters, and contract_months(), open_interest() and volume() on the futures universe.

How Has This Been Tested?

  • FuturesChainTests: every chain filter returns the same contracts as the universe filter, alone and chained; the interfaces declare every universe filter; Python access.
  • FutureFilterTests: contract_months() over ES and crude oil, where contract month and expiration month differ; expiration sets and bounds, open interest, volume, zero_dte().
  • OptionChainTests: unchanged expectations over the shared base.
  • FuturesChainFiltersRegressionAlgorithm (C# and Python): ES and GC universes selected with the filters, each filter checked on futures_chain() and the slice chains, one trade.
  • Futures filter, futures chain and option chain suites: 180 passed. Futures chain, futures universe, future option chain and basic futures template regression algorithms: 8 passed.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • Refactor (non-breaking change which improves implementation)
  • Performance (non-breaking change which improves performance. Please add associated performance test and results)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Non-functional change (xml comments/documentation/etc)

Checklist:

  • My code follows the code style of this project.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • My branch follows the naming convention bug-<issue#>-<description> or feature-<issue#>-<description>

Split the contract filters shared by every derivative into IContractFilters,
with IFutureContractFilters and IOptionContractFilters on top, and move the
open interest and volume filters into ContractSecurityFilterUniverse so the
futures universe gets them.

Add BaseFutureFilterUniverse over the universe rows and the chain contracts,
FuturesChainFilterUniverse, and FuturesChain filters that forward to it.
The chain wrappers shared by OptionChain and FuturesChain live once in a
BaseChain layer with the chain type and its filter universe as parameters.

New futures filters: ContractMonth(year, month) and ContractMonths(months),
by the contract month rather than the expiration month. FutureSymbol.IsStandard
and FutureOptionSymbol.IsStandard back the standard and weekly filters.

Tests: FuturesChainTests (parity with the universe, every universe filter
declared on the interface, Python), FutureFilterTests, OptionChainTests, and
the FuturesChainFiltersRegressionAlgorithm in C# and Python. The futures
templates pick the front contract with the new filters.
@jhonabreul
jhonabreul merged commit 02e491c into QuantConnect:master Sep 14, 2026
7 of 8 checks passed
@jhonabreul
jhonabreul deleted the feature-future-chain-filters branch September 14, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants