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 Initializeinitialize method, call the AddFutureadd_future method. This method returns an Future object, which contains the continuous contract Symbolsymbol. The continuous contract Symbolsymbol is the key to access the contracts in the FutureChain that LEAN passes to the OnDataon_data method. The continuous contract Mappedmapped 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.
public class BasicFutureAlgorithm : QCAlgorithm
{
private Future _future;
public override void Initialize()
{
UniverseSettings.Asynchronous = true;
_future = AddFuture(Futures.Currencies.BTC,
extendedMarketHours: true,
dataMappingMode: DataMappingMode.LastTradingDay,
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
contractDepthOffset: 0);
_future.SetFilter(0, 62);
}
public override void OnData(Slice data)
{
if (Portfolio.Invested)
{
return;
}
data.Bars.TryGetValue(_future.Symbol, out var continuousTradeBar);
data.Bars.TryGetValue(_future.Mapped, out var mappedTradeBar);
MarketOrder(_future.Mapped, 1);
}
// Track events when security changes its ticker, allowing the algorithm to adapt to these changes.
public override void OnSymbolChangedEvents(SymbolChangedEvents symbolChangedEvents)
{
foreach (var (symbol, changedEvent) in symbolChangedEvents)
{
var oldSymbol = changedEvent.OldSymbol;
var newSymbol = changedEvent.NewSymbol;
var quantity = Portfolio[oldSymbol].Quantity;
// Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract
var tag = $"Rollover - Symbol changed at {Time}: {oldSymbol} -> {newSymbol}";
Liquidate(oldSymbol, tag: tag);
if (quantity != 0) MarketOrder(newSymbol, quantity, tag: tag);
}
}
} 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 AddFutureadd_future method arguments:
Argument: |
Argument: |
Argument: |
Argument: |
Argument: |
Argument: |
Argument: |
Argument: |
Argument: |
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 Symbolsymbol property and a Mappedmapped property. The price of the Symbolsymbol property is the adjusted price of the continuous contract. The price of the Mappedmapped property is the raw price of the currently selected contract in the continuous contract series.
// Get the adjusted price of the continuous contract. var adjustedPrice = Securities[_future.Symbol].Price; // Get the raw price of the currently selected contract in the continuous contract series. var rawPrice = Securities[_future.Mapped].Price;
# 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 Mappedmapped property of the Future object.
// Place a market order for the currently selected contract in the continuous contract series. MarkerOrder(_future.Mapped, 1);
# 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 dataMappingModedata_mapping_mode, dataNormalizationModedata_normalization_mode, and contractDepthOffsetcontract_depth_offset arguments to the AddFutureadd_future method. The Future object that the AddFutureadd_future method returns contains a Mappedmapped property that references the current contract in the continuous contract series. As the contracts roll over, the Mappedmapped property references the next contract in the series and you receive a SymbolChangedEvent object in the OnDataon_data method. The SymbolChangedEvent references the old contract Symbol and the new contract Symbol. You can use SymbolChangedEvents to roll over contracts.
public class BasicFutureAlgorithm : QCAlgorithm
{
// Track when the continuous contract switches from one contract to the next.
public override void OnSymbolChangedEvents(SymbolChangedEvents symbolChangedEvents)
{
foreach (var (symbol, changedEvent) in symbolChangedEvents)
{
var oldSymbol = changedEvent.OldSymbol;
var newSymbol = changedEvent.NewSymbol;
var quantity = Portfolio[oldSymbol].Quantity;
// Rolling over: To liquidate the old mapped contract and switch to the new mapped contract.
var tag = $"Rollover - Symbol changed at {Time}: {oldSymbol} -> {newSymbol}";
Liquidate(oldSymbol, tag: tag);
if (quantity != 0) MarketOrder(newSymbol, quantity, tag: tag);
}
}
} 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 dataNormalizationModedata_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 dataMappingModedata_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.OpenInterestDataMappingMode.OPEN_INTEREST and DataMappingMode.OpenInterestAnnuakDataMappingMode.OPEN_INTEREST_ANNUAL.
Contract Depth Offsets
The contractDepthOffsetcontract_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 OnDataon_data method.
To set a contract filter, in the Initializeinitialize method, call the SetFilterset_filter method of the Future object. The following table describes the available filter techniques:
SetFilter(int minExpiry, int maxExpiry)set_filter(minExpiry: int, maxExpiry: int) Selects the contracts that expire within the range you set. This filter runs asynchronously by default. |
SetFilter(Func<FutureFilterUniverse, FutureFilterUniverse> universeFunc)set_filter(universeFunc: Callable[[FutureFilterUniverse], FutureFilterUniverse]) Selects the contracts that a function selects. |
# 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())
// Select the contracts that expire within 182 days. _future.SetFilter(0, 182); // Select the front month contract. _future.SetFilter(futureFilterUniverse => futureFilterUniverse.FrontMonth());
The following table describes the filter methods of the FutureFilterUniverse class:
StandardsOnly()standards_only() Selects standard contracts |
FrontMonth()front_month() Selects the front month contract |
BackMonths()back_months() Selects the non-front month contracts |
BackMonth()back_month() Selects the back month contracts |
Expiration(TimeSpan minExpiry, TimeSpan maxExpiry)expiration(min_expiry: timedelta, max_expiry: timedelta) Selects contracts that expire within a range of dates relative to the current day |
Expiration(int minExpiryDays, int maxExpiryDays)expiration(min_expiry_days: int, max_expiry_days: int) Selects contracts that expire within a range of dates relative to the current day |
Contracts(IEnumerable<Symbol> contracts)contracts(contracts: list[Symbol]) Selects a list of contracts |
Contracts(Func<IEnumerable<Symbol>, IEnumerable< Symbol>> contractSelector)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 the front month standard contracts _future.SetFilter(futureFilterUniverse => futureFilterUniverse.StandardsOnly().FrontMonth());
# 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.
// In Initialize
_future.SetFilter(Selector);
private FutureFilterUniverse Selector(FutureFilterUniverse futureFilterUniverse)
{
return futureFilterUniverse.StandardsOnly().FrontMonth();
} # 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 FuturesChainsfutures_chains property of the Slice with the continuous contract Symbol.
public override void OnData(Slice slice)
{
if (slice.FuturesChains.TryGetValue(_symbol, out var chain))
{
// Example: Select the contract with the greatest open interest
var contract = chain.OrderBy(x => x.OpenInterest).Last();
}
}
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 FuturesChainsfutures_chains property to get each FuturesChain.
public override void OnData(Slice slice)
{
foreach (var kvp in slice.FuturesChains)
{
var continuousContractSymbol = kvp.Key;
var chain = kvp.Value;
}
}
public void OnData(FuturesChains futuresChains)
{
foreach (var kvp in futuresChains)
{
var continuousContractSymbol = kvp.Key;
var chain = kvp.Value;
}
} def on_data(self, slice: Slice) -> None:
for continuous_contract_symbol, chain in slice.futures_chains.items():
pass
Historical Data
To get historical Future universe data, call the historyHistory<FutureUniverse> method.
This method returns the all the available contracts for each trading day, not the subset of contracts that pass your universe filter.
The return type depends on how you call the method.
public class FutureHistoryAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
var future = AddFuture(Futures.Indices.SP500EMini);
var history = History<FutureUniverse>(future.Symbol, 5);
foreach (var universe in history)
{
var endTime = universe.EndTime;
foreach (FutureUniverse contract in universe)
{
var symbol = contract.Symbol;
var expiry = contract.ID.Date;
var price = contract.Close;
var oi = contract.OpenInterest;
}
}
}
} class FutureHistoryAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
future = self.add_future(Futures.Indices.SP_500_E_MINI)
# DataFrame example:
history_df = self.history(FutureUniverse, future.symbol, 5, flatten=True)
# Series example:
history_series = self.history(FutureUniverse, future.symbol, 5, flatten=False)
# FutureUniverse objects example:
history = self.history[FutureUniverse](future.symbol, 5)
for universe in history:
end_time = universe.end_time
for contract in universe:
symbol = contract.symbol
expiry = contract.id.date
price = contract.close
oi = contract.open_interest
The method represents each contract with a FutureUniverse object, which have the following properties:
Examples
The following examples demonstrate some common Futures universes.
Example 1: Contracts Expiring On Quarter End
To reduce the need for rolling over, you can filter Future contracts that expire only on quarter ends to save transaction costs. The following example trades a simple EMA cross strategy on SP500 EMini Future contracts that expire on quarter ends.
public class QuarterlyFuturesAlgorithm : QCAlgorithm
{
private Future _future;
private ExponentialMovingAverage _ema;
// A variable to control the algorithm trading in daily frequency only, since the indicator signal is by daily.
private int _day = -1;
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
SetCash(10000000);
// Request ES Future data for trading. We only use the continuous contract for indicator values.
_future = AddFuture(
Futures.Indices.SP500EMini,
extendedMarketHours: true,
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
dataMappingMode: DataMappingMode.OpenInterest
);
// We only filter the ones expiring on quarter ends to reduce the transaction cost needed for rolling over contracts.
_future.SetFilter(futureFilterUniverse => futureFilterUniverse.ExpirationCycle(new int[] { 3, 6, 9, 12 }));
// Create an EMA indicator to track trends for trade signal generation.
_ema = EMA(_future.Symbol, 20, Resolution.Daily);
// Warm up the indicator for immediate readiness.
WarmUpIndicator(_future.Symbol, _ema, Resolution.Daily);
}
public override void OnData(Slice slice)
{
// Get the chain to trade from the filtered contracts.
if (_day != slice.Time.Day && slice.FutureChains.TryGetValue(_future.Symbol, out var chain))
{
// Select the contract with the nearest expiry.
var contract = chain.MinBy(x => x.Expiry);
// Trade an EMA cross strategy to follow the trend.
var quantity = Portfolio[_future.Symbol].Quantity;
if (contract.LastPrice > _ema && quantity <= 0)
{
MarketOrder(contract.Symbol, 1-quantity);
}
else if (contract.LastPrice < _ema && quantity >= 0)
{
MarketOrder(contract.Symbol, -1-quantity);
}
_day = slice.Time.Day;
}
}
} class QuarterlyFuturesAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
self.set_cash(10000000)
# Request ES Future data for trading.
self.future = self.add_future(
Futures.Indices.SP_500_E_MINI,
extended_market_hours=True,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode=DataMappingMode.OPEN_INTEREST,
)
# We only filter the ones expiring on quarter ends to reduce the transaction cost needed for rolling over contracts.
self.future.set_filter(lambda future_filter_universe: future_filter_universe.expiration_cycle([3, 6, 9, 12]))
# Create an EMA indicator to track trends for trade signal generation.
self._ema = self.ema(self.future.symbol, 20, Resolution.DAILY)
# Warm up the indicator for immediate readiness.
self.warm_up_indicator(self.future.symbol, self._ema, Resolution.DAILY)
# A variable to control the algorithm trading in daily frequency only, since the indicator signal is by daily.
self._day = -1
def on_data(self, slice: Slice) -> None:
# Get the chain to trade from the filtered contracts.
chain = slice.future_chains.get(self.future.symbol)
if self._day != slice.time.day and chain:
# Select the contract with the nearest expiry.
contract = min(chain, key=lambda x: x.expiry)
# Trade an EMA cross strategy to follow the trend.
quantity = self.portfolio[self.future.symbol].quantity
if contract.last_price > self._ema.current.value and quantity <= 0:
self.market_order(contract.symbol, 1-quantity)
elif contract.last_price < self._ema.current.value and quantity >= 0:
self.market_order(contract.symbol, -1-quantity)
self._day = slice.time.day
Example 2: Contracts Expiring Within One Week
The trading volume of Futures reaches its maximum during the last week of the trading period. The following algorithm selects contracts that expire within a week:
public class WeeklyFuturesAlgorithm : QCAlgorithm
{
private Future _future;
private BollingerBands _bb;
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
// Add ES Futures for trading. The continuous contract data is
// only used for indicator values.
_future = AddFuture(
Futures.Indices.SP500EMini,
extendedMarketHours: true,
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
dataMappingMode: DataMappingMode.OpenInterest
);
// Select the contract expiring within a week since they have
// the highest liquidity.
_future.SetFilter(futureFilterUniverse => futureFilterUniverse.Expiration(0, 7));
// Create a Bollinger Band indicator to identify extreme prices
// for trade signal generation.
_bb = BB(_future.Symbol, 60, 2);
// Warm up the indicator for so we can trade right away.
WarmUpIndicator(_future.Symbol, _bb);
}
public override void OnData(Slice slice)
{
// Get the current Futures chain, if there is one.
if (!slice.FutureChains.TryGetValue(_future.Symbol, out var chain))
{
return;
}
// Select the contract with the nearest expiry.
var contract = chain.MinBy(x => x.Expiry);
// Trade a mean-reversal strategy based on Bollinger Band signal.
var holding = Portfolio[contract.Symbol];
var price = contract.LastPrice;
int targetQuantity;
// Case 1: Short
if (!holding.IsShort && price > _bb.UpperBand)
{
targetQuantity = -1;
}
// Case 2: Long
else if (!holding.IsLong && price < _bb.LowerBand)
{
targetQuantity = 1;
}
// Case 3: Exit
else if ((holding.IsShort && price < _bb.MiddleBand) || (holding.IsLong && price > _bb.MiddleBand))
{
targetQuantity = 0;
}
// Case 4: Hold
else
{
return;
}
MarketOrder(contract.Symbol, targetQuantity - holding.Quantity);
}
} class WeeklyFuturesAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
# Add ES Futures for trading. The continuous contract data is
# only used for indicator values.
self._future = self.add_future(
Futures.Indices.SP_500_E_MINI,
extended_market_hours=True,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
data_mapping_mode=DataMappingMode.OPEN_INTEREST
)
# Select the contract expiring within a week since they have
# the highest liquidity.
self._future.set_filter(lambda universe: universe.expiration(0, 7))
# Create a Bollinger Band indicator to identify extreme prices
# for trade signal generation.
self._bb = self.bb(self._future.symbol, 60, 2)
# Warm up the indicator for so we can trade right away.
self.warm_up_indicator(self._future.symbol, self._bb)
def on_data(self, slice: Slice) -> None:
# Get the current Futures chain, if there is one.
chain = slice.future_chains.get(self._future.symbol)
if not chain:
return
# Select the contract with the nearest expiry.
contract = min(chain, key=lambda x: x.expiry)
# Trade a mean-reversal strategy based on Bollinger Band signal.
holding = self.portfolio[contract.symbol]
price = contract.last_price
# Case 1: Short
if not holding.is_short and price > self._bb.upper_band.current.value:
target_quantity = -1
# Case 2: Long
elif not holding.is_long and price < self._bb.lower_band.current.value:
target_quantity = 1
# Case 3: Exit
elif ((holding.is_short and price < self._bb.middle_band.current.value) or
(holding.is_long and price > self._bb.middle_band.current.value)):
target_quantity = 0
# Case 4: Hold
else:
return
self.market_order(contract.symbol, target_quantity - holding.quantity)
Other Examples
For more examples, see the following algorithms: