/* * 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 NodaTime; using QuantConnect.Securities; using QuantConnect.Logging; using System.Linq; using Python.Runtime; namespace QuantConnect.Scheduling { /// /// Provides access to the real time handler's event scheduling feature /// public class ScheduleManager : IEventSchedule { private IEventSchedule _eventSchedule; private readonly SecurityManager _securities; private readonly object _eventScheduleLock = new object(); private readonly List _preInitializedEvents; /// /// Gets the date rules helper object to make specifying dates for events easier /// public DateRules DateRules { get; } /// /// Gets the time rules helper object to make specifying times for events easier /// public TimeRules TimeRules { get; } /// /// Initializes a new instance of the class /// /// Securities manager containing the algorithm's securities /// The algorithm's time zone public ScheduleManager(SecurityManager securities, DateTimeZone timeZone) { _securities = securities; DateRules = new DateRules(securities); TimeRules = new TimeRules(securities, timeZone); // used for storing any events before the event schedule is set _preInitializedEvents = new List(); } /// /// Sets the implementation /// /// The event schedule implementation to be used. This is the IRealTimeHandler internal void SetEventSchedule(IEventSchedule eventSchedule) { if (eventSchedule == null) { throw new ArgumentNullException(nameof(eventSchedule)); } lock (_eventScheduleLock) { _eventSchedule = eventSchedule; // load up any events that were added before we were ready to send them to the scheduler foreach (var scheduledEvent in _preInitializedEvents) { _eventSchedule.Add(scheduledEvent); } } } /// /// Adds the specified event to the schedule /// /// The event to be scheduled, including the date/times the event fires and the callback public void Add(ScheduledEvent scheduledEvent) { lock (_eventScheduleLock) { if (_eventSchedule != null) { _eventSchedule.Add(scheduledEvent); } else { _preInitializedEvents.Add(scheduledEvent); } } } /// /// Removes the specified event from the schedule /// /// The event to be removed public void Remove(ScheduledEvent scheduledEvent) { lock (_eventScheduleLock) { if (_eventSchedule != null) { _eventSchedule.Remove(scheduledEvent); } else { _preInitializedEvents.RemoveAll(se => Equals(se, scheduledEvent)); } } } /// /// Schedules the callback to run using the specified date and time rules /// /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The callback to be invoked public ScheduledEvent On(IDateRule dateRule, ITimeRule timeRule, Action callback) { return On(dateRule, timeRule, (name, time) => callback()); } /// /// Schedules the callback to run using the specified date and time rules /// /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The callback to be invoked public ScheduledEvent On(IDateRule dateRule, ITimeRule timeRule, PyObject callback) { return On(dateRule, timeRule, (name, time) => { using (Py.GIL()) callback.Invoke(); }); } /// /// Schedules the callback to run using the specified date and time rules /// /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The callback to be invoked public ScheduledEvent On(IDateRule dateRule, ITimeRule timeRule, Action callback) { var name = dateRule.Name + ": " + timeRule.Name; return On(name, dateRule, timeRule, callback); } /// /// Schedules the callback to run using the specified date and time rules /// /// The event's unique name /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The callback to be invoked public ScheduledEvent On(string name, IDateRule dateRule, ITimeRule timeRule, Action callback) { return On(name, dateRule, timeRule, (n, d) => callback()); } /// /// Schedules the callback to run using the specified date and time rules /// /// The event's unique name /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The callback to be invoked public ScheduledEvent On(string name, IDateRule dateRule, ITimeRule timeRule, PyObject callback) { return On(name, dateRule, timeRule, (n, d) => { using (Py.GIL()) callback.Invoke(); }); } /// /// Schedules the callback to run using the specified date and time rules /// /// The event's unique name /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The callback to be invoked public ScheduledEvent On(string name, IDateRule dateRule, ITimeRule timeRule, Action callback) { // back the date up to ensure we get all events, the event scheduler will skip past events that whose time has passed var dates = dateRule.GetDates(_securities.UtcTime.Date.AddDays(-1), Time.EndOfTime); var eventTimes = timeRule.CreateUtcEventTimes(dates); var scheduledEvent = new ScheduledEvent(name, eventTimes, callback); Add(scheduledEvent); var exampleTimes = eventTimes.Take(3) .Select(x => x.ToString()) .ToArray(); if (exampleTimes.Length > 0) { Log.Trace("Event Name \"{0}\", scheduled to run at {1} (UTC){2}", scheduledEvent.Name, string.Join(", ", exampleTimes), exampleTimes.Length > 1? "..." : ""); } else { Log.Trace("Event Name \"{0}\", scheduled to run, but no event times were selected", scheduledEvent.Name); } return scheduledEvent; } #region Fluent Scheduling /// /// Entry point for the fluent scheduled event builder /// public IFluentSchedulingDateSpecifier Event() { return new FluentScheduledEventBuilder(this, _securities); } /// /// Entry point for the fluent scheduled event builder /// public IFluentSchedulingDateSpecifier Event(string name) { return new FluentScheduledEventBuilder(this, _securities, name); } #endregion } }