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)));
153 if (daysOffset < 0 || 365 < daysOffset)
155 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.YearStart() : Offset must be between 0 and 365");
165 return new FuncDateRule(GetName(symbol,
"YearStart", daysOffset), (start, end) => YearIterator(securityExchangeHours, start, end, daysOffset,
true));
175 return YearEnd(
null, daysOffset);
187 if (daysOffset < 0 || 365 < daysOffset)
189 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.YearEnd() : Offset must be between 0 and 365");
199 return new FuncDateRule(GetName(symbol,
"YearEnd", -daysOffset), (start, end) => YearIterator(securityExchangeHours, start, end, daysOffset,
false));
209 return new FuncDateRule(GetName(
null,
"MonthStart", daysOffset), (start, end) => MonthIterator(
null, start, end, daysOffset,
true));
222 if (daysOffset < 0 || 30 < daysOffset)
224 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.MonthStart() : Offset must be between 0 and 30");
238 return new FuncDateRule(GetName(
null,
"MonthEnd", -daysOffset), (start, end) => MonthIterator(
null, start, end, daysOffset,
false));
250 if (daysOffset < 0 || 30 < daysOffset)
252 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.MonthEnd() : Offset must be between 0 and 30");
267 if (daysOffset < 0 || 6 < daysOffset)
269 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.WeekStart() : Offset must be between 0 and 6");
272 return new FuncDateRule(GetName(
null,
"WeekStart", daysOffset), (start, end) => WeekIterator(
null, start, end, daysOffset,
true));
287 var tradingDays = securitySchedule.MarketHours.Values
288 .Where(x => x.IsClosedAllDay ==
false).OrderBy(x => x.DayOfWeek).ToList();
291 if (daysOffset > tradingDays.Count - 1)
293 throw new ArgumentOutOfRangeException(nameof(daysOffset),
294 $
"DateRules.WeekStart() : {tradingDays.First().DayOfWeek}+{daysOffset} is out of range for {symbol}'s schedule," +
295 $
" please use an offset between 0 - {tradingDays.Count - 1}; Schedule : {string.Join(",
", tradingDays.Select(x => x.DayOfWeek))}");
299 return new FuncDateRule(GetName(symbol,
"WeekStart", daysOffset), (start, end) => WeekIterator(securitySchedule, start, end, daysOffset,
true));
310 if (daysOffset < 0 || 6 < daysOffset)
312 throw new ArgumentOutOfRangeException(nameof(daysOffset),
"DateRules.WeekEnd() : Offset must be between 0 and 6");
315 return new FuncDateRule(GetName(
null,
"WeekEnd", -daysOffset), (start, end) => WeekIterator(
null, start, end, daysOffset,
false));
329 var tradingDays = securitySchedule.MarketHours.Values
330 .Where(x => x.IsClosedAllDay ==
false).OrderBy(x => x.DayOfWeek).ToList();
333 if (daysOffset > tradingDays.Count - 1)
335 throw new ArgumentOutOfRangeException(nameof(daysOffset),
336 $
"DateRules.WeekEnd() : {tradingDays.Last().DayOfWeek}-{daysOffset} is out of range for {symbol}'s schedule," +
337 $
" please use an offset between 0 - {tradingDays.Count - 1}; Schedule : {string.Join(",
", tradingDays.Select(x => x.DayOfWeek))}");
341 return new FuncDateRule(GetName(symbol,
"WeekEnd", -daysOffset), (start, end) => WeekIterator(securitySchedule, start, end, daysOffset,
false));
351 private static string GetName(
Symbol symbol,
string ruleType,
int offset)
354 var offsetString = offset.ToString(
"+#;-#;''", CultureInfo.InvariantCulture);
355 var name = symbol ==
null ? $
"{ruleType}{offsetString}" : $
"{symbol.Value}: {ruleType}{offsetString}";
370 private static DateTime GetScheduledDay(
SecurityExchangeHours securityExchangeHours, DateTime baseDay,
int offset,
bool searchForward, DateTime? boundary =
null)
373 var scheduledDate = baseDay;
376 if (!securityExchangeHours.
IsDateOpen(scheduledDate, extendedMarketHours:
true))
378 scheduledDate = searchForward
384 for (var i = 0; i < offset; i++)
386 scheduledDate = searchForward
392 if (boundary.HasValue)
396 if (searchForward && scheduledDate > boundary)
398 scheduledDate = GetScheduledDay(securityExchangeHours, (DateTime)boundary, 0,
false);
403 if (!searchForward && scheduledDate < boundary)
405 scheduledDate = GetScheduledDay(securityExchangeHours, (DateTime)boundary, 0,
true);
409 return scheduledDate;
412 private static IEnumerable<DateTime> BaseIterator(
418 DateTime periodBegin,
420 Func<DateTime, DateTime> baseDateFunc,
421 Func<DateTime, DateTime> boundaryDateFunc)
424 if (securitySchedule ==
null)
429 foreach (var date
in Time.EachDay(periodBegin, periodEnd))
431 var baseDate = baseDateFunc(date);
432 var boundaryDate = boundaryDateFunc(date);
435 if (date == baseDate)
437 var scheduledDay = GetScheduledDay(securitySchedule, baseDate, offset, searchForward, boundaryDate);
440 if (scheduledDay >= start && scheduledDay <= end)
442 yield
return scheduledDay;
448 private static IEnumerable<DateTime> MonthIterator(
SecurityExchangeHours securitySchedule, DateTime start, DateTime end,
int offset,
bool searchForward)
452 var beginningOfStartMonth =
new DateTime(start.Year, start.Month, 1);
453 var endOfEndMonth =
new DateTime(end.Year, end.Month, DateTime.DaysInMonth(end.Year, end.Month));
457 Func<DateTime, DateTime> baseDateFunc = date => searchForward ?
new DateTime(date.Year, date.Month, 1) : new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
458 Func<DateTime, DateTime> boundaryDateFunc = date => searchForward ?
new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)) : new DateTime(date.Year, date.Month, 1);
460 return BaseIterator(securitySchedule, start, end, offset, searchForward, beginningOfStartMonth, endOfEndMonth, baseDateFunc, boundaryDateFunc);
463 private static IEnumerable<DateTime> YearIterator(
SecurityExchangeHours securitySchedule, DateTime start, DateTime end,
int offset,
bool searchForward)
467 var beginningOfStartOfYear =
new DateTime(start.Year, start.Month, 1);
468 var endOfEndYear =
new DateTime(end.Year, end.Month, DateTime.DaysInMonth(end.Year, end.Month));
472 Func<DateTime, DateTime> baseDateFunc = date => searchForward ?
new DateTime(date.Year, 1, 1) : new DateTime(date.Year, 12, 31);
473 Func<DateTime, DateTime> boundaryDateFunc = date => searchForward ?
new DateTime(date.Year, 12, 31) : new DateTime(date.Year, 1, 1);
475 return BaseIterator(securitySchedule, start, end, offset, searchForward, beginningOfStartOfYear, endOfEndYear, baseDateFunc, boundaryDateFunc);
478 private static IEnumerable<DateTime> WeekIterator(
SecurityExchangeHours securitySchedule, DateTime start, DateTime end,
int offset,
bool searchForward)
481 DayOfWeek weeklyBaseDay;
482 DayOfWeek weeklyBoundaryDay;
483 if (securitySchedule ==
null)
490 weeklyBaseDay = searchForward ? DayOfWeek.Monday : DayOfWeek.Friday;
491 weeklyBoundaryDay = searchForward ? DayOfWeek.Saturday + 1 : DayOfWeek.Sunday - 1;
496 var weeklySchedule = securitySchedule.
MarketHours.Values
497 .Where(x => x.IsClosedAllDay ==
false).OrderBy(x => x.DayOfWeek).ToList();
500 weeklyBaseDay = searchForward ? weeklySchedule.First().DayOfWeek : weeklySchedule.Last().DayOfWeek;
501 weeklyBoundaryDay = searchForward ? weeklySchedule.Last().DayOfWeek : weeklySchedule.First().DayOfWeek;
507 var startAdjustment = start.DayOfWeek == DayOfWeek.Sunday ? -7 : 0;
508 var beginningOfStartWeek = start.AddDays(-(
int)start.DayOfWeek + 1 + startAdjustment);
510 var endAdjustment = end.DayOfWeek == DayOfWeek.Sunday ? -7 : 0;
511 var endOfEndWeek = end.AddDays(-(
int)end.DayOfWeek + 7 + endAdjustment);
514 foreach (var date
in Time.EachDay(beginningOfStartWeek, endOfEndWeek).Where(x => x.DayOfWeek == weeklyBaseDay))
516 var boundary = date.AddDays(weeklyBoundaryDay - weeklyBaseDay);
517 var scheduledDay = GetScheduledDay(securitySchedule, date, offset, searchForward, boundary);
520 if (scheduledDay >= start && scheduledDay <= end)
522 yield
return scheduledDay;