20 using System.Globalization;
22 using System.Collections.Generic;
38 : base(securities, timeZone, marketHoursDatabase)
61 var dates =
new[] {
new DateTime(year, month, day)};
62 return new FuncDateRule(
string.Join(
",", dates.Select(x => x.ToShortDateString())), (start, end) => dates.Where(x => x >= start && x <= end));
72 dates = dates.Select(x => x.Date).ToArray();
73 return new FuncDateRule(
string.Join(
",", dates.Select(x => x.ToShortDateString())), (start, end) => dates.Where(x => x >= start && x <= end));
108 var hash = days.ToHashSet();
109 return new FuncDateRule(
string.Join(
",", days), (start, end) =>
Time.
EachDay(start, end).Where(date => hash.Contains(date.DayOfWeek)));
139 return new FuncDateRule(GetName(
null,
"MonthStart", daysOffset), (start, end) => MonthIterator(
null, start, end, daysOffset,
true));
152 if (daysOffset < 0 || 30 < daysOffset)
154 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.MonthStart() : Offset must be between 0 and 30");
168 return new FuncDateRule(GetName(
null,
"MonthEnd", -daysOffset), (start, end) => MonthIterator(
null, start, end, daysOffset,
false));
180 if (daysOffset < 0 || 30 < daysOffset)
182 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.MonthEnd() : Offset must be between 0 and 30");
197 if (daysOffset < 0 || 6 < daysOffset)
199 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.WeekStart() : Offset must be between 0 and 6");
202 return new FuncDateRule(GetName(
null,
"WeekStart", daysOffset), (start, end) => WeekIterator(
null, start, end, daysOffset,
true));
217 var tradingDays = securitySchedule.MarketHours.Values
218 .Where(x => x.IsClosedAllDay ==
false).OrderBy(x => x.DayOfWeek).ToList();
221 if (daysOffset > tradingDays.Count - 1)
223 throw new ArgumentOutOfRangeException(nameof(daysOffset),
224 $
"DateRules.WeekStart() : {tradingDays.First().DayOfWeek}+{daysOffset} is out of range for {symbol}'s schedule," +
225 $
" please use an offset between 0 - {tradingDays.Count - 1}; Schedule : {string.Join(",
", tradingDays.Select(x => x.DayOfWeek))}");
229 return new FuncDateRule(GetName(symbol,
"WeekStart", daysOffset), (start, end) => WeekIterator(securitySchedule, start, end, daysOffset,
true));
240 if (daysOffset < 0 || 6 < daysOffset)
242 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.WeekEnd() : Offset must be between 0 and 6");
245 return new FuncDateRule(GetName(
null,
"WeekEnd", -daysOffset), (start, end) => WeekIterator(
null, start, end, daysOffset,
false));
259 var tradingDays = securitySchedule.MarketHours.Values
260 .Where(x => x.IsClosedAllDay ==
false).OrderBy(x => x.DayOfWeek).ToList();
263 if (daysOffset > tradingDays.Count - 1)
265 throw new ArgumentOutOfRangeException(nameof(daysOffset),
266 $
"DateRules.WeekEnd() : {tradingDays.Last().DayOfWeek}-{daysOffset} is out of range for {symbol}'s schedule," +
267 $
" please use an offset between 0 - {tradingDays.Count - 1}; Schedule : {string.Join(",
", tradingDays.Select(x => x.DayOfWeek))}");
271 return new FuncDateRule(GetName(symbol,
"WeekEnd", -daysOffset), (start, end) => WeekIterator(securitySchedule, start, end, daysOffset,
false));
281 private static string GetName(
Symbol symbol,
string ruleType,
int offset)
284 var offsetString = offset.ToString(
"+#;-#;''", CultureInfo.InvariantCulture);
285 var name = symbol ==
null ? $
"{ruleType}{offsetString}" : $
"{symbol.Value}: {ruleType}{offsetString}";
300 private static DateTime GetScheduledDay(
SecurityExchangeHours securityExchangeHours, DateTime baseDay,
int offset,
bool searchForward, DateTime? boundary =
null)
303 var scheduledDate = baseDay;
306 if (!securityExchangeHours.
IsDateOpen(scheduledDate))
308 scheduledDate = searchForward
314 for (var i = 0; i < offset; i++)
316 scheduledDate = searchForward
322 if (boundary.HasValue)
326 if (searchForward && scheduledDate > boundary)
328 scheduledDate = GetScheduledDay(securityExchangeHours, (DateTime)boundary, 0,
false);
333 if (!searchForward && scheduledDate < boundary)
335 scheduledDate = GetScheduledDay(securityExchangeHours, (DateTime)boundary, 0,
true);
339 return scheduledDate;
342 private static IEnumerable<DateTime> MonthIterator(
SecurityExchangeHours securitySchedule, DateTime start, DateTime end,
int offset,
bool searchForward)
345 if (securitySchedule ==
null)
352 var beginningOfStartMonth =
new DateTime(start.Year, start.Month, 1);
353 var endOfEndMonth =
new DateTime(end.Year, end.Month, DateTime.DaysInMonth(end.Year, end.Month));
355 foreach (var date
in Time.EachDay(beginningOfStartMonth, endOfEndMonth))
357 var daysInMonth = DateTime.DaysInMonth(date.Year, date.Month);
361 var baseDate = searchForward?
new DateTime(date.Year, date.Month, 1) : new DateTime(date.Year, date.Month, daysInMonth);
362 var boundaryDate = searchForward ?
new DateTime(date.Year, date.Month, daysInMonth) : new DateTime(date.Year, date.Month, 1);
365 if (date == baseDate)
367 var scheduledDay = GetScheduledDay(securitySchedule, baseDate, offset, searchForward, boundaryDate);
370 if (scheduledDay >= start && scheduledDay <= end)
372 yield
return scheduledDay;
378 private static IEnumerable<DateTime> WeekIterator(
SecurityExchangeHours securitySchedule, DateTime start, DateTime end,
int offset,
bool searchForward)
381 DayOfWeek weeklyBaseDay;
382 DayOfWeek weeklyBoundaryDay;
383 if (securitySchedule ==
null)
390 weeklyBaseDay = searchForward ? DayOfWeek.Monday : DayOfWeek.Friday;
391 weeklyBoundaryDay = searchForward ? DayOfWeek.Saturday + 1 : DayOfWeek.Sunday - 1;
396 var weeklySchedule = securitySchedule.
MarketHours.Values
397 .Where(x => x.IsClosedAllDay ==
false).OrderBy(x => x.DayOfWeek).ToList();
400 weeklyBaseDay = searchForward ? weeklySchedule.First().DayOfWeek : weeklySchedule.Last().DayOfWeek;
401 weeklyBoundaryDay = searchForward ? weeklySchedule.Last().DayOfWeek : weeklySchedule.First().DayOfWeek;
407 var startAdjustment = start.DayOfWeek == DayOfWeek.Sunday ? -7 : 0;
408 var beginningOfStartWeek = start.AddDays(-(
int)start.DayOfWeek + 1 + startAdjustment);
410 var endAdjustment = end.DayOfWeek == DayOfWeek.Sunday ? -7 : 0;
411 var endOfEndWeek = end.AddDays(-(
int)end.DayOfWeek + 7 + endAdjustment);
414 foreach (var date
in Time.EachDay(beginningOfStartWeek, endOfEndWeek).Where(x => x.DayOfWeek == weeklyBaseDay))
416 var boundary = date.AddDays(weeklyBoundaryDay - weeklyBaseDay);
417 var scheduledDay = GetScheduledDay(securitySchedule, date, offset, searchForward, boundary);
420 if (scheduledDay >= start && scheduledDay <= end)
422 yield
return scheduledDay;