Lean  $LEAN_TAG$
ScheduleManager.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 
17 using System;
18 using System.Collections.Generic;
19 using NodaTime;
21 using QuantConnect.Logging;
22 using Python.Runtime;
23 
25 {
26  /// <summary>
27  /// Provides access to the real time handler's event scheduling feature
28  /// </summary>
30  {
31  private IEventSchedule _eventSchedule;
32 
33  private readonly SecurityManager _securities;
34  private readonly object _eventScheduleLock = new object();
35  private readonly List<ScheduledEvent> _preInitializedEvents;
36 
37  /// <summary>
38  /// Gets the date rules helper object to make specifying dates for events easier
39  /// </summary>
40  public DateRules DateRules { get; }
41 
42  /// <summary>
43  /// Gets the time rules helper object to make specifying times for events easier
44  /// </summary>
45  public TimeRules TimeRules { get; }
46 
47  /// <summary>
48  /// Initializes a new instance of the <see cref="ScheduleManager"/> class
49  /// </summary>
50  /// <param name="securities">Securities manager containing the algorithm's securities</param>
51  /// <param name="timeZone">The algorithm's time zone</param>
52  /// <param name="marketHoursDatabase">The market hours database instance to use</param>
53  public ScheduleManager(SecurityManager securities, DateTimeZone timeZone, MarketHoursDatabase marketHoursDatabase)
54  {
55  _securities = securities;
56  DateRules = new DateRules(securities, timeZone, marketHoursDatabase);
57  TimeRules = new TimeRules(securities, timeZone, marketHoursDatabase);
58 
59  // used for storing any events before the event schedule is set
60  _preInitializedEvents = new List<ScheduledEvent>();
61  }
62 
63  /// <summary>
64  /// Sets the <see cref="IEventSchedule"/> implementation
65  /// </summary>
66  /// <param name="eventSchedule">The event schedule implementation to be used. This is the IRealTimeHandler</param>
67  internal void SetEventSchedule(IEventSchedule eventSchedule)
68  {
69  if (eventSchedule == null)
70  {
71  throw new ArgumentNullException(nameof(eventSchedule));
72  }
73 
74  lock (_eventScheduleLock)
75  {
76  _eventSchedule = eventSchedule;
77 
78  // load up any events that were added before we were ready to send them to the scheduler
79  foreach (var scheduledEvent in _preInitializedEvents)
80  {
81  _eventSchedule.Add(scheduledEvent);
82  }
83  }
84  }
85 
86  /// <summary>
87  /// Adds the specified event to the schedule
88  /// </summary>
89  /// <param name="scheduledEvent">The event to be scheduled, including the date/times the event fires and the callback</param>
90  public void Add(ScheduledEvent scheduledEvent)
91  {
92  lock (_eventScheduleLock)
93  {
94  if (_eventSchedule != null)
95  {
96  _eventSchedule.Add(scheduledEvent);
97  }
98  else
99  {
100  _preInitializedEvents.Add(scheduledEvent);
101  }
102  }
103  }
104 
105  /// <summary>
106  /// Removes the specified event from the schedule
107  /// </summary>
108  /// <param name="scheduledEvent">The event to be removed</param>
109  public void Remove(ScheduledEvent scheduledEvent)
110  {
111  lock (_eventScheduleLock)
112  {
113  if (_eventSchedule != null)
114  {
115  _eventSchedule.Remove(scheduledEvent);
116  }
117  else
118  {
119  _preInitializedEvents.RemoveAll(se => Equals(se, scheduledEvent));
120  }
121  }
122  }
123 
124  /// <summary>
125  /// Schedules the callback to run using the specified date and time rules
126  /// </summary>
127  /// <param name="dateRule">Specifies what dates the event should run</param>
128  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
129  /// <param name="callback">The callback to be invoked</param>
130  public ScheduledEvent On(IDateRule dateRule, ITimeRule timeRule, Action callback)
131  {
132  return On(dateRule, timeRule, (name, time) => callback());
133  }
134 
135  /// <summary>
136  /// Schedules the callback to run using the specified date and time rules
137  /// </summary>
138  /// <param name="dateRule">Specifies what dates the event should run</param>
139  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
140  /// <param name="callback">The callback to be invoked</param>
141  public ScheduledEvent On(IDateRule dateRule, ITimeRule timeRule, PyObject callback)
142  {
143  return On(dateRule, timeRule, (name, time) => { using (Py.GIL()) callback.Invoke(); });
144  }
145 
146  /// <summary>
147  /// Schedules the callback to run using the specified date and time rules
148  /// </summary>
149  /// <param name="dateRule">Specifies what dates the event should run</param>
150  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
151  /// <param name="callback">The callback to be invoked</param>
152  public ScheduledEvent On(IDateRule dateRule, ITimeRule timeRule, Action<string, DateTime> callback)
153  {
154  var name = $"{dateRule.Name}: {timeRule.Name}";
155  return On(name, dateRule, timeRule, callback);
156  }
157 
158  /// <summary>
159  /// Schedules the callback to run using the specified date and time rules
160  /// </summary>
161  /// <param name="name">The event's unique name</param>
162  /// <param name="dateRule">Specifies what dates the event should run</param>
163  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
164  /// <param name="callback">The callback to be invoked</param>
165  public ScheduledEvent On(string name, IDateRule dateRule, ITimeRule timeRule, Action callback)
166  {
167  return On(name, dateRule, timeRule, (n, d) => callback());
168  }
169 
170  /// <summary>
171  /// Schedules the callback to run using the specified date and time rules
172  /// </summary>
173  /// <param name="name">The event's unique name</param>
174  /// <param name="dateRule">Specifies what dates the event should run</param>
175  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
176  /// <param name="callback">The callback to be invoked</param>
177  public ScheduledEvent On(string name, IDateRule dateRule, ITimeRule timeRule, PyObject callback)
178  {
179  return On(name, dateRule, timeRule, (n, d) => { using (Py.GIL()) callback.Invoke(); });
180  }
181 
182  /// <summary>
183  /// Schedules the callback to run using the specified date and time rules
184  /// </summary>
185  /// <param name="name">The event's unique name</param>
186  /// <param name="dateRule">Specifies what dates the event should run</param>
187  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
188  /// <param name="callback">The callback to be invoked</param>
189  public ScheduledEvent On(string name, IDateRule dateRule, ITimeRule timeRule, Action<string, DateTime> callback)
190  {
191  // back the date up to ensure we get all events, the event scheduler will skip past events that whose time has passed
192  var dates = GetDatesDeferred(dateRule, _securities);
193  var eventTimes = timeRule.CreateUtcEventTimes(dates);
194  var scheduledEvent = new ScheduledEvent(name, eventTimes, callback);
195  Add(scheduledEvent);
196 
197  Log.Trace($"Event Name \"{scheduledEvent.Name}\", scheduled to run.");
198  return scheduledEvent;
199  }
200 
201  #region Fluent Scheduling
202 
203  /// <summary>
204  /// Entry point for the fluent scheduled event builder
205  /// </summary>
207  {
208  return new FluentScheduledEventBuilder(this, _securities);
209  }
210 
211  /// <summary>
212  /// Entry point for the fluent scheduled event builder
213  /// </summary>
215  {
216  return new FluentScheduledEventBuilder(this, _securities, name);
217  }
218 
219  #endregion
220 
221  #region Training Events
222 
223  /// <summary>
224  /// Schedules the provided training code to execute immediately
225  /// </summary>
226  public ScheduledEvent TrainingNow(Action trainingCode)
227  {
228  return On($"Training: Now: {_securities.UtcTime:O}", DateRules.Today, TimeRules.Now, trainingCode);
229  }
230 
231  /// <summary>
232  /// Schedules the provided training code to execute immediately
233  /// </summary>
234  public ScheduledEvent TrainingNow(PyObject trainingCode)
235  {
236  return On($"Training: Now: {_securities.UtcTime:O}", DateRules.Today, TimeRules.Now, trainingCode);
237  }
238 
239  /// <summary>
240  /// Schedules the training code to run using the specified date and time rules
241  /// </summary>
242  /// <param name="dateRule">Specifies what dates the event should run</param>
243  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
244  /// <param name="trainingCode">The training code to be invoked</param>
245  public ScheduledEvent Training(IDateRule dateRule, ITimeRule timeRule, Action trainingCode)
246  {
247  var name = $"{dateRule.Name}: {timeRule.Name}";
248  return On(name, dateRule, timeRule, (n, time) => trainingCode());
249  }
250 
251 
252  /// <summary>
253  /// Schedules the training code to run using the specified date and time rules
254  /// </summary>
255  /// <param name="dateRule">Specifies what dates the event should run</param>
256  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
257  /// <param name="trainingCode">The training code to be invoked</param>
258  public ScheduledEvent Training(IDateRule dateRule, ITimeRule timeRule, PyObject trainingCode)
259  {
260  var name = $"{dateRule.Name}: {timeRule.Name}";
261  return On(name, dateRule, timeRule, (n, time) => { using (Py.GIL()) trainingCode.Invoke(); });
262  }
263 
264  /// <summary>
265  /// Schedules the training code to run using the specified date and time rules
266  /// </summary>
267  /// <param name="dateRule">Specifies what dates the event should run</param>
268  /// <param name="timeRule">Specifies the times on those dates the event should run</param>
269  /// <param name="trainingCode">The training code to be invoked</param>
270  public ScheduledEvent Training(IDateRule dateRule, ITimeRule timeRule, Action<DateTime> trainingCode)
271  {
272  var name = $"{dateRule.Name}: {timeRule.Name}";
273  return On(name, dateRule, timeRule, (n, time) => trainingCode(time));
274  }
275 
276  #endregion
277 
278  /// <summary>
279  /// Helper methods to defer the evaluation of the current time until the dates are enumerated for the first time.
280  /// This allows for correct support for warmup period
281  /// </summary>
282  internal static IEnumerable<DateTime> GetDatesDeferred(IDateRule dateRule, SecurityManager securities)
283  {
284  foreach (var item in dateRule.GetDates(securities.UtcTime.Date.AddDays(-1), Time.EndOfTime))
285  {
286  yield return item;
287  }
288  }
289  }
290 }