Universes
Future Options
Introduction
A Future Option universe lets you select a basket of Option contracts on the contracts in a Futures universe.
Create Universes
To add a universe of Future Option contracts, in the Initialize
method, define a Future universe and then pass the canonical Symbol
to the AddFutureOption
method.
var future = AddFuture(Futures.Metals.Gold); future.SetFilter(0, 90); AddFutureOption(future.Symbol);
future = self.AddFuture(Futures.Metals.Gold) future.SetFilter(0, 90) self.AddFutureOption(future.Symbol)
The following table describes the AddFutureOption
method arguments:
Argument | Data Type | Description | Default Value |
---|---|---|---|
symbol | Symbol | The continuous Future contract Symbol. To view the supported assets in the US Future Options dataset, see Supported Assets. | |
optionFilter | Func<OptionFilterUniverse, OptionFilterUniverse> Callable[[OptionFilterUniverse], OptionFilterUniverse] | A function that selects Future Option contracts | null None |
To override the default pricing model of the Option, set a pricing model in a security initializer.
// In Initialize var seeder = SecuritySeeder.Null; SetSecurityInitializer(new MySecurityInitializer(BrokerageModel, seeder, this)); // Outside of the algorithm class class MySecurityInitializer : BrokerageModelSecurityInitializer { private QCAlgorithm _algorithm; public MySecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder, QCAlgorithm algorithm) : base(brokerageModel, securitySeeder) { _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, set the price model if (security.Type == SecurityType.FutureOption) // Option type { security.PriceModel = OptionPriceModels.CrankNicolsonFD(); } } }
# In Initialize seeder = SecuritySeeder.Null self.SetSecurityInitializer(MySecurityInitializer(self.BrokerageModel, seeder, self)) # Outside of the algorithm class class MySecurityInitializer(BrokerageModelSecurityInitializer): def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder, algorithm: QCAlgorithm) -> None: super().__init__(brokerage_model, security_seeder) 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) # Next, set the price model if security.Type == SecurityType.FutureOption: # Option type security.PriceModel = OptionPriceModels.CrankNicolsonFD()
To override the initial guess of implied volatility, set and warm up the underlying volatility model.
Filter Contracts
By default, LEAN subscribes to the Option contracts that have the following characteristics:
- Standard type (weeklies and non-standard contracts are not available)
- Within 1 strike price of the underlying asset price
- Expire within 31 days
To adjust the universe of contracts, set a filter. The filter usually runs at every time step in your algorithm. When the filter selects a contract that isn't currently in your universe, LEAN adds the new contract data to the next Slice
that it passes to the OnData
method.
To set a contract filter, in the Initialize
method, pass a filter function to the AddFutureOption
method. The following table describes the available filter techniques:
AddFutureOption(future.Symbol, optionFilterUniverse => optionFilterUniverse.Strikes(-1, 1));
self.AddFutureOption(future.Symbol, lambda option_filter_universe: option_filter_universe.Strikes(-1, 1))
The following table describes the filter methods of the OptionFilterUniverse
class:
Method | Description |
---|---|
Strikes(int minStrike, int maxStrike) Strikes(minStrike: int, maxStrike: int) | Selects contracts that are within minStrike strikes below the underlying price and maxStrike strikes above the underlying price |
CallsOnly() | Selects call contracts |
PutsOnly() | Selects put contracts |
StandardsOnly() | Selects standard contracts |
IncludeWeeklys() | Selects non-standard weeklys contracts |
WeeklysOnly() | Selects weekly contracts |
FrontMonth() | Selects the front month contract |
BackMonths() | Selects the non-front month contracts |
BackMonth() | Selects the back month contracts |
Expiration(TimeSpan minExpiry, TimeSpan maxExpiry) Expiration(minExpiry: timedelta, maxExpiry: timedelta) | Selects contracts that expire within a range of dates relative to the current day |
Expiration(int minExpiryDays, int maxExpiryDays) Expiration(minExpiryDays: int, maxExpiryDays: 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 |
OnlyApplyFilterAtMarketOpen() | Instructs the engine to only filter contracts on the first time step of each market day |
The preceding methods return an OptionFilterUniverse
, so you can chain the methods together.
AddFutureOption(future.Symbol, optionFilterUniverse => optionFilterUniverse.Strikes(-1, 1).CallsOnly());
self.AddFutureOption(future.Symbol, lambda option_filter_universe: option_filter_universe.Strikes(-1, 1).CallsOnly())
To perform thorough filtering on the OptionFilterUniverse
, define an isolated filter method.
# In Initialize AddFutureOption(future.Symbol, Selector); private OptionFilterUniverse Selector(OptionFilterUniverse optionFilterUniverse) { var symbols = optionFilterUniverse.PutsOnly(); var strike = symbols.Select(symbol => symbol.ID.StrikePrice).Min(); symbols = symbols.Where(symbol => symbol.ID.StrikePrice == strike); return optionFilterUniverse.Contracts(symbols); }
# In Initialize self.AddFutureOption(future.Symbol, self.contract_selector) def contract_selector(self, option_filter_universe: OptionFilterUniverse) -> OptionFilterUniverse: symbols = option_filter_universe.PutsOnly() strike = min([symbol.ID.StrikePrice for symbol in symbols]) symbols = [symbol for symbol in symbols if symbol.ID.StrikePrice == strike] return option_filter_universe.Contracts(symbols)
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
.
Navigate Option Chains
OptionChain
objects represent an entire chain of Option contracts for a single underlying security. They have the following properties:
To get the OptionChain
, loop through the OptionChains
property. After you get the OptionChain
, you can sort and filter the Option contracts in the chain.
public override void OnData(Slice slice) { foreach (var kvp in slice.OptionChains) { var optionChain = kvp.Value; // Example: Find 5 put contracts that are closest to at-the-money (ATM) and have the farthest expiration var contracts = optionChain .Where(x => x.Right == OptionRight.Put) .OrderByDescending(x => x.Expiry) .ThenBy(x => Math.Abs(chain.Underlying.Price - x.Strike)) .Take(5); // Select the contract with the delta closest to -0.5 var contract = contracts.OrderBy(x => Math.Abs(-0.5m - x.Greeks.Delta)).FirstOrDefault(); } } public void OnData(OptionChains optionChains) { foreach (var kvp in optionChains) { var optionChain = kvp.Value; } }
def OnData(self, slice: Slice) -> None: for _, option_chain in slice.OptionChains.items(): # Example: Find 5 put contracts that are closest to at-the-money (ATM) and have the farthest expiration contracts = [x for x in option_chain if x.Right == OptionRight.Put] contracts = sorted(sorted(contracts, \ key = lambda x: abs(option_chain.Underlying.Price - x.Strike)), \ key = lambda x: x.Expiry, reverse=True)[:5] # Select the contract with the delta closest to -0.5 contract = sorted(contracts, key=lambda x: abs(-0.5 - x.Greeks.Delta))[0]
You can also iterate through the FuturesChains
first.
public override void OnData(Slice slice) { foreach (var kvp in slice.FuturesChains) { var continuousContractSymbol = kvp.Key; var futuresChain = kvp.Value; // Select a Future Contract and create its canonical FOP Symbol var futuresContract = futuresChain.First(); var canonicalFOPSymbol = QuantConnect.Symbol.CreateCanonicalOption(futuresContract.Symbol); if (slice.OptionChains.TryGetValue(canonicalFOPSymbol, out var fopChain)) { foreach (var contract in fopChain) { // } } } } public void OnData(FuturesChains futuresChains) { foreach (var kvp in futuresChains) { var continuousContractSymbol = kvp.Key; var futuresChain = kvp.Value; } }
def OnData(self, slice: Slice) -> None: for continuous_future_symbol, futures_chain in slice.FuturesChains.items(): # Select a Future Contract and create its canonical FOP Symbol futures_contract = [contract for contract in futures_chain][0] canonical_fop_symbol = Symbol.CreateCanonicalOption(futures_contract.Symbol) fop_chain = slice.OptionChains.get(canonical_fop_symbol) if fop_chain: for contract in fop_chain: pass
Live Trading Considerations
By default, LEAN adds contracts to the OptionChain
that pass the filter criteria at every time step in your algorithm. If a contract has been in the universe for a duration that matches the MinimumTimeInUniverse
setting and it no longer passes the filter criteria, LEAN removes it from the chain.
This default behavior can lead to a large number of open data subscriptions, which can be inconvenient in live trading since some data feed providers impose data subscription limits. To reduce the number of data subscriptions, change the value of the MinimumTimeInUniverse
universe setting.
UniverseSettings.MinimumTimeInUniverse = TimeSpan.FromMinutes(15); UniverseSettings.MinimumTimeInUniverse = TimeSpan.Zero;
self.UniverseSettings.MinimumTimeInUniverse = timedelta(minutes=15) self.UniverseSettings.MinimumTimeInUniverse = timedelta(0)
If you set the value to TimeSpan.Zero
timedelta(0)
, LEAN removes all of the contract subscriptions at every time step and your data provider stops sending the data. If your filter re-selects some of the same contracts when it runs next, LEAN requests new data subscriptions from your data provider. To avoid unsubscribing and resubscribing to the same contracts at every time step, use a timedelta
TimeSpan
greater than 0.