Lean  $LEAN_TAG$
ScheduledEvent.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 System.Linq;
20 using QuantConnect.Logging;
21 
23 {
24  /// <summary>
25  /// Real time self scheduling event
26  /// </summary>
27  public class ScheduledEvent : IDisposable
28  {
29  /// <summary>
30  /// Gets the default time before market close end of trading day events will fire
31  /// </summary>
32  public static readonly TimeSpan SecurityEndOfDayDelta = TimeSpan.FromMinutes(10);
33 
34  /// <summary>
35  /// Gets the default time before midnight end of day events will fire
36  /// </summary>
37  public static readonly TimeSpan AlgorithmEndOfDayDelta = TimeSpan.FromMinutes(2);
38 
39  private bool _needsMoveNext;
40  private bool _endOfScheduledEvents;
41  private readonly Action<string, DateTime> _callback;
42  private readonly IEnumerator<DateTime> _orderedEventUtcTimes;
43 
44  /// <summary>
45  /// Event that fires each time this scheduled event happens
46  /// </summary>
47  public event Action<string, DateTime> EventFired;
48 
49  /// <summary>
50  /// Gets or sets whether this event is enabled
51  /// </summary>
52  public bool Enabled
53  {
54  get; set;
55  }
56 
57  /// <summary>
58  /// Gets or sets whether this event will log each time it fires
59  /// </summary>
60  internal bool IsLoggingEnabled
61  {
62  get; set;
63  }
64 
65  /// <summary>
66  /// Gets the next time this scheduled event will fire in UTC
67  /// </summary>
68  public DateTime NextEventUtcTime
69  {
70  get
71  {
72  if (_endOfScheduledEvents)
73  {
74  return DateTime.MaxValue;
75  }
76 
77  if (_needsMoveNext)
78  {
79  _needsMoveNext = false;
80  _endOfScheduledEvents = !_orderedEventUtcTimes.MoveNext();
81  return NextEventUtcTime;
82  }
83  return _orderedEventUtcTimes.Current;
84  }
85  }
86 
87  /// <summary>
88  /// Gets an identifier for this event
89  /// </summary>
90  public string Name { get; }
91 
92  /// <summary>
93  /// Initializes a new instance of the <see cref="ScheduledEvent"/> class
94  /// </summary>
95  /// <param name="name">An identifier for this event</param>
96  /// <param name="eventUtcTime">The date time the event should fire</param>
97  /// <param name="callback">Delegate to be called when the event time passes</param>
98  public ScheduledEvent(string name, DateTime eventUtcTime, Action<string, DateTime> callback = null)
99  : this(name, new[] { eventUtcTime }.AsEnumerable().GetEnumerator(), callback)
100  {
101  }
102 
103  /// <summary>
104  /// Initializes a new instance of the <see cref="ScheduledEvent"/> class
105  /// </summary>
106  /// <param name="name">An identifier for this event</param>
107  /// <param name="orderedEventUtcTimes">An enumerable that emits event times</param>
108  /// <param name="callback">Delegate to be called each time an event passes</param>
109  public ScheduledEvent(string name, IEnumerable<DateTime> orderedEventUtcTimes, Action<string, DateTime> callback = null)
110  : this(name, orderedEventUtcTimes.GetEnumerator(), callback)
111  {
112  }
113 
114  /// <summary>
115  /// Initializes a new instance of the <see cref="ScheduledEvent"/> class
116  /// </summary>
117  /// <param name="name">An identifier for this event</param>
118  /// <param name="orderedEventUtcTimes">An enumerator that emits event times</param>
119  /// <param name="callback">Delegate to be called each time an event passes</param>
120  public ScheduledEvent(string name, IEnumerator<DateTime> orderedEventUtcTimes, Action<string, DateTime> callback = null)
121  {
122  Name = name;
123  Enabled = true;
124  _callback = callback;
125  // we don't move next until we are requested, this allows the algorithm to support the warmup period correctly
126  _needsMoveNext = true;
127  _orderedEventUtcTimes = orderedEventUtcTimes;
128 
129  }
130 
131  /// <summary>Serves as the default hash function. </summary>
132  /// <returns>A hash code for the current object.</returns>
133  /// <filterpriority>2</filterpriority>
134  public override int GetHashCode()
135  {
136  return Name.GetHashCode();
137  }
138 
139  /// <summary>Determines whether the specified object is equal to the current object.</summary>
140  /// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
141  /// <param name="obj">The object to compare with the current object. </param>
142  /// <filterpriority>2</filterpriority>
143  public override bool Equals(object obj)
144  {
145  return !ReferenceEquals(null, obj) && ReferenceEquals(this, obj);
146  }
147 
148  /// <summary>
149  /// Scans this event and fires the callback if an event happened
150  /// </summary>
151  /// <param name="utcTime">The current time in UTC</param>
152  internal void Scan(DateTime utcTime)
153  {
154  if (_endOfScheduledEvents)
155  {
156  return;
157  }
158 
159  do
160  {
161  if (_needsMoveNext)
162  {
163  // if we've passed an event or are just priming the pump, we need to move next
164  if (!_orderedEventUtcTimes.MoveNext())
165  {
166  if (IsLoggingEnabled)
167  {
168  Log.Trace($"ScheduledEvent.{Name}: Completed scheduled events.");
169  }
170  _endOfScheduledEvents = true;
171  return;
172  }
173  if (IsLoggingEnabled)
174  {
175  Log.Trace($"ScheduledEvent.{Name}: Next event: {_orderedEventUtcTimes.Current.ToStringInvariant(DateFormat.UI)} UTC");
176  }
177  }
178 
179  // if time has passed our event
180  if (utcTime >= _orderedEventUtcTimes.Current)
181  {
182  if (IsLoggingEnabled)
183  {
184  Log.Trace($"ScheduledEvent.{Name}: Firing at {utcTime.ToStringInvariant(DateFormat.UI)} UTC " +
185  $"Scheduled at {_orderedEventUtcTimes.Current.ToStringInvariant(DateFormat.UI)} UTC"
186  );
187  }
188  // fire the event
189  OnEventFired(_orderedEventUtcTimes.Current);
190  _needsMoveNext = true;
191  }
192  else
193  {
194  // we haven't passed the event time yet, so keep waiting on this Current
195  _needsMoveNext = false;
196  }
197  }
198  // keep checking events until we pass the current time, this will fire
199  // all 'skipped' events back to back in order, perhaps this should be handled
200  // in the real time handler
201  while (_needsMoveNext);
202  }
203 
204  /// <summary>
205  /// Fast forwards this schedule to the specified time without invoking the events
206  /// </summary>
207  /// <param name="utcTime">Frontier time</param>
208  internal void SkipEventsUntil(DateTime utcTime)
209  {
210  do
211  {
212  // zoom through the enumerator until we get to the desired time
213  if (utcTime <= NextEventUtcTime)
214  {
215  if (IsLoggingEnabled)
216  {
217  Log.Trace($"ScheduledEvent.{Name}: Skipped events before {utcTime.ToStringInvariant(DateFormat.UI)}. " +
218  $"Next event: {_orderedEventUtcTimes.Current.ToStringInvariant(DateFormat.UI)}"
219  );
220  }
221  return;
222  }
223  }
224  while (_orderedEventUtcTimes.MoveNext());
225 
226  if (IsLoggingEnabled)
227  {
228  Log.Trace($"ScheduledEvent.{Name}: Exhausted event stream during skip until {utcTime.ToStringInvariant(DateFormat.UI)}");
229  }
230  _endOfScheduledEvents = true;
231  }
232 
233  /// <summary>
234  /// Will return the ScheduledEvents name
235  /// </summary>
236  public override string ToString()
237  {
238  return $"{Name}";
239  }
240 
241  /// <summary>
242  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
243  /// </summary>
244  /// <filterpriority>2</filterpriority>
245  void IDisposable.Dispose()
246  {
247  _orderedEventUtcTimes.Dispose();
248  }
249 
250  /// <summary>
251  /// Event invocator for the <see cref="EventFired"/> event
252  /// </summary>
253  /// <param name="triggerTime">The event's time in UTC</param>
254  protected void OnEventFired(DateTime triggerTime)
255  {
256  // don't fire the event if we're turned off
257  if (!Enabled) return;
258 
259  _callback?.Invoke(Name, _orderedEventUtcTimes.Current);
260  EventFired?.Invoke(Name, triggerTime);
261  }
262  }
263 }