/*
* 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;
}
}
}
}
}