Lean  $LEAN_TAG$
ScheduledUniverse.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 NodaTime;
17 using Python.Runtime;
21 using System;
22 using System.Collections.Generic;
23 
25 {
26  /// <summary>
27  /// Defines a user that is fired based on a specified <see cref="IDateRule"/> and <see cref="ITimeRule"/>
28  /// </summary>
30  {
31  private readonly IDateRule _dateRule;
32  private readonly ITimeRule _timeRule;
33  private readonly Func<DateTime, IEnumerable<Symbol>> _selector;
34 
35  /// <summary>
36  /// Initializes a new instance of the <see cref="ScheduledUniverse"/> class
37  /// </summary>
38  /// <param name="timeZone">The time zone the date/time rules are in</param>
39  /// <param name="dateRule">Date rule defines what days the universe selection function will be invoked</param>
40  /// <param name="timeRule">Time rule defines what times on each day selected by date rule the universe selection function will be invoked</param>
41  /// <param name="selector">Selector function accepting the date time firing time and returning the universe selected symbols</param>
42  /// <param name="settings">Universe settings for subscriptions added via this universe, null will default to algorithm's universe settings</param>
43  public ScheduledUniverse(DateTimeZone timeZone, IDateRule dateRule, ITimeRule timeRule, Func<DateTime, IEnumerable<Symbol>> selector, UniverseSettings settings = null)
44  : base(CreateConfiguration(timeZone, dateRule, timeRule))
45  {
46  _dateRule = dateRule;
47  _timeRule = timeRule;
48  _selector = selector;
49  UniverseSettings = settings;
50  }
51 
52  /// <summary>
53  /// Initializes a new instance of the <see cref="ScheduledUniverse"/> class
54  /// </summary>
55  /// <param name="dateRule">Date rule defines what days the universe selection function will be invoked</param>
56  /// <param name="timeRule">Time rule defines what times on each day selected by date rule the universe selection function will be invoked</param>
57  /// <param name="selector">Selector function accepting the date time firing time and returning the universe selected symbols</param>
58  /// <param name="settings">Universe settings for subscriptions added via this universe, null will default to algorithm's universe settings</param>
59  public ScheduledUniverse(IDateRule dateRule, ITimeRule timeRule, Func<DateTime, IEnumerable<Symbol>> selector, UniverseSettings settings = null)
60  : this(TimeZones.Utc, dateRule, timeRule, selector, settings)
61  {
62  }
63 
64  /// <summary>
65  /// Initializes a new instance of the <see cref="ScheduledUniverse"/> class
66  /// </summary>
67  /// <param name="timeZone">The time zone the date/time rules are in</param>
68  /// <param name="dateRule">Date rule defines what days the universe selection function will be invoked</param>
69  /// <param name="timeRule">Time rule defines what times on each day selected by date rule the universe selection function will be invoked</param>
70  /// <param name="selector">Selector function accepting the date time firing time and returning the universe selected symbols</param>
71  /// <param name="settings">Universe settings for subscriptions added via this universe, null will default to algorithm's universe settings</param>
72  public ScheduledUniverse(DateTimeZone timeZone, IDateRule dateRule, ITimeRule timeRule, PyObject selector, UniverseSettings settings = null)
73  : base(CreateConfiguration(timeZone, dateRule, timeRule))
74  {
75  Func<DateTime, object> func;
76  selector.TryConvertToDelegate(out func);
77  _dateRule = dateRule;
78  _timeRule = timeRule;
79  _selector = func.ConvertSelectionSymbolDelegate();
80  UniverseSettings = settings;
81  }
82 
83  /// <summary>
84  /// Initializes a new instance of the <see cref="ScheduledUniverse"/> class
85  /// </summary>
86  /// <param name="dateRule">Date rule defines what days the universe selection function will be invoked</param>
87  /// <param name="timeRule">Time rule defines what times on each day selected by date rule the universe selection function will be invoked</param>
88  /// <param name="selector">Selector function accepting the date time firing time and returning the universe selected symbols</param>
89  /// <param name="settings">Universe settings for subscriptions added via this universe, null will default to algorithm's universe settings</param>
90  public ScheduledUniverse(IDateRule dateRule, ITimeRule timeRule, PyObject selector, UniverseSettings settings = null)
91  : this(TimeZones.Utc, dateRule, timeRule, selector, settings)
92  {
93  }
94 
95  /// <summary>
96  /// Performs universe selection using the data specified
97  /// </summary>
98  /// <param name="utcTime">The current utc time</param>
99  /// <param name="data">The symbols to remain in the universe</param>
100  /// <returns>The data that passes the filter</returns>
101  public override IEnumerable<Symbol> SelectSymbols(DateTime utcTime, BaseDataCollection data)
102  {
103  return _selector(utcTime);
104  }
105 
106  /// <summary>
107  /// Get an enumerator of UTC DateTimes that defines when this universe will be invoked
108  /// </summary>
109  /// <param name="startTimeUtc">The start time of the range in UTC</param>
110  /// <param name="endTimeUtc">The end time of the range in UTC</param>
111  /// <returns>An enumerator of UTC DateTimes that defines when this universe will be invoked</returns>
112  public IEnumerable<DateTime> GetTriggerTimes(DateTime startTimeUtc, DateTime endTimeUtc, MarketHoursDatabase marketHoursDatabase)
113  {
114  var startTimeLocal = startTimeUtc.ConvertFromUtc(Configuration.ExchangeTimeZone);
115  var endTimeLocal = endTimeUtc.ConvertFromUtc(Configuration.ExchangeTimeZone);
116 
117  // define date/time rule enumerable
118  var dates = _dateRule.GetDates(startTimeLocal, endTimeLocal);
119  var times = _timeRule.CreateUtcEventTimes(dates).GetEnumerator();
120 
121  // Make sure and filter out any times before our start time
122  // GH #5440
123  do
124  {
125  if (!times.MoveNext())
126  {
127  times.Dispose();
128  yield break;
129  }
130  }
131  while (times.Current < startTimeUtc);
132 
133  // Start yielding times
134  do
135  {
136  yield return times.Current;
137  }
138  while (times.MoveNext());
139  times.Dispose();
140  }
141 
142  private static SubscriptionDataConfig CreateConfiguration(DateTimeZone timeZone, IDateRule dateRule, ITimeRule timeRule)
143  {
144  // remove forbidden characters
145  var ticker = $"{dateRule.Name}_{timeRule.Name}";
147  {
148  ticker = ticker.Replace(c.ToStringInvariant(), "_");
149  }
150 
151  var symbol = Symbol.Create(ticker, SecurityType.Base, QuantConnect.Market.USA);
152  var config = new SubscriptionDataConfig(typeof(Tick),
153  symbol: symbol,
154  resolution: Resolution.Daily,
155  dataTimeZone: timeZone,
156  exchangeTimeZone: timeZone,
157  fillForward: false,
158  extendedHours: false,
159  isInternalFeed: true,
160  isCustom: false,
161  tickType: null,
162  isFilteredSubscription: false
163  );
164 
165  // force always open hours so we don't inadvertently mess with the scheduled firing times
167  .SetEntryAlwaysOpen(config.Market, config.Symbol.Value, config.SecurityType, config.ExchangeTimeZone);
168 
169  return config;
170  }
171  }
172 }