17 using System.Collections.Generic;
29 .ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
31 private static readonly HashSet<string> _dairyUnderlying =
new HashSet<string>
51 internal static HashSet<DateTime> GetExpirationHolidays(
string market,
string symbol)
58 return exchangeHours.
Holidays.Concat(exchangeHours.BankHolidays).ToHashSet();
60 return exchangeHours.Holidays;
70 public static DateTime
AddBusinessDays(DateTime time,
int n, HashSet<DateTime> holidays)
74 var businessDays = -n;
78 var previousDay = time.AddDays(-totalDays);
79 if (!holidays.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
84 if (businessDays > 0) totalDays++;
85 }
while (businessDays > 0);
87 return time.AddDays(-totalDays);
95 var previousDay = time.AddDays(totalDays);
96 if (!holidays.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
101 if (businessDays > 0) totalDays++;
102 }
while (businessDays > 0);
104 return time.AddDays(totalDays);
117 if (holidayList.Contains(time))
136 var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
137 var lastDayOfMonth =
new DateTime(time.Year, time.Month, daysInMonth);
138 var holidays = holidayList.Select(x => x.Date);
142 throw new ArgumentOutOfRangeException(nameof(n), Invariant(
143 $
"Number of days ({n}) is larger than the size of month({daysInMonth})"
147 var businessDays = n;
151 var previousDay = lastDayOfMonth.AddDays(-totalDays);
152 if (
NotHoliday(previousDay, holidays) && !holidays.Contains(previousDay))
156 if (businessDays > 0) totalDays++;
157 }
while (businessDays > 0);
159 return lastDayOfMonth.AddDays(-totalDays);
169 public static DateTime
NthBusinessDay(DateTime time,
int nthBusinessDay, IEnumerable<DateTime> holidayList)
171 var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
172 var holidays = holidayList.Select(x => x.Date);
173 if (nthBusinessDay > daysInMonth)
175 throw new ArgumentOutOfRangeException(Invariant(
176 $
"Argument nthBusinessDay (${nthBusinessDay}) is larger than the amount of days in the current month (${daysInMonth})"
179 if (nthBusinessDay < 1)
181 throw new ArgumentOutOfRangeException(Invariant(
182 $
"Argument nthBusinessDay (${nthBusinessDay}) is less than one. Provide a number greater than one and less than the days in month"
186 var calculatedTime =
new DateTime(time.Year, time.Month, 1);
188 var daysCounted = calculatedTime.IsCommonBusinessDay() ? 1 : 0;
193 while (daysCounted < nthBusinessDay || holidays.Contains(calculatedTime) || !calculatedTime.IsCommonBusinessDay())
198 if (holidays.Contains(calculatedTime))
201 if (i == 0 && calculatedTime.DayOfWeek == DayOfWeek.Friday)
206 calculatedTime = calculatedTime.AddDays(1);
208 if (i != 0 && calculatedTime.IsCommonBusinessDay())
216 calculatedTime = calculatedTime.AddDays(1);
218 if (!holidays.Contains(calculatedTime) &&
NotHoliday(calculatedTime, holidays))
225 return calculatedTime;
248 public static DateTime
NthFriday(DateTime time,
int n) =>
NthWeekday(time, n, DayOfWeek.Friday);
264 public static DateTime
NthWeekday(DateTime time,
int n, DayOfWeek dayOfWeek)
268 throw new ArgumentOutOfRangeException(nameof(n),
"'n' lower than 1 or greater than 5");
271 var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
272 return (from day in Enumerable.Range(1, daysInMonth)
273 where
new DateTime(time.Year, time.Month, day).DayOfWeek == dayOfWeek
274 select
new DateTime(time.Year, time.Month, day)).ElementAt(n - 1);
284 public static DateTime
LastWeekday(DateTime time, DayOfWeek dayOfWeek)
287 var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
288 return (from day in Enumerable.Range(1, daysInMonth).Reverse()
289 where
new DateTime(time.Year, time.Month, day).DayOfWeek == dayOfWeek
290 select
new DateTime(time.Year, time.Month, day)).First();
313 public static bool NotHoliday(DateTime time, IEnumerable<DateTime> holidayList)
315 return time.IsCommonBusinessDay() && !holidayList.Contains(time.Date);
326 if (thursday.DayOfWeek != DayOfWeek.Thursday)
328 throw new ArgumentException(
"Input to NotPrecededByHolidays must be a Thursday");
332 for (var i = 1; i <= 3; i++)
334 if (!
NotHoliday(thursday.AddDays(-i), holidayList))
340 if (!
NotHoliday(thursday.AddDays(-6), holidayList))
354 public static DateTime
DairyLastTradeDate(DateTime time, IEnumerable<DateTime> holidayList, TimeSpan? lastTradeTime =
null)
357 var contractMonth =
new DateTime(time.Year, time.Month, 1);
358 var lastTradeTs = lastTradeTime ??
new TimeSpan(17, 10, 0);
364 publicationDate = publicationDate.AddDays(-1);
366 while (holidayList.Contains(publicationDate) || publicationDate.DayOfWeek == DayOfWeek.Saturday);
370 publicationDate = contractMonth.AddMonths(1);
377 return publicationDate.Add(lastTradeTs);
388 if (futureExpiryDate !=
null && _dairyUnderlying.Contains(underlying))
391 var dairyReportDate = futureExpiryDate.Value.Date.AddDays(1);
392 if (_reverseDairyReportDates.ContainsKey(dairyReportDate))
394 var contractMonth = _reverseDairyReportDates[dairyReportDate];
396 return ((contractMonth.Year - dairyReportDate.Year) * 12) + contractMonth.Month - dairyReportDate.Month;
402 return ExpiriesPriorMonth.TryGetValue(underlying, out
int value) ? value : 0;
418 int h = (c - c / 4 - (8 * c + 13) / 25 + 19 * g + 15) % 30;
419 int i = h - h / 28 * (1 - h / 28 * (29 / (h + 1)) * ((21 - g) / 11));
421 int day = i - (year + year / 4 + i + 2 - c + c / 4) % 7 + 28;
430 return new DateTime(year, month, day).AddDays(-2);
433 private static readonly Dictionary<string, int> ExpiriesPriorMonth =
new Dictionary<string, int>
437 { Futures.Energy.BrentCrude, 2 },
438 { Futures.Energy.BrentLastDayFinancial, 2 },
439 { Futures.Energy.CrudeOilWTI, 1 },
440 { Futures.Energy.MicroCrudeOilWTI, 1 },
441 { Futures.Energy.Gasoline, 1 },
442 { Futures.Energy.HeatingOil, 1 },
443 { Futures.Energy.MarsArgusVsWTITradeMonth, 1 },
444 { Futures.Energy.NaturalGas, 1 },
445 { Futures.Energy.NaturalGasHenryHubLastDayFinancial, 1 },
446 { Futures.Energy.NaturalGasHenryHubPenultimateFinancial, 1 },
447 { Futures.Energy.WTIHoustonArgusVsWTITradeMonth, 1 },
448 { Futures.Energy.WTIHoustonCrudeOil, 1 },
449 { Futures.Softs.Sugar11, 1 },
450 { Futures.Softs.Sugar11CME, 1 }