Lean  $LEAN_TAG$
ContinuousContractUniverse.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Linq;
18 using QuantConnect.Util;
21 using System.Collections.Generic;
23 
25 {
26  /// <summary>
27  /// Continuous contract universe selection that based on the requested mapping mode will select each symbol
28  /// </summary>
30  {
31  private readonly IMapFileProvider _mapFileProvider;
32  private readonly SubscriptionDataConfig _config;
33  private readonly Security _security;
34  private readonly bool _liveMode;
35  private Symbol _currentSymbol;
36  private string _mappedSymbol;
37 
38  /// <summary>
39  /// True if this universe filter can run async in the data stack
40  /// TODO: see IContinuousSecurity.Mapped
41  /// </summary>
42  public override bool Asynchronous => false;
43 
44  /// <summary>
45  /// Creates a new instance
46  /// </summary>
47  public ContinuousContractUniverse(Security security, UniverseSettings universeSettings, bool liveMode, SubscriptionDataConfig universeConfig)
48  : base(universeConfig)
49  {
50  _security = security;
51  _liveMode = liveMode;
52  UniverseSettings = universeSettings;
53  _mapFileProvider = Composer.Instance.GetPart<IMapFileProvider>();
54 
55  _config = new SubscriptionDataConfig(Configuration, dataMappingMode: UniverseSettings.DataMappingMode, symbol: _security.Symbol.Canonical);
56  }
57 
58  /// <summary>
59  /// Performs universe selection based on the symbol mapping
60  /// </summary>
61  /// <param name="utcTime">The current utc time</param>
62  /// <param name="data">Empty data</param>
63  /// <returns>The symbols to use</returns>
64  public override IEnumerable<Symbol> SelectSymbols(DateTime utcTime, BaseDataCollection data)
65  {
66  yield return _security.Symbol.Canonical;
67 
68  var mapFile = _mapFileProvider.ResolveMapFile(_config);
69  var mappedSymbol = mapFile.GetMappedSymbol(utcTime.ConvertFromUtc(_security.Exchange.TimeZone), dataMappingMode: _config.DataMappingMode);
70  if (!string.IsNullOrEmpty(mappedSymbol) && mappedSymbol != _mappedSymbol)
71  {
72  if (_currentSymbol != null)
73  {
74  // let's emit the old and new for the mapping date
75  yield return _currentSymbol;
76  }
77  _mappedSymbol = mappedSymbol;
78 
79  _currentSymbol = _security.Symbol.Canonical
80  .UpdateMappedSymbol(mappedSymbol, Configuration.ContractDepthOffset)
81  .Underlying;
82  }
83 
84  if (_currentSymbol != null)
85  {
86  // TODO: this won't work with async universe selection
87  ((IContinuousSecurity)_security).Mapped = _currentSymbol;
88  yield return _currentSymbol;
89  }
90  }
91 
92  /// <summary>
93  /// Gets the subscription requests to be added for the specified security
94  /// </summary>
95  /// <param name="security">The security to get subscriptions for</param>
96  /// <param name="currentTimeUtc">The current time in utc. This is the frontier time of the algorithm</param>
97  /// <param name="maximumEndTimeUtc">The max end time</param>
98  /// <param name="subscriptionService">Instance which implements <see cref="ISubscriptionDataConfigService"/> interface</param>
99  /// <returns>All subscriptions required by this security</returns>
100  public override IEnumerable<SubscriptionRequest> GetSubscriptionRequests(Security security,
101  DateTime currentTimeUtc,
102  DateTime maximumEndTimeUtc,
103  ISubscriptionDataConfigService subscriptionService)
104  {
105  var configs = AddConfigurations(subscriptionService, UniverseSettings, security.Symbol);
106  return configs.Select(config => new SubscriptionRequest(isUniverseSubscription: false,
107  universe: this,
108  security: security,
109  configuration: new SubscriptionDataConfig(config, isInternalFeed: config.IsInternalFeed || config.TickType == TickType.OpenInterest),
110  startTimeUtc: currentTimeUtc,
111  endTimeUtc: maximumEndTimeUtc));
112  }
113 
114  /// <summary>
115  /// Each tradeable day of the future we trigger a new selection.
116  /// Allows use to select the current contract
117  /// </summary>
118  public IEnumerable<DateTime> GetTriggerTimes(DateTime startTimeUtc, DateTime endTimeUtc, MarketHoursDatabase marketHoursDatabase)
119  {
120  var startTimeLocal = startTimeUtc.ConvertFromUtc(_security.Exchange.TimeZone);
121  var endTimeLocal = endTimeUtc.ConvertFromUtc(_security.Exchange.TimeZone);
122 
123  return Time.EachTradeableDay(_security, startTimeLocal, endTimeLocal, Configuration.ExtendedMarketHours)
124  // in live trading selection happens on start see 'DataQueueFuturesChainUniverseDataCollectionEnumerator'
125  .Where(tradeableDay => _liveMode || tradeableDay >= startTimeLocal)
126  // in live trading we delay selection so that we make sure auxiliary data is ready
127  .Select(time => _liveMode ? time.Add(Time.LiveAuxiliaryDataOffset) : time);
128  }
129 
130  /// <summary>
131  /// Helper method to add and get the required configurations associated with a continuous universe
132  /// </summary>
133  public static List<SubscriptionDataConfig> AddConfigurations(ISubscriptionDataConfigService subscriptionService, UniverseSettings universeSettings, Symbol symbol)
134  {
135  List<SubscriptionDataConfig> configs = new(universeSettings.SubscriptionDataTypes.Count);
136  foreach (var pair in universeSettings.SubscriptionDataTypes)
137  {
138  configs.AddRange(subscriptionService.Add(symbol,
139  universeSettings.Resolution,
140  universeSettings.FillForward,
141  universeSettings.ExtendedMarketHours,
142  dataNormalizationMode: universeSettings.DataNormalizationMode,
143  // we need to provider the data types we want, else since it's canonical it would assume the default ZipEntry type used in universe chain
144  subscriptionDataTypes: new List<Tuple<Type, TickType>> { pair },
145  dataMappingMode: universeSettings.DataMappingMode,
146  contractDepthOffset: (uint)Math.Abs(universeSettings.ContractDepthOffset),
147  // open interest is internal and the underlying mapped contracts of the continuous canonical
148  isInternalFeed: !symbol.IsCanonical() || pair.Item2 == TickType.OpenInterest));
149  }
150  return configs;
151  }
152 
153  /// <summary>
154  /// Creates a continuous universe symbol
155  /// </summary>
156  /// <param name="symbol">The associated symbol</param>
157  /// <returns>A symbol for a continuous universe of the specified symbol</returns>
158  public static Symbol CreateSymbol(Symbol symbol)
159  {
160  var ticker = $"qc-universe-continuous-{symbol.ID.Market.ToLowerInvariant()}-{symbol.SecurityType}-{symbol.ID.Symbol}";
161  return UniverseExtensions.CreateSymbol(symbol.SecurityType, symbol.ID.Market, ticker);
162  }
163  }
164 }