Lean  $LEAN_TAG$
Universe.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.Collections;
18 using System.Collections.Concurrent;
19 using System.Collections.Generic;
20 using System.Linq;
23 using QuantConnect.Util;
24 
26 {
27  /// <summary>
28  /// Provides a base class for all universes to derive from.
29  /// </summary>
30  public abstract class Universe : IDisposable
31  {
32  /// <summary>
33  /// Used to round the members time in universe <see cref="CanRemoveMember"/>, this is
34  /// done because we can not guarantee exact selection time in live mode, see GH issue 3287
35  /// </summary>
36  private TimeSpan? _minimumTimeInUniverseRoundingInterval;
37 
38  /// <summary>
39  /// Gets a value indicating that no change to the universe should be made
40  /// </summary>
42 
43  private HashSet<Symbol> _previousSelections;
44 
45  /// <summary>
46  /// Gets the internal security collection used to define membership in this universe
47  /// </summary>
48  public virtual ConcurrentDictionary<Symbol, Member> Securities
49  {
50  get;
51  private set;
52  }
53 
54  /// <summary>
55  /// The currently selected symbol set
56  /// </summary>
57  /// <remarks>This set might be different than <see cref="Securities"/> which might hold members that are no longer selected
58  /// but have not been removed yet, this can be because they have some open position, orders, haven't completed the minimum time in universe</remarks>
59  public HashSet<Symbol> Selected
60  {
61  get; set;
62  }
63 
64  /// <summary>
65  /// True if this universe filter can run async in the data stack
66  /// </summary>
67  public virtual bool Asynchronous
68  {
69  get
70  {
71  if (UniverseSettings.Asynchronous.HasValue)
72  {
73  return UniverseSettings.Asynchronous.Value;
74  }
75  return false;
76  }
77  set
78  {
80  }
81  }
82 
83  /// <summary>
84  /// Event fired when the universe selection has changed
85  /// </summary>
86  public event EventHandler SelectionChanged;
87 
88  /// <summary>
89  /// Gets the security type of this universe
90  /// </summary>
92 
93  /// <summary>
94  /// Gets the market of this universe
95  /// </summary>
96  public string Market => Configuration.Market;
97 
98  /// <summary>
99  /// Gets the symbol of this universe
100  /// </summary>
102 
103  /// <summary>
104  /// Gets the data type of this universe
105  /// </summary>
106  public Type DataType => Configuration.Type;
107 
108  /// <summary>
109  /// Flag indicating if disposal of this universe has been requested
110  /// </summary>
111  public virtual bool DisposeRequested
112  {
113  get;
114  protected set;
115  }
116 
117  /// <summary>
118  /// Gets the settings used for subscriptions added for this universe
119  /// </summary>
120  public virtual UniverseSettings UniverseSettings
121  {
122  get; set;
123  }
124 
125  /// <summary>
126  /// Gets the configuration used to get universe data
127  /// </summary>
129  {
130  get; private set;
131  }
132 
133  /// <summary>
134  /// Gets the current listing of members in this universe. Modifications
135  /// to this dictionary do not change universe membership.
136  /// </summary>
137  public Dictionary<Symbol, Security> Members
138  {
139  get { return Securities.Select(x => x.Value.Security).ToDictionary(x => x.Symbol); }
140  }
141 
142  /// <summary>
143  /// Initializes a new instance of the <see cref="Universe"/> class
144  /// </summary>
145  /// <param name="config">The configuration used to source data for this universe</param>
147  {
148  _previousSelections = new HashSet<Symbol>();
149  Securities = new ConcurrentDictionary<Symbol, Member>();
150 
151  Configuration = config;
152  }
153 
154  /// <summary>
155  /// Determines whether or not the specified security can be removed from
156  /// this universe. This is useful to prevent securities from being taken
157  /// out of a universe before the algorithm has had enough time to make
158  /// decisions on the security
159  /// </summary>
160  /// <param name="utcTime">The current utc time</param>
161  /// <param name="security">The security to check if its ok to remove</param>
162  /// <returns>True if we can remove the security, false otherwise</returns>
163  public virtual bool CanRemoveMember(DateTime utcTime, Security security)
164  {
165  // can always remove securities after dispose requested
166  if (DisposeRequested)
167  {
168  return true;
169  }
170 
171  // can always remove delisted securities from the universe
172  if (security.IsDelisted)
173  {
174  return true;
175  }
176 
177  Member member;
178  if (Securities.TryGetValue(security.Symbol, out member))
179  {
180  if (_minimumTimeInUniverseRoundingInterval == null)
181  {
182  // lets set _minimumTimeInUniverseRoundingInterval once
183  _minimumTimeInUniverseRoundingInterval = UniverseSettings.MinimumTimeInUniverse;
184  AdjustMinimumTimeInUniverseRoundingInterval();
185  }
186 
187  var timeInUniverse = utcTime - member.Added;
188  if (timeInUniverse.Round(_minimumTimeInUniverseRoundingInterval.Value)
190  {
191  return true;
192  }
193  }
194  return false;
195  }
196 
197  /// <summary>
198  /// Performs universe selection using the data specified
199  /// </summary>
200  /// <param name="utcTime">The current utc time</param>
201  /// <param name="data">The symbols to remain in the universe</param>
202  /// <returns>The data that passes the filter</returns>
203  public IEnumerable<Symbol> PerformSelection(DateTime utcTime, BaseDataCollection data)
204  {
205  // select empty set of symbols after dispose requested
206  if (DisposeRequested)
207  {
209  return Enumerable.Empty<Symbol>();
210  }
211 
212  var selections = data.FilteredContracts;
213  if (selections == null)
214  {
215  // only trigger selection if it hasn't already been run
216  var result = SelectSymbols(utcTime, data);
217  if (ReferenceEquals(result, Unchanged))
218  {
219  data.FilteredContracts = _previousSelections;
220  return Unchanged;
221  }
222 
223  selections = result.ToHashSet();
224  data.FilteredContracts = selections;
225  }
226 
227  var hasDiffs = _previousSelections.AreDifferent(selections);
228  _previousSelections = selections;
229  if (!hasDiffs)
230  {
231  return Unchanged;
232  }
233 
234  OnSelectionChanged(selections);
235  return selections;
236  }
237 
238  /// <summary>
239  /// Performs universe selection using the data specified
240  /// </summary>
241  /// <param name="utcTime">The current utc time</param>
242  /// <param name="data">The symbols to remain in the universe</param>
243  /// <returns>The data that passes the filter</returns>
244  public abstract IEnumerable<Symbol> SelectSymbols(DateTime utcTime, BaseDataCollection data);
245 
246  /// <summary>
247  /// Creates and configures a security for the specified symbol
248  /// </summary>
249  /// <param name="symbol">The symbol of the security to be created</param>
250  /// <param name="algorithm">The algorithm instance</param>
251  /// <param name="marketHoursDatabase">The market hours database</param>
252  /// <param name="symbolPropertiesDatabase">The symbol properties database</param>
253  /// <returns>The newly initialized security object</returns>
254  /// <obsolete>The CreateSecurity won't be called</obsolete>
255  [Obsolete("CreateSecurity is obsolete and will not be called. The system will create the required Securities based on selected symbols")]
256  public virtual Security CreateSecurity(Symbol symbol, IAlgorithm algorithm, MarketHoursDatabase marketHoursDatabase, SymbolPropertiesDatabase symbolPropertiesDatabase)
257  {
258  throw new InvalidOperationException("CreateSecurity is obsolete and should not be called." +
259  "The system will create the required Securities based on selected symbols");
260  }
261 
262  /// <summary>
263  /// Gets the subscription requests to be added for the specified security
264  /// </summary>
265  /// <param name="security">The security to get subscriptions for</param>
266  /// <param name="currentTimeUtc">The current time in utc. This is the frontier time of the algorithm</param>
267  /// <param name="maximumEndTimeUtc">The max end time</param>
268  /// <returns>All subscriptions required by this security</returns>
269  [Obsolete("This overload is obsolete and will not be called. It was not capable of creating new SubscriptionDataConfig due to lack of information")]
270  public virtual IEnumerable<SubscriptionRequest> GetSubscriptionRequests(Security security, DateTime currentTimeUtc, DateTime maximumEndTimeUtc)
271  {
272  throw new InvalidOperationException("This overload is obsolete and should not be called." +
273  "It was not capable of creating new SubscriptionDataConfig due to lack of information");
274  }
275 
276 
277  /// <summary>
278  /// Gets the subscription requests to be added for the specified security
279  /// </summary>
280  /// <param name="security">The security to get subscriptions for</param>
281  /// <param name="currentTimeUtc">The current time in utc. This is the frontier time of the algorithm</param>
282  /// <param name="maximumEndTimeUtc">The max end time</param>
283  /// <param name="subscriptionService">Instance which implements <see cref="ISubscriptionDataConfigService"/> interface</param>
284  /// <returns>All subscriptions required by this security</returns>
285  public virtual IEnumerable<SubscriptionRequest> GetSubscriptionRequests(Security security,
286  DateTime currentTimeUtc,
287  DateTime maximumEndTimeUtc,
288  ISubscriptionDataConfigService subscriptionService)
289  {
290  var result = subscriptionService.Add(security.Symbol,
294  dataNormalizationMode: UniverseSettings.DataNormalizationMode,
295  subscriptionDataTypes: UniverseSettings.SubscriptionDataTypes,
296  dataMappingMode: UniverseSettings.DataMappingMode,
297  contractDepthOffset: (uint)Math.Abs(UniverseSettings.ContractDepthOffset));
298  return result.Select(config => new SubscriptionRequest(isUniverseSubscription: false,
299  universe: this,
300  security: security,
301  configuration: config,
302  startTimeUtc: currentTimeUtc,
303  endTimeUtc: maximumEndTimeUtc));
304  }
305 
306  /// <summary>
307  /// Determines whether or not the specified symbol is currently a member of this universe
308  /// </summary>
309  /// <param name="symbol">The symbol whose membership is to be checked</param>
310  /// <returns>True if the specified symbol is part of this universe, false otherwise</returns>
311  public bool ContainsMember(Symbol symbol)
312  {
313  return Securities.ContainsKey(symbol);
314  }
315 
316  /// <summary>
317  /// Adds the specified security to this universe
318  /// </summary>
319  /// <param name="utcTime">The current utc date time</param>
320  /// <param name="security">The security to be added</param>
321  /// <param name="isInternal">True if internal member</param>
322  /// <returns>True if the security was successfully added,
323  /// false if the security was already in the universe</returns>
324  internal virtual bool AddMember(DateTime utcTime, Security security, bool isInternal)
325  {
326  // never add members to disposed universes
327  if (DisposeRequested)
328  {
329  return false;
330  }
331 
332  if (security.IsDelisted)
333  {
334  return false;
335  }
336 
337  return Securities.TryAdd(security.Symbol, new Member(utcTime, security, isInternal));
338  }
339 
340  /// <summary>
341  /// Tries to remove the specified security from the universe. This
342  /// will first check to verify that we can remove the security by
343  /// calling the <see cref="CanRemoveMember"/> function.
344  /// </summary>
345  /// <param name="utcTime">The current utc time</param>
346  /// <param name="security">The security to be removed</param>
347  /// <returns>True if the security was successfully removed, false if
348  /// we're not allowed to remove or if the security didn't exist</returns>
349  internal virtual bool RemoveMember(DateTime utcTime, Security security)
350  {
351  if (CanRemoveMember(utcTime, security))
352  {
353  Member member;
354  return Securities.TryRemove(security.Symbol, out member);
355  }
356  return false;
357  }
358 
359  /// <summary>
360  /// Marks this universe as disposed and ready to remove all child subscriptions
361  /// </summary>
362  public virtual void Dispose()
363  {
364  DisposeRequested = true;
365  }
366 
367  /// <summary>
368  /// Event invocator for the <see cref="SelectionChanged"/> event
369  /// </summary>
370  /// <param name="selection">The current universe selection</param>
371  protected void OnSelectionChanged(HashSet<Symbol> selection = null)
372  {
373  SelectionChanged?.Invoke(this, new SelectionEventArgs(selection ?? new HashSet<Symbol>()));
374  }
375 
376  /// <summary>
377  /// Provides a value to indicate that no changes should be made to the universe.
378  /// This value is intended to be returned by reference via <see cref="Universe.SelectSymbols"/>
379  /// </summary>
380  public sealed class UnchangedUniverse : IEnumerable<string>, IEnumerable<Symbol>
381  {
382  /// <summary>
383  /// Read-only instance of the <see cref="UnchangedUniverse"/> value
384  /// </summary>
385  public static readonly UnchangedUniverse Instance = new UnchangedUniverse();
386  private UnchangedUniverse() { }
387  IEnumerator<Symbol> IEnumerable<Symbol>.GetEnumerator() { yield break; }
388  IEnumerator<string> IEnumerable<string>.GetEnumerator() { yield break; }
389  IEnumerator IEnumerable.GetEnumerator() { yield break; }
390  }
391 
392  /// <summary>
393  /// Will adjust the <see cref="_minimumTimeInUniverseRoundingInterval"/>
394  /// so rounding is performed as expected
395  /// </summary>
396  private void AdjustMinimumTimeInUniverseRoundingInterval()
397  {
398  if (_minimumTimeInUniverseRoundingInterval >= Time.OneDay)
399  {
400  _minimumTimeInUniverseRoundingInterval = Time.OneDay;
401  }
402  else if (_minimumTimeInUniverseRoundingInterval >= Time.OneHour)
403  {
404  _minimumTimeInUniverseRoundingInterval = Time.OneHour;
405  }
406  else if (_minimumTimeInUniverseRoundingInterval >= Time.OneMinute)
407  {
408  _minimumTimeInUniverseRoundingInterval = Time.OneMinute;
409  }
410  else if (_minimumTimeInUniverseRoundingInterval >= Time.OneSecond)
411  {
412  _minimumTimeInUniverseRoundingInterval = Time.OneSecond;
413  }
414  }
415 
416  /// <summary>
417  /// Member of the Universe
418  /// </summary>
419  public sealed class Member
420  {
421  /// <summary>
422  /// DateTime when added
423  /// </summary>
424  public DateTime Added { get; init; }
425 
426  /// <summary>
427  /// The security that was added
428  /// </summary>
429  public Security Security { get; init; }
430 
431  /// <summary>
432  /// True if the security was added as internal by this universe
433  /// </summary>
434  public bool IsInternal { get; init; }
435 
436  /// <summary>
437  /// Initialize a new member for the universe
438  /// </summary>
439  /// <param name="added">DateTime added</param>
440  /// <param name="security">Security to add</param>
441  /// <param name="isInternal">True if internal member</param>
442  public Member(DateTime added, Security security, bool isInternal)
443  {
444  Added = added;
445  Security = security;
446  IsInternal = isInternal;
447  }
448  }
449 
450  /// <summary>
451  /// Event fired when the universe selection changes
452  /// </summary>
453  public class SelectionEventArgs : EventArgs
454  {
455  /// <summary>
456  /// The current universe selection
457  /// </summary>
458  public HashSet<Symbol> CurrentSelection { get; }
459 
460  /// <summary>
461  /// Creates a new instance
462  /// </summary>
463  public SelectionEventArgs(HashSet<Symbol> currentSelection)
464  {
465  CurrentSelection = currentSelection;
466  }
467  }
468  }
469 }