book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Universes

Futures

Introduction

A Futures universe lets you select a basket of contracts for a single Future. LEAN models Future subscriptions as a universe of Future contracts. A Future universe is similar to an Option universe, except Future contracts don't have a strike price, so the universe filter primarily focuses on the contract expiration date.

Create Universes

To add a universe of Future contracts, in the initialize method, call the add_future method. This method returns an Future object, which contains the continuous contract symbol. The continuous contract symbol is the key to access the contracts in the FutureChain that LEAN passes to the on_data method. The continuous contract mapped property references the current contract in the continuous contract series. When you create the Future subscription, save a reference to the Future object so you can use it later in your algorithm.

Select Language:
class BasicFutureAlgorithm(QCAlgorithm):
    def initialize(self):
        self.universe_settings.asynchronous = True
        self._future = self.add_future(Futures.Currencies.BTC,
            extended_market_hours=True,
            data_mapping_mode=DataMappingMode.LAST_TRADING_DAY,
            data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
            contract_depth_offset=0)
        self._future.set_filter(0,62)

    def on_data(self, data):
        if self.portfolio.invested:
            return
        continuous_trade_bar = data.bars.get(self._future.symbol)
        mapped_trade_bar = data.bars.get(self._future.mapped)
        self.market_order(self._future.mapped, 1)
   
    # Track events when security changes its ticker allowing algorithm to adapt to these changes.
    def on_symbol_changed_events(self, symbol_changed_events):
        for symbol, changed_event in  symbol_changed_events.items():
            old_symbol = changed_event.old_symbol
            new_symbol = changed_event.new_symbol
            quantity = self.portfolio[old_symbol].quantity

            # Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract
            tag = f"Rollover - Symbol changed at {self.time}: {old_symbol} -> {new_symbol}"
            self.liquidate(old_symbol, tag=tag)
            if quantity: self.market_order(new_symbol, quantity, tag=tag)

The following table describes the add_future method arguments:

Argument: ticker

The Future ticker. To view the supported assets in the US Futures dataset, see Supported Assets.

Data Type: str | Default Value: None

Argument: resolution

The resolution of the market data. To view the supported resolutions, see Resolutions. If you don't provide a value, it uses Resolution.MINUTE by default.

Data Type: Resolution/NoneType | Default Value: None

Argument: market

The Futures market. To view the supported markets in the US Futures dataset, see Supported Markets. If you don't provide a value, it uses the default Future market of your brokerage model.

Data Type: str | Default Value: None

Argument: fill_forward

If true, the current slice contains the last available data even if there is no data at the current time.

Data Type: bool | Default Value: True

Argument: leverage

The leverage for this Future.

Data Type: float | Default Value: Security.NULL_LEVERAGE

Argument: extended_market_hours

If true, use data from the pre and post market sessions

Data Type: bool | Default Value: False

Argument: data_mapping_mode

The contract mapping mode to use for the continuous future contract

Data Type: DataMappingMode/NoneType | Default Value: None

Argument: data_normalization_mode

The price scaling mode to use for the continuous future contract

Data Type: DataNormalizationMode/NoneType | Default Value: None

Argument: contract_depth_offset

The continuous future contract desired offset from the current front month. For example, 0 is the front month, 1 is the back month contract.

Data Type: int | Default Value: 0

Continous Contracts

By default, LEAN only subscribes to the continuous Future contract. A continuous Future contract represents a series of separate contracts stitched together to form a continuous price. If you need a lot of historical data to warm up an indicator, apply the indicator to the continuous contract price series. The Future object has a symbol property and a mapped property. The price of the symbol property is the adjusted price of the continuous contract. The price of the mapped property is the raw price of the currently selected contract in the continuous contract series.

Select Language:
# Get the adjusted price of the continuous contract.
adjusted_price = self.securities[self._future.symbol].price 

# Get the raw price of the currently selected contract in the continuous contract series.
raw_price = self.securities[self._future.mapped].price

The continuous Futures contract isn't a tradable security. You must place orders for a specific Futures contract. To access the currently selected contract in the continuous contract series, use the mapped property of the Future object.

Select Language:
# Place a market order for the currently selected contract in the continuous contract series.
self.market_order(self._future.mapped, 1)

To configure how LEAN identifies the current Future contract in the continuous series and how it forms the adjusted price between each contract, provide data_mapping_mode, data_normalization_mode, and contract_depth_offset arguments to the add_future method. The Future object that the add_future method returns contains a mapped property that references the current contract in the continuous contract series. As the contracts roll over, the mapped property references the next contract in the series and you receive a SymbolChangedEvent object in the on_data method. The SymbolChangedEvent references the old contract Symbol and the new contract Symbol. You can use SymbolChangedEvents to roll over contracts.

Select Language:
class BasicFutureAlgorithm(QCAlgorithm):
    # Track when the continuous contract switches from one contract to the next.
    def on_symbol_changed_events(self, symbol_changed_events):
        for symbol, changed_event in  symbol_changed_events.items():
            old_symbol = changed_event.old_symbol
            new_symbol = changed_event.new_symbol
            quantity = self.portfolio[old_symbol].quantity

            # Rolling over: To liquidate the old mapped contract and switch to the new mapped contract.
            tag = f"Rollover - Symbol changed at {self.time}: {old_symbol} -> {new_symbol}"
            self.liquidate(old_symbol, tag=tag)
            if quantity: self.market_order(new_symbol, quantity, tag=tag)

In backtesting, the SymbolChangedEvent occurs at midnight Eastern Time (ET). In live trading, the live data for continuous contract mapping arrives at 6/7 AM ET, so that's when it occurs.

Data Normalization Modes

The data_normalization_mode argument defines how the price series of two contracts are stitched together when the contract rollovers occur. The following DataNormalizatoinMode enumeration members are available for continuous contracts:

We use the entire Futures history to adjust historical prices. This process ensures you get the same adjusted prices, regardless of the backtest end date.

Data Mapping Modes

The data_mapping_mode argument defines when contract rollovers occur. The DataMappingMode enumeration has the following members:

Futures.Indices.VIX (VX) doesn't support continous contract rolling with DataMappingMode.OPEN_INTEREST and DataMappingMode.OPEN_INTEREST_ANNUAL.

Contract Depth Offsets

The contract_depth_offset argument defines which contract to use. 0 is the front month contract, 1 is the following back month contract, and 2 is the second back month contract.

Filter Contracts

By default, LEAN doesn't add any contracts to the FuturesChain it passes to the on_data method.

To set a contract filter, in the initialize method, call the set_filter method of the Future object. The following table describes the available filter techniques:

set_filter(minExpiry: int, maxExpiry: int)

Selects the contracts that expire within the range you set. This filter runs asynchronously by default.

set_filter(universeFunc: Callable[[FutureFilterUniverse], FutureFilterUniverse])

Selects the contracts that a function selects.

Select Language:
# Select the contracts that expire within 182 days.
self._future.set_filter(0, 182)

# Select the front month contract.
self._future.set_filter(lambda future_filter_universe: future_filter_universe.front_month())

The following table describes the filter methods of the FutureFilterUniverse class:

standards_only()

Selects standard contracts

include_weeklys()

Selects non-standard weekly contracts

weeklys_only()

Selects weekly contracts

front_month()

Selects the front month contract

back_months()

Selects the non-front month contracts

back_month()

Selects the back month contracts

expiration(min_expiry: timedelta, max_expiry: timedelta)

Selects contracts that expire within a range of dates relative to the current day

expiration(min_expiry_days: int, max_expiry_days: int)

Selects contracts that expire within a range of dates relative to the current day

contracts(contracts: List[Symbol])

Selects a list of contracts

contracts(contractSelector: Callable[[List[Symbol]], List[Symbol]])

Selects contracts that a selector function selects

The preceding methods return an FutureFilterUniverse, so you can chain the methods together.

Select Language:
# Select the front month standard contracts
self._future.set_filter(lambda future_filter_universe: future_filter_universe.standards_only().front_month())

You can also define an isolated filter method.

Select Language:
# In Initialize
self._future.set_filter(self._contract_selector)
    
def _contract_selector(self, 
    future_filter_universe: Callable[[FutureFilterUniverse], FutureFilterUniverse]) -> FutureFilterUniverse:
    return future_filter_universe.standards_only().front_month()

Navigate Futures Chains

FuturesChain objects represent an entire chain of contracts for a single underlying Future. They have the following properties:

To get the FuturesChain, index the futures_chains property of the Slice with the continuous contract Symbol.

Select Language:
def on_data(self, slice: Slice) -> None:
    chain = slice.futures_chains.get(self._symbol)
    if chain:
        # Example: Select the contract with the greatest open interest
        contract = sorted(chain, key=lambda contract: contract.open_interest, reverse=True)[0]

You can also loop through the futures_chains property to get each FuturesChain.

Select Language:
def on_data(self, slice: Slice) -> None:
    for continuous_contract_symbol, chain in slice.futures_chains.items():
        pass

Selection Frequency

By default, Futures universes run at the first time step of each day to select their contracts.

Examples

The following examples demonstrate some common Futures universes.

Example 1: Contracts Expiring On Quarter End

If you run strategies with a quarterly trading cycle, you can filter Future contracts that expire only on quarter ends to reduce the need of rollovers.

Select Language:

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: