319 lines
13 KiB
C#
319 lines
13 KiB
C#
/*
|
|
* 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.Securities;
|
|
using QuantConnect.Util;
|
|
|
|
namespace QuantConnect.Scheduling
|
|
{
|
|
/// <summary>
|
|
/// Helper class used to provide better syntax when defining date rules
|
|
/// </summary>
|
|
public class DateRules
|
|
{
|
|
private readonly SecurityManager _securities;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="DateRules"/> helper class
|
|
/// </summary>
|
|
/// <param name="securities">The security manager</param>
|
|
public DateRules(SecurityManager securities)
|
|
{
|
|
_securities = securities;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire only on the specified day
|
|
/// </summary>
|
|
/// <param name="year">The year</param>
|
|
/// <param name="month">The month</param>
|
|
/// <param name="day">The day</param>
|
|
/// <returns></returns>
|
|
public IDateRule On(int year, int month, int day)
|
|
{
|
|
// make sure they're date objects
|
|
var dates = new[] {new DateTime(year, month, day)};
|
|
return new FuncDateRule(string.Join(",", dates.Select(x => x.ToShortDateString())), (start, end) => dates);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire only on the specified days
|
|
/// </summary>
|
|
/// <param name="dates">The dates the event should fire</param>
|
|
/// <returns></returns>
|
|
public IDateRule On(params DateTime[] dates)
|
|
{
|
|
// make sure they're date objects
|
|
dates = dates.Select(x => x.Date).ToArray();
|
|
return new FuncDateRule(string.Join(",", dates.Select(x => x.ToShortDateString())), (start, end) => dates);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on each of the specified days of week
|
|
/// </summary>
|
|
/// <param name="day">The day the event shouls fire</param>
|
|
/// <returns>A date rule that fires on every specified day of week</returns>
|
|
public IDateRule Every(DayOfWeek day) => Every(new[] { day });
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on each of the specified days of week
|
|
/// </summary>
|
|
/// <param name="days">The days the event shouls fire</param>
|
|
/// <returns>A date rule that fires on every specified day of week</returns>
|
|
public IDateRule Every(params DayOfWeek[] days)
|
|
{
|
|
var hash = days.ToHashSet();
|
|
return new FuncDateRule(string.Join(",", days), (start, end) => Time.EachDay(start, end).Where(date => hash.Contains(date.DayOfWeek)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire every day
|
|
/// </summary>
|
|
/// <returns>A date rule that fires every day</returns>
|
|
public IDateRule EveryDay()
|
|
{
|
|
return new FuncDateRule("EveryDay", Time.EachDay);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire every day the symbol is trading
|
|
/// </summary>
|
|
/// <param name="symbol">The symbol whose exchange is used to determine tradeable dates</param>
|
|
/// <returns>A date rule that fires every day the specified symbol trades</returns>
|
|
public IDateRule EveryDay(Symbol symbol)
|
|
{
|
|
var security = GetSecurity(symbol);
|
|
return new FuncDateRule(symbol.Value + ": EveryDay", (start, end) => Time.EachTradeableDay(security, start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on the first of each month
|
|
/// </summary>
|
|
/// <returns>A date rule that fires on the first of each month</returns>
|
|
public IDateRule MonthStart()
|
|
{
|
|
return new FuncDateRule("MonthStart", (start, end) => MonthStartIterator(null, start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on the first tradeable date for the specified
|
|
/// symbol of each month
|
|
/// </summary>
|
|
/// <param name="symbol">The symbol whose exchange is used to determine the first
|
|
/// tradeable date of the month</param>
|
|
/// <returns>A date rule that fires on the first tradeable date for the specified security each month</returns>
|
|
public IDateRule MonthStart(Symbol symbol)
|
|
{
|
|
return new FuncDateRule(symbol.Value + ": MonthStart", (start, end) => MonthStartIterator(GetSecurity(symbol), start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on the last of each month
|
|
/// </summary>
|
|
/// <returns>A date rule that fires on the last of each month</returns>
|
|
public IDateRule MonthEnd()
|
|
{
|
|
return new FuncDateRule("MonthEnd", (start, end) => MonthEndIterator(null, start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on the last tradeable date for the specified
|
|
/// symbol of each month
|
|
/// </summary>
|
|
/// <param name="symbol">The symbol whose exchange is used to determine the last
|
|
/// tradeable date of the month</param>
|
|
/// <returns>A date rule that fires on the last tradeable date for the specified security each month</returns>
|
|
public IDateRule MonthEnd(Symbol symbol)
|
|
{
|
|
return new FuncDateRule(symbol.Value + ": MonthEnd", (start, end) => MonthEndIterator(GetSecurity(symbol), start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on Monday each week
|
|
/// </summary>
|
|
/// <returns>A date rule that fires on Monday each week</returns>
|
|
public IDateRule WeekStart()
|
|
{
|
|
return new FuncDateRule("WeekStart", (start, end) => WeekStartIterator(null, start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on the first tradeable date for the specified
|
|
/// symbol of each week
|
|
/// </summary>
|
|
/// <param name="symbol">The symbol whose exchange is used to determine the first
|
|
/// tradeable date of the week</param>
|
|
/// <returns>A date rule that fires on the first tradeable date for the specified security each week</returns>
|
|
public IDateRule WeekStart(Symbol symbol)
|
|
{
|
|
return new FuncDateRule(symbol.Value + ": WeekStart", (start, end) => WeekStartIterator(GetSecurity(symbol), start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on Friday each week
|
|
/// </summary>
|
|
/// <returns>A date rule that fires on Friday each week</returns>
|
|
public IDateRule WeekEnd()
|
|
{
|
|
return new FuncDateRule("WeekEnd", (start, end) => WeekEndIterator(null, start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies an event should fire on the last tradeable date for the specified
|
|
/// symbol of each week
|
|
/// </summary>
|
|
/// <param name="symbol">The symbol whose exchange is used to determine the last
|
|
/// tradeable date of the week</param>
|
|
/// <returns>A date rule that fires on the last tradeable date for the specified security each week</returns>
|
|
public IDateRule WeekEnd(Symbol symbol)
|
|
{
|
|
return new FuncDateRule(symbol.Value + ": WeekEnd", (start, end) => WeekEndIterator(GetSecurity(symbol), start, end));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the security with the specified symbol, or throws an exception if the symbol is not found
|
|
/// </summary>
|
|
/// <param name="symbol">The security's symbol to search for</param>
|
|
/// <returns>The security object matching the given symbol</returns>
|
|
private Security GetSecurity(Symbol symbol)
|
|
{
|
|
Security security;
|
|
if (!_securities.TryGetValue(symbol, out security))
|
|
{
|
|
throw new Exception(symbol.Value + " not found in portfolio. Request this data when initializing the algorithm.");
|
|
}
|
|
return security;
|
|
}
|
|
|
|
private static IEnumerable<DateTime> MonthStartIterator(Security security, DateTime start, DateTime end)
|
|
{
|
|
if (security == null)
|
|
{
|
|
foreach (var date in Time.EachDay(start, end))
|
|
{
|
|
// fire on the first of each month
|
|
if (date.Day == 1) yield return date;
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
// start a month back so we can properly resolve the first event (we may have passed it)
|
|
var aMonthBeforeStart = start.AddMonths(-1);
|
|
int lastMonth = aMonthBeforeStart.Month;
|
|
foreach (var date in Time.EachTradeableDay(security, aMonthBeforeStart, end))
|
|
{
|
|
if (date.Month != lastMonth)
|
|
{
|
|
if (date >= start)
|
|
{
|
|
// only emit if the date is on or after the start
|
|
// the date may be before here because we backed up a month
|
|
// to properly resolve the first tradeable date
|
|
yield return date;
|
|
}
|
|
lastMonth = date.Month;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<DateTime> MonthEndIterator(Security security, DateTime start, DateTime end)
|
|
{
|
|
foreach (var date in Time.EachDay(start, end))
|
|
{
|
|
if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))
|
|
{
|
|
if (security == null)
|
|
{
|
|
// fire on the last of each month
|
|
yield return date;
|
|
}
|
|
else
|
|
{
|
|
// find previous date when market is open
|
|
var currentDate = date;
|
|
while (!security.Exchange.Hours.IsDateOpen(currentDate))
|
|
{
|
|
currentDate = currentDate.AddDays(-1);
|
|
}
|
|
yield return currentDate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<DateTime> WeekStartIterator(Security security, DateTime start, DateTime end)
|
|
{
|
|
var skippedMarketClosedDay = false;
|
|
|
|
foreach (var date in Time.EachDay(start, end))
|
|
{
|
|
if (security == null)
|
|
{
|
|
// fire on Monday
|
|
if (date.DayOfWeek == DayOfWeek.Monday)
|
|
{
|
|
yield return date;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// skip Mondays and following days when market is closed
|
|
if (date.DayOfWeek == DayOfWeek.Monday || skippedMarketClosedDay)
|
|
{
|
|
if (security.Exchange.Hours.IsDateOpen(date))
|
|
{
|
|
skippedMarketClosedDay = false;
|
|
yield return date;
|
|
}
|
|
else
|
|
{
|
|
skippedMarketClosedDay = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<DateTime> WeekEndIterator(Security security, DateTime start, DateTime end)
|
|
{
|
|
foreach (var date in Time.EachDay(start, end))
|
|
{
|
|
if (date.DayOfWeek == DayOfWeek.Friday)
|
|
{
|
|
if (security == null)
|
|
{
|
|
// fire on Friday
|
|
yield return date;
|
|
}
|
|
else
|
|
{
|
|
// find previous date when market is open
|
|
var currentDate = date;
|
|
while (!security.Exchange.Hours.IsDateOpen(currentDate))
|
|
{
|
|
currentDate = currentDate.AddDays(-1);
|
|
}
|
|
yield return currentDate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|