Universe Selection
Options Universes
Options Universe Selection
The OptionUniverseSelectionModel
selects all the available contracts for the Equity Options, Index Options, and Future Options you specify. To use this model, provide a refreshInterval
refresh_interval
and a selector function. The refreshInterval
refresh_interval
defines how frequently LEAN calls the selector function. The selector function receives a DateTime
datetime
object that represents the current Coordinated Universal Time (UTC) and returns a list of Symbol
objects. The Symbol
objects you return from the selector function are the Options of the universe.
// Run universe selection asynchronously to speed up your algorithm. This means you cannot rely on method or algorithm state between filter calls. UniverseSettings.Asynchronous = true; // Select SPY option symbol for the future universe. AddUniverseSelection( new OptionUniverseSelectionModel( // Refresh the universe daily. TimeSpan.FromDays(1), _ => new [] { QuantConnect.Symbol.Create("SPY", SecurityType.Option, Market.USA) } ) );
from Selection.OptionUniverseSelectionModel import OptionUniverseSelectionModel # Run universe selection asynchronously to speed up your algorithm. This means you cannot rely on method or algorithm state between filter calls. self.universe_settings.asynchronous = True # Select SPY option symbol for the future universe. self.set_universe_selection( OptionUniverseSelectionModel( # Refresh the universe daily. timedelta(1), lambda _: [Symbol.create("SPY", SecurityType.OPTION, Market.USA)] ) )
The following table describes the arguments the model accepts:
Argument | Data Type | Description | Default Value |
---|---|---|---|
refreshInterval refresh_interval | TimeSpan timedelta | Time interval between universe refreshes | |
optionChainSymbolSelector option_chain_symbol_selector | Func<DateTime, IEnumerable<Symbol>> Callable[[datetime], List[Symbol]] | A function that selects the Option symbols | |
universeSettings universe_settings | UniverseSettings | The universe settings. If you don't provide an argument, the model uses the algorithm.UniverseSettings algorithm.universe_settings by default. | null None |
The following example shows how to define the Option chain Symbol selector as an isolated method:
// Define option chain symbol selector to subscribe to SP500 E-mini futures options security in the algorithm. public override void Initialize() { AddUniverseSelection( new OptionUniverseSelectionModel(TimeSpan.FromDays(1), SelectOptionChainSymbols) ); } private IEnumerable<Symbol> SelectOptionChainSymbols(DateTime utcTime) { // Equity Options example: //var tickers = new[] {"SPY", "QQQ", "TLT"}; //return tickers.Select(ticker => QuantConnect.Symbol.Create(ticker, SecurityType.Option, Market.USA)); // Index Options example: //var tickers = new[] {"VIX", "SPX"}; //return tickers.Select(ticker => QuantConnect.Symbol.Create(ticker, SecurityType.IndexOption, Market.USA)); // Future Options example: var futureSymbol = QuantConnect.Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME); var futureContractSymbols = FutureChainProvider.GetFutureContractList(futureSymbol, Time); foreach (var symbol in futureContractSymbols) { yield return QuantConnect.Symbol.CreateCanonicalOption(symbol); } }
# Define option chain symbol selector to subscribe to SP500 E-mini futures options security in the algorithm. from Selection.OptionUniverseSelectionModel import OptionUniverseSelectionModel def initialize(self) -> None: self.add_universe_selection( OptionUniverseSelectionModel(timedelta(days=1), self.select_option_chain_symbols) ) def select_option_chain_symbols(self, utc_time: datetime) -> List[Symbol]: # Equity Options example: #tickers = ["SPY", "QQQ", "TLT"] #return [Symbol.create(ticker, SecurityType.OPTION, Market.USA) for ticker in tickers] # Index Options example: #tickers = ["VIX", "SPX"] #return [Symbol.create(ticker, SecurityType.INDEX_OPTION, Market.USA) for ticker in tickers] # Future Options example: future_symbol = Symbol.create(Futures.Indices.SP500E_MINI, SecurityType.FUTURE, Market.CME) future_contract_symbols = self.future_chain_provider.get_future_contract_list(future_symbol, self.time) return [Symbol.create_canonical_option(symbol) for symbol in future_contract_symbols]
This model uses the default Option filter, which selects all of the available Option contracts at the current time step. To use a different filter for the contracts, subclass the OptionUniverseSelectionModel
and define a Filter
filter
method. The Filter
filter
method accepts and returns an OptionFilterUniverse
object to select the Option contracts. The following table describes the methods of the OptionFilterUniverse
class:
The following table describes the filter methods of the OptionFilterUniverse
class:
Strikes(int minStrike, int maxStrike) strikes(min_strike: int, max_strike: int) Selects contracts that are within |
CallsOnly() calls_only() Selects call contracts. |
PutsOnly() puts_only() Selects put contracts. |
StandardsOnly() standards_only() Selects standard contracts. |
IncludeWeeklys() include_weeklys() Selects non-standard weeklys contracts. |
WeeklysOnly() weeklys_only() Selects weekly 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(int minExpiryDays, int maxExpiryDays) expiration(min_expiryDays: int, max_expiryDays: 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(contract_selector: Callable[[List[Symbol]], List[Symbol]]) Selects contracts that a selector function selects. |
The preceding methods return an OptionFilterUniverse
, so you can chain the methods together.
The contract filter runs at the first time step of each day.
To move the Option chain Symbol selector outside of the algorithm class, create a universe selection model that inherits the OptionUniverseSelectionModel
class.
// Setup algorithm settings and request data in initialize. UniverseSettings.Asynchronous = true; AddUniverseSelection(new EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(this)); // Outside of the algorithm class class EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel : OptionUniverseSelectionModel { public EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(QCAlgorithm algorithm) : base(TimeSpan.FromDays(1), utcTime => SelectOptionChainSymbols(algorithm, utcTime)) {} private static IEnumerable<Symbol> SelectOptionChainSymbols(QCAlgorithm algorithm, DateTime utcTime) { // Equity Options example: //var tickers = new[] {"SPY", "QQQ", "TLT"}; //return tickers.Select(ticker => QuantConnect.Symbol.Create(ticker, SecurityType.Option, Market.USA)); // Index Options example: //var tickers = new[] {"VIX", "SPX"}; //return tickers.Select(ticker => QuantConnect.Symbol.Create(ticker, SecurityType.IndexOption, Market.USA)); // Future Options example: var futureSymbol = QuantConnect.Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME); var futureContractSymbols = algorithm.FutureChainProvider.GetFutureContractList(futureSymbol, algorithm.Time); foreach (var symbol in futureContractSymbols) { yield return QuantConnect.Symbol.CreateCanonicalOption(symbol); } } // Create a filter to return contracts that have the strike price within 1 strike level and expire within 7 days. protected override OptionFilterUniverse Filter(OptionFilterUniverse filter) { return filter.Strikes(-1, -1).Expiration(0, 7).CallsOnly(); } }
# Setup algorithm settings and request data in initialize. self.universe_settings.asynchronous = True self.add_universe_settings(EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(self)) # Outside of the algorithm class class EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(OptionUniverseSelectionModel): def __init__(self, algorithm): self.algo = algorithm super().__init__(timedelta(1), self.select_option_chain_symbols) def select_option_chain_symbols(self, utc_time: datetime) -> List[Symbol]: # Equity Options example: #tickers = ["SPY", "QQQ", "TLT"] #return [Symbol.create(ticker, SecurityType.OPTION, Market.USA) for ticker in tickers] # Index Options example: #tickers = ["VIX", "SPX"] #return [Symbol.create(ticker, SecurityType.INDEX_OPTION, Market.USA) for ticker in tickers] # Future Options example: future_symbol = Symbol.create(Futures.Indices.SP500E_MINI, SecurityType.FUTURE, Market.CME) future_contract_symbols = self.algo.future_chain_provider.get_future_contract_list(future_symbol, self.algo.time) return [Symbol.create_canonical_option(symbol) for symbol in future_contract_symbols] # Create a filter to return contracts that have the strike price within 1 strike level and expire within 7 days. def Filter(self, option_filter_universe: OptionFilterUniverse) -> OptionFilterUniverse: return option_filter_universe.strikes(-1, -1).expiration(0, 7).calls_only()
Some of the preceding filter methods only set an internal enumeration in the OptionFilterUniverse
that it uses later on in the filter process. This subset of filter methods don't immediately reduce the number of contract Symbol
objects in the OptionFilterUniverse
.
To override the default pricing model of the Options, set a pricing model in a security initializer.
To override the initial guess of implied volatility, set and warm up the underlying volatility model.
To view the implementation of this model, see the LEAN GitHub repositoryLEAN GitHub repository.
Option Chained Universe Selection
An Option chained universe subscribes to Option contracts on the constituents of a US Equity universe.
// Subscribe price data unadjusted for splits and dividends ("raw") into the algorithm. Required for options and useful for more accurately modeling historical periods. UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw; UniverseSettings.Asynchronous = true; AddUniverseSelection( new OptionChainedUniverseSelectionModel( // Add 10 highest dollar trading volume securities to the universe. AddUniverse(Universe.DollarVolume.Top(10)), // Set contract filter to return only front month CALL options that have the strike price within 2 strike level. optionFilterUniverse => optionFilterUniverse.Strikes(-2, +2).FrontMonth().CallsOnly() ) );
# Subscribe price data unadjusted for splits and dividends ("raw") into the algorithm. Required for options and useful for more accurately modeling historical periods. self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW self.universe_settings.asynchronous = True self.add_universe_selection( OptionChainedUniverseSelectionModel( # Add 10 highest dollar trading volume securities to the universe. self.add_universe(self.universe.dollar_volume.top(10)), # Set contract filter to return only front month CALL options that have the strike price within 2 strike level. lambda option_filter_universe: option_filter_universe.strikes(-2, +2).front_month().calls_only() ) )
The following table describes the arguments the model accepts:
Argument | Data Type | Description | Default Value |
---|---|---|---|
universe | Universe | The universe to chain onto the Option Universe Selection model | |
optionFilter option_filter | Func<OptionFilterUniverse, OptionFilterUniverse> Callable[[OptionFilterUniverse], OptionFilterUniverse] | The Option filter universe to use | |
universeSettings universe_settings | UniverseSettings | The universe settings. If you don't provide an argument, the model uses the algorithm.UniverseSettings algorithm.universe_settings by default. | null None |
The optionFilter
option_filter
function receives and returns an OptionFilterUniverse
to select the Option contracts. The following table describes the methods of the OptionFilterUniverse
class:
The following table describes the filter methods of the OptionFilterUniverse
class:
Strikes(int minStrike, int maxStrike) strikes(min_strike: int, max_strike: int) Selects contracts that are within |
CallsOnly() calls_only() Selects call contracts. |
PutsOnly() puts_only() Selects put contracts. |
StandardsOnly() standards_only() Selects standard contracts. |
IncludeWeeklys() include_weeklys() Selects non-standard weeklys contracts. |
WeeklysOnly() weeklys_only() Selects weekly 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(int minExpiryDays, int maxExpiryDays) expiration(min_expiryDays: int, max_expiryDays: 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(contract_selector: Callable[[List[Symbol]], List[Symbol]]) Selects contracts that a selector function selects. |
The preceding methods return an OptionFilterUniverse
, so you can chain the methods together.
The following example shows how to define the Option filter as an isolated method:
// Configure the algorithm to use raw data for precision and asynchronous processing for speed. Apply OptionChainedUniverseSelectionModel to select the top 10 equities by dollar volume, and filter for front month CALL options with strike prices within 2 levels. public override void Initialize() { // Setup algorithm settings and request data. UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw; UniverseSettings.Asynchronous = true; AddUniverseSelection( new OptionChainedUniverseSelectionModel( AddUniverse(Universe.DollarVolume.Top(10)), OptionFilterFunction ) ); } // Set contract filter to return only front month CALL options that have the strike price within 2 strike level. private OptionFilterUniverse OptionFilterFunction(OptionFilterUniverse optionFilterUniverse) { return optionFilterUniverse.Strikes(-2, +2).FrontMonth().CallsOnly(); }
# Configure the algorithm to use raw data for precision and asynchronous processing for speed. Apply OptionChainedUniverseSelectionModel to select the top 10 equities by dollar volume, and filter for front month CALL options with strike prices within 2 levels. def initialize(self) -> None: # Setup algorithm settings and request data. self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW self.universe_settings.asynchronous = True self.add_universe_selection( OptionChainedUniverseSelectionModel( self.add_universe(self.universe.dollar_volume.top(10)), self.option_filter_function ) ) # Set contract filter to return only front month CALL options that have the strike price within 2 strike level. def option_filter_function(self, option_filter_universe: OptionFilterUniverse) -> OptionFilterUniverse: return option_filter_universe.strikes(-2, +2).front_month().calls_only()
Some of the preceding filter methods only set an internal enumeration in the OptionFilterUniverse
that it uses later on in the filter process. This subset of filter methods don't immediately reduce the number of contract Symbol
objects in the OptionFilterUniverse
.
To view the implementation of this model, see the LEAN GitHub repository.
Example
The following example chains a fundamental universe and an Equity Options universe. It first selects 10 stocks with the lowest PE ratio and then selects their front-month call Option contracts. It buys one front-month call Option contract every day.
To override the default pricing model of the Options, set a pricing model in a security initializer.
To override the initial guess of implied volatility, set and warm up the underlying volatility model.
// Example code to chain a fundamental universe and an Equity Options universe by selecting top 10 stocks with lowest PE, indicating potentially undervalued stocks and then selecting their from-month call Option contracts to target contracts with high liquidity. using QuantConnect.Data; using QuantConnect.Data.Fundamental; using QuantConnect.Data.UniverseSelection; using QuantConnect.Securities; using QuantConnect.Securities.Option; using QuantConnect.Util; using System; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Algorithm.CSharp { public class ETFUniverseOptions : QCAlgorithm { private int _day; public override void Initialize() { SetStartDate(2023, 2, 2); SetCash(100000); UniverseSettings.Asynchronous = true; UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw; SetSecurityInitializer(new CustomSecurityInitializer(this)); var universe = AddUniverse(FundamentalFunction); AddUniverseOptions(universe, OptionFilterFunction); } private IEnumerable<Symbol> FundamentalFunction(IEnumerable<Fundamental> fundamental) { return fundamental .Where(f => !double.IsNaN(f.ValuationRatios.PERatio)) .OrderBy(f => f.ValuationRatios.PERatio) .Take(10) .Select(x => x.Symbol); } private OptionFilterUniverse OptionFilterFunction(OptionFilterUniverse optionFilterUniverse) { return optionFilterUniverse.Strikes(-2, +2).FrontMonth().CallsOnly(); } public override void OnData(Slice data) { if (IsWarmingUp || _day == Time.Day) return; foreach (var (symbol, chain) in data.OptionChains) { if (Portfolio[chain.Underlying.Symbol].Invested) Liquidate(chain.Underlying.Symbol); var spot = chain.Underlying.Price; var contract = chain.OrderBy(x => Math.Abs(spot-x.Strike)).FirstOrDefault(); var tag = $"IV: {contract.ImpliedVolatility:F3} Δ: {contract.Greeks.Delta:F3}"; MarketOrder(contract.Symbol, 1, true, tag); _day = Time.Day; } } } internal class CustomSecurityInitializer : BrokerageModelSecurityInitializer { private QCAlgorithm _algorithm; public CustomSecurityInitializer(QCAlgorithm algorithm) : base(algorithm.BrokerageModel, new FuncSecuritySeeder(algorithm.GetLastKnownPrices)) { _algorithm = algorithm; } public override void Initialize(Security security) { // First, call the superclass definition // This method sets the reality models of each security using the default reality models of the brokerage model base.Initialize(security); // Next, overwrite the price model if (security.Type == SecurityType.Option) // Option type { (security as Option).PriceModel = OptionPriceModels.CrankNicolsonFD(); } // Overwrite the volatility model and warm it up if (security.Type == SecurityType.Equity) { security.VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(30); var tradeBars = _algorithm.History(security.Symbol, 30, Resolution.Daily); foreach (var tradeBar in tradeBars) security.VolatilityModel.Update(security, tradeBar); } } } }
# Example code to chain a fundamental universe and an Equity Options universe by selecting top 10 stocks with lowest PE, indicating potentially undervalued stocks and then selecting their from-month call Option contracts to target contracts with high liquidity. from AlgorithmImports import * class ChainedUniverseAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2023, 2, 2) self.set_cash(100000) self.universe_settings.asynchronous = True self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW self.set_security_initializer(CustomSecurityInitializer(self)) universe = self.add_universe(self.fundamental_function) self.add_universe_options(universe, self.option_filter_function) self.day = 0 def fundamental_function(self, fundamental: List[Fundamental]) -> List[Symbol]: filtered = (f for f in fundamental if not np.isnan(f.valuation_ratios.pe_ratio)) sorted_by_pe_ratio = sorted(filtered, key=lambda f: f.valuation_ratios.pe_ratio) return [f.symbol for f in sorted_by_pe_ratio[:10]] def option_filter_function(self, option_filter_universe: OptionFilterUniverse) -> OptionFilterUniverse: return option_filter_universe.strikes(-2, +2).front_month().calls_only() def on_data(self, data: Slice) -> None: if self.is_warming_up or self.day == self.time.day: return for symbol, chain in data.option_chains.items(): if self.portfolio[chain.underlying.symbol].invested: self.liquidate(chain.underlying.symbol) spot = chain.underlying.price contract = sorted(chain, key=lambda x: abs(spot-x.strike))[0] tag = f"IV: {contract.implied_volatility:.3f} Δ: {contract.greeks.delta:.3f}" self.market_order(contract.symbol, 1, True, tag) self.day = self.time.day class CustomSecurityInitializer(BrokerageModelSecurityInitializer): def __init__(self, algorithm: QCAlgorithm) -> None: super().__init__(algorithm.brokerage_model, FuncSecuritySeeder(algorithm.get_last_known_prices)) self.algorithm = algorithm def initialize(self, security: Security) -> None: # First, call the superclass definition # This method sets the reality models of each security using the default reality models of the brokerage model super().initialize(security) # Overwrite the price model if security.type == SecurityType.OPTION: # Option type security.price_model = OptionPriceModels.crank_nicolson_fd() # Overwrite the volatility model and warm it up if security.type == SecurityType.EQUITY: security.volatility_model = StandardDeviationOfReturnsVolatilityModel(30) trade_bars = self.algorithm.history[TradeBar](security.symbol, 30, Resolution.DAILY) for trade_bar in trade_bars: security.volatility_model.update(security, trade_bar)