/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.UniverseSelection; using QuantConnect.Interfaces; using QuantConnect.Securities; using QuantConnect.Securities.Option; namespace QuantConnect.Algorithm.Framework.Selection { /// /// Provides an implementation of that subscribes to option chains /// public class OptionUniverseSelectionModel : UniverseSelectionModel { private DateTime _nextRefreshTimeUtc; private readonly TimeSpan _refreshInterval; private readonly UniverseSettings _universeSettings; private readonly Func> _optionChainSymbolSelector; /// /// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes. /// public override DateTime GetNextRefreshTimeUtc() => _nextRefreshTimeUtc; /// /// Creates a new instance of /// /// Time interval between universe refreshes /// Selects symbols from the provided option chain public OptionUniverseSelectionModel(TimeSpan refreshInterval, Func> optionChainSymbolSelector) : this(refreshInterval, optionChainSymbolSelector, null) { } /// /// Creates a new instance of /// /// Time interval between universe refreshes /// Selects symbols from the provided option chain /// Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed /// Performs extra initialization (such as setting models) after we create a new security object [Obsolete("This constructor is obsolete because SecurityInitializer is obsolete and will not be used.")] public OptionUniverseSelectionModel( TimeSpan refreshInterval, Func> optionChainSymbolSelector, UniverseSettings universeSettings, ISecurityInitializer securityInitializer ) :this (refreshInterval,optionChainSymbolSelector,universeSettings) { } /// /// Creates a new instance of /// /// Time interval between universe refreshes /// Selects symbols from the provided option chain /// Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed public OptionUniverseSelectionModel( TimeSpan refreshInterval, Func> optionChainSymbolSelector, UniverseSettings universeSettings ) { _nextRefreshTimeUtc = DateTime.MinValue; _refreshInterval = refreshInterval; _universeSettings = universeSettings; _optionChainSymbolSelector = optionChainSymbolSelector; } /// /// Creates the universes for this algorithm. Called once after /// /// The algorithm instance to create universes for /// The universes to be used by the algorithm public override IEnumerable CreateUniverses(QCAlgorithm algorithm) { _nextRefreshTimeUtc = algorithm.UtcTime + _refreshInterval; var uniqueUnderlyingSymbols = new HashSet(); foreach (var optionSymbol in _optionChainSymbolSelector(algorithm.UtcTime)) { if (optionSymbol.SecurityType != SecurityType.Option) { throw new ArgumentException("optionChainSymbolSelector must return option symbols."); } // prevent creating duplicate option chains -- one per underlying if (uniqueUnderlyingSymbols.Add(optionSymbol.Underlying)) { yield return CreateOptionChain(algorithm, optionSymbol); } } } /// /// Creates the canonical chain security for a given symbol /// /// The algorithm instance to create universes for /// Symbol of the option /// Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed /// Performs extra initialization (such as setting models) after we create a new security object /// for the given symbol [Obsolete("This method is obsolete because SecurityInitializer is obsolete and will not be used.")] protected virtual Option CreateOptionChainSecurity(QCAlgorithm algorithm, Symbol symbol, UniverseSettings settings, ISecurityInitializer initializer) { return CreateOptionChainSecurity( algorithm.SubscriptionManager.SubscriptionDataConfigService, symbol, settings, algorithm.Securities); } /// /// Creates the canonical chain security for a given symbol /// /// The service used to create new /// Symbol of the option /// Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed /// Used to create new /// for the given symbol protected virtual Option CreateOptionChainSecurity( ISubscriptionDataConfigService subscriptionDataConfigService, Symbol symbol, UniverseSettings settings, SecurityManager securityManager) { var config = subscriptionDataConfigService.Add( typeof(ZipEntryName), symbol, settings.Resolution, settings.FillForward, settings.ExtendedMarketHours, false); return (Option)securityManager.CreateSecurity(symbol, config, settings.Leverage, false); } /// /// Defines the option chain universe filter /// protected virtual OptionFilterUniverse Filter(OptionFilterUniverse filter) { // NOP return filter; } /// /// Creates a for a given symbol /// /// The algorithm instance to create universes for /// Symbol of the option /// for the given symbol private OptionChainUniverse CreateOptionChain(QCAlgorithm algorithm, Symbol symbol) { if (symbol.SecurityType != SecurityType.Option) { throw new ArgumentException("CreateOptionChain requires an option symbol."); } // rewrite non-canonical symbols to be canonical var market = symbol.ID.Market; var underlying = symbol.Underlying; if (!symbol.IsCanonical()) { var alias = $"?{underlying.Value}"; symbol = Symbol.Create(underlying.Value, SecurityType.Option, market, alias); } // resolve defaults if not specified var settings = _universeSettings ?? algorithm.UniverseSettings; // create canonical security object, but don't duplicate if it already exists Security security; Option optionChain; if (!algorithm.Securities.TryGetValue(symbol, out security)) { optionChain = CreateOptionChainSecurity( algorithm.SubscriptionManager.SubscriptionDataConfigService, symbol, settings, algorithm.Securities); } else { optionChain = (Option)security; } // set the option chain contract filter function optionChain.SetFilter(Filter); // force option chain security to not be directly tradable AFTER it's configured to ensure it's not overwritten optionChain.IsTradable = false; return new OptionChainUniverse(optionChain, settings, algorithm.LiveMode); } } }