chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Logging;
|
||||
|
||||
namespace QuantConnect.Scheduling
|
||||
{
|
||||
/// <summary>
|
||||
/// Real time self scheduling event
|
||||
/// </summary>
|
||||
public class ScheduledEvent : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default time before market close end of trading day events will fire
|
||||
/// </summary>
|
||||
public static readonly TimeSpan SecurityEndOfDayDelta = TimeSpan.FromMinutes(10);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default time before midnight end of day events will fire
|
||||
/// </summary>
|
||||
public static readonly TimeSpan AlgorithmEndOfDayDelta = TimeSpan.FromMinutes(2);
|
||||
|
||||
private bool _needsMoveNext;
|
||||
private bool _endOfScheduledEvents;
|
||||
private readonly Action<string, DateTime> _callback;
|
||||
private readonly IEnumerator<DateTime> _orderedEventUtcTimes;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires each time this scheduled event happens
|
||||
/// </summary>
|
||||
public event Action<string, DateTime> EventFired;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this event is enabled
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this event will log each time it fires
|
||||
/// </summary>
|
||||
internal bool IsLoggingEnabled
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next time this scheduled event will fire in UTC
|
||||
/// </summary>
|
||||
public DateTime NextEventUtcTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_endOfScheduledEvents)
|
||||
{
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
|
||||
if (_needsMoveNext)
|
||||
{
|
||||
_needsMoveNext = false;
|
||||
_endOfScheduledEvents = !_orderedEventUtcTimes.MoveNext();
|
||||
return NextEventUtcTime;
|
||||
}
|
||||
return _orderedEventUtcTimes.Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an identifier for this event
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledEvent"/> class
|
||||
/// </summary>
|
||||
/// <param name="name">An identifier for this event</param>
|
||||
/// <param name="eventUtcTime">The date time the event should fire</param>
|
||||
/// <param name="callback">Delegate to be called when the event time passes</param>
|
||||
public ScheduledEvent(string name, DateTime eventUtcTime, Action<string, DateTime> callback = null)
|
||||
: this(name, new[] { eventUtcTime }.AsEnumerable().GetEnumerator(), callback)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledEvent"/> class
|
||||
/// </summary>
|
||||
/// <param name="name">An identifier for this event</param>
|
||||
/// <param name="orderedEventUtcTimes">An enumerable that emits event times</param>
|
||||
/// <param name="callback">Delegate to be called each time an event passes</param>
|
||||
public ScheduledEvent(string name, IEnumerable<DateTime> orderedEventUtcTimes, Action<string, DateTime> callback = null)
|
||||
: this(name, orderedEventUtcTimes.GetEnumerator(), callback)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledEvent"/> class
|
||||
/// </summary>
|
||||
/// <param name="name">An identifier for this event</param>
|
||||
/// <param name="orderedEventUtcTimes">An enumerator that emits event times</param>
|
||||
/// <param name="callback">Delegate to be called each time an event passes</param>
|
||||
public ScheduledEvent(string name, IEnumerator<DateTime> orderedEventUtcTimes, Action<string, DateTime> callback = null)
|
||||
{
|
||||
Name = name;
|
||||
Enabled = true;
|
||||
_callback = callback;
|
||||
// we don't move next until we are requested, this allows the algorithm to support the warmup period correctly
|
||||
_needsMoveNext = true;
|
||||
_orderedEventUtcTimes = orderedEventUtcTimes;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Serves as the default hash function. </summary>
|
||||
/// <returns>A hash code for the current object.</returns>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified object is equal to the current object.</summary>
|
||||
/// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
|
||||
/// <param name="obj">The object to compare with the current object. </param>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return !ReferenceEquals(null, obj) && ReferenceEquals(this, obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans this event and fires the callback if an event happened
|
||||
/// </summary>
|
||||
/// <param name="utcTime">The current time in UTC</param>
|
||||
internal void Scan(DateTime utcTime)
|
||||
{
|
||||
if (_endOfScheduledEvents)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (_needsMoveNext)
|
||||
{
|
||||
// if we've passed an event or are just priming the pump, we need to move next
|
||||
if (!_orderedEventUtcTimes.MoveNext())
|
||||
{
|
||||
if (IsLoggingEnabled)
|
||||
{
|
||||
Log.Trace($"ScheduledEvent.{Name}: Completed scheduled events.");
|
||||
}
|
||||
_endOfScheduledEvents = true;
|
||||
return;
|
||||
}
|
||||
if (IsLoggingEnabled)
|
||||
{
|
||||
Log.Trace($"ScheduledEvent.{Name}: Next event: {_orderedEventUtcTimes.Current.ToStringInvariant(DateFormat.UI)} UTC");
|
||||
}
|
||||
}
|
||||
|
||||
// if time has passed our event
|
||||
if (utcTime >= _orderedEventUtcTimes.Current)
|
||||
{
|
||||
if (IsLoggingEnabled)
|
||||
{
|
||||
Log.Trace($"ScheduledEvent.{Name}: Firing at {utcTime.ToStringInvariant(DateFormat.UI)} UTC " +
|
||||
$"Scheduled at {_orderedEventUtcTimes.Current.ToStringInvariant(DateFormat.UI)} UTC"
|
||||
);
|
||||
}
|
||||
// fire the event
|
||||
OnEventFired(_orderedEventUtcTimes.Current);
|
||||
_needsMoveNext = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we haven't passed the event time yet, so keep waiting on this Current
|
||||
_needsMoveNext = false;
|
||||
}
|
||||
}
|
||||
// keep checking events until we pass the current time, this will fire
|
||||
// all 'skipped' events back to back in order, perhaps this should be handled
|
||||
// in the real time handler
|
||||
while (_needsMoveNext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast forwards this schedule to the specified time without invoking the events
|
||||
/// </summary>
|
||||
/// <param name="utcTime">Frontier time</param>
|
||||
internal void SkipEventsUntil(DateTime utcTime)
|
||||
{
|
||||
do
|
||||
{
|
||||
// zoom through the enumerator until we get to the desired time
|
||||
if (utcTime <= NextEventUtcTime)
|
||||
{
|
||||
if (IsLoggingEnabled)
|
||||
{
|
||||
Log.Trace($"ScheduledEvent.{Name}: Skipped events before {utcTime.ToStringInvariant(DateFormat.UI)}. " +
|
||||
$"Next event: {_orderedEventUtcTimes.Current.ToStringInvariant(DateFormat.UI)}"
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (_orderedEventUtcTimes.MoveNext());
|
||||
|
||||
if (IsLoggingEnabled)
|
||||
{
|
||||
Log.Trace($"ScheduledEvent.{Name}: Exhausted event stream during skip until {utcTime.ToStringInvariant(DateFormat.UI)}");
|
||||
}
|
||||
_endOfScheduledEvents = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will return the ScheduledEvents name
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
_orderedEventUtcTimes.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the <see cref="EventFired"/> event
|
||||
/// </summary>
|
||||
/// <param name="triggerTime">The event's time in UTC</param>
|
||||
protected void OnEventFired(DateTime triggerTime)
|
||||
{
|
||||
// don't fire the event if we're turned off
|
||||
if (!Enabled) return;
|
||||
|
||||
_callback?.Invoke(Name, _orderedEventUtcTimes.Current);
|
||||
EventFired?.Invoke(Name, triggerTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user