/* * 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 NodaTime; using QuantConnect.Securities; namespace QuantConnect.Scheduling { /// /// Helper class used to provide better syntax when defining time rules /// public class TimeRules { private DateTimeZone _timeZone; private readonly SecurityManager _securities; /// /// Initializes a new instance of the helper class /// /// The security manager /// The algorithm's default time zone public TimeRules(SecurityManager securities, DateTimeZone timeZone) { _securities = securities; _timeZone = timeZone; } /// /// Sets the default time zone /// /// The time zone to use for helper methods that can't resolve a time zone public void SetDefaultTimeZone(DateTimeZone timeZone) { _timeZone = timeZone; } /// /// Specifies an event should fire at the specified time of day in the algorithm's time zone /// /// The time of day in the algorithm's time zone the event should fire /// A time rule that fires at the specified time in the algorithm's time zone public ITimeRule At(TimeSpan timeOfDay) { return At(timeOfDay, _timeZone); } /// /// Specifies an event should fire at the specified time of day in the algorithm's time zone /// /// The hour /// The minute /// The second /// A time rule that fires at the specified time in the algorithm's time zone public ITimeRule At(int hour, int minute, int second = 0) { return At(new TimeSpan(hour, minute, second), _timeZone); } /// /// Specifies an event should fire at the specified time of day in the specified time zone /// /// The hour /// The minute /// The time zone the event time is represented in /// A time rule that fires at the specified time in the algorithm's time zone public ITimeRule At(int hour, int minute, DateTimeZone timeZone) { return At(new TimeSpan(hour, minute, 0), timeZone); } /// /// Specifies an event should fire at the specified time of day in the specified time zone /// /// The hour /// The minute /// The second /// The time zone the event time is represented in /// A time rule that fires at the specified time in the algorithm's time zone public ITimeRule At(int hour, int minute, int second, DateTimeZone timeZone) { return At(new TimeSpan(hour, minute, second), timeZone); } /// /// Specifies an event should fire at the specified time of day in the specified time zone /// /// The time of day in the algorithm's time zone the event should fire /// The time zone the date time is expressed in /// A time rule that fires at the specified time in the algorithm's time zone public ITimeRule At(TimeSpan timeOfDay, DateTimeZone timeZone) { var name = string.Join(",", timeOfDay.TotalHours.ToString("0.##")); Func, IEnumerable> applicator = dates => from date in dates let localEventTime = date + timeOfDay let utcEventTime = localEventTime.ConvertToUtc(timeZone) select utcEventTime; return new FuncTimeRule(name, applicator); } /// /// Specifies an event should fire periodically on the requested interval /// /// The frequency with which the event should fire, can not be zero or less /// A time rule that fires after each interval passes public ITimeRule Every(TimeSpan interval) { if (interval <= TimeSpan.Zero) { throw new ArgumentException("TimeRules.Every(): time span interval can not be zero or less"); } var name = "Every " + interval.TotalMinutes.ToString("0.##") + " min"; Func, IEnumerable> applicator = dates => EveryIntervalIterator(dates, interval); return new FuncTimeRule(name, applicator); } /// /// Specifies an event should fire at market open +- /// /// The symbol whose market open we want an event for /// The minutes after market open that the event should fire /// True to use extended market open, false to use regular market open /// A time rule that fires the specified number of minutes after the symbol's market open public ITimeRule AfterMarketOpen(Symbol symbol, double minutesAfterOpen = 0, bool extendedMarketOpen = false) { var security = GetSecurity(symbol); var type = extendedMarketOpen ? "ExtendedMarketOpen" : "MarketOpen"; var name = string.Format("{0}: {1} min after {2}", symbol, minutesAfterOpen.ToString("0.##"), type); var timeAfterOpen = TimeSpan.FromMinutes(minutesAfterOpen); Func, IEnumerable> applicator = dates => from date in dates where security.Exchange.DateIsOpen(date) let marketOpen = security.Exchange.Hours.GetNextMarketOpen(date, extendedMarketOpen) let localEventTime = marketOpen + timeAfterOpen let utcEventTime = localEventTime.ConvertToUtc(security.Exchange.TimeZone) select utcEventTime; return new FuncTimeRule(name, applicator); } /// /// Specifies an event should fire at the market close +- /// /// The symbol whose market close we want an event for /// The time before market close that the event should fire /// True to use extended market close, false to use regular market close /// A time rule that fires the specified number of minutes before the symbol's market close public ITimeRule BeforeMarketClose(Symbol symbol, double minutesBeforeClose = 0, bool extendedMarketClose = false) { var security = GetSecurity(symbol); var type = extendedMarketClose ? "ExtendedMarketClose" : "MarketClose"; var name = string.Format("{0}: {1} min before {2}", security.Symbol, minutesBeforeClose.ToString("0.##"), type); var timeBeforeClose = TimeSpan.FromMinutes(minutesBeforeClose); Func, IEnumerable> applicator = dates => from date in dates where security.Exchange.DateIsOpen(date) let marketClose = security.Exchange.Hours.GetNextMarketClose(date, extendedMarketClose) let localEventTime = marketClose - timeBeforeClose let utcEventTime = localEventTime.ConvertToUtc(security.Exchange.TimeZone) select utcEventTime; return new FuncTimeRule(name, applicator); } private Security GetSecurity(Symbol symbol) { Security security; if (!_securities.TryGetValue(symbol, out security)) { throw new Exception(symbol.ToString() + " not found in portfolio. Request this data when initializing the algorithm."); } return security; } /// /// For each provided date will yield all the time intervals based on the supplied time span /// /// The dates for which we want to create the different intervals /// The interval value to use, can not be zero or less private static IEnumerable EveryIntervalIterator(IEnumerable dates, TimeSpan interval) { if (interval <= TimeSpan.Zero) { throw new ArgumentException("TimeRules.EveryIntervalIterator(): time span interval can not be zero or less"); } foreach (var date in dates) { for (var time = TimeSpan.Zero; time < Time.OneDay; time += interval) { yield return date + time; } } } } }