Skip to content

Commit daf9b5a

Browse files
Address review
1 parent ec0d55f commit daf9b5a

File tree

6 files changed

+520
-749
lines changed

6 files changed

+520
-749
lines changed

Common/Securities/Future/FuturesExpiryFunctions.cs

Lines changed: 464 additions & 662 deletions
Large diffs are not rendered by default.

Common/Securities/Future/FuturesExpiryUtilityFunctions.cs

Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,12 @@ public static class FuturesExpiryUtilityFunctions
4343
/// </summary>
4444
/// <param name="market">The market the exchange resides in, i.e, 'usa', 'fxcm', ect...</param>
4545
/// <param name="symbol">The particular symbol being traded</param>s
46-
public static HashSet<DateTime> GetHolidays(string market, string symbol)
46+
internal static HashSet<DateTime> GetExpirationHolidays(string market, string symbol)
4747
{
48-
return MarketHoursDatabase.FromDataFolder()
48+
var exchangeHours = MarketHoursDatabase.FromDataFolder()
4949
.GetEntry(market, symbol, SecurityType.Future)
50-
.ExchangeHours
51-
.Holidays;
52-
}
53-
54-
/// <summary>
55-
/// Get bank holiday list from the MHDB given the market and the symbol of the security
56-
/// </summary>
57-
/// <param name="market">The market the exchange resides in, i.e, 'usa', 'fxcm', ect...</param>
58-
/// <param name="symbol">The particular symbol being traded</param>s
59-
public static HashSet<DateTime> GetBankHolidays(string market, string symbol)
60-
{
61-
return MarketHoursDatabase.FromDataFolder()
62-
.GetEntry(market, symbol, SecurityType.Future)
63-
.ExchangeHours
64-
.BankHolidays;
50+
.ExchangeHours;
51+
return exchangeHours.Holidays.Concat(exchangeHours.BankHolidays).ToHashSet();
6552
}
6653

6754
/// <summary>
@@ -70,19 +57,17 @@ public static HashSet<DateTime> GetBankHolidays(string market, string symbol)
7057
/// <param name="time">The current Time</param>
7158
/// <param name="n">Number of business days succeeding current time. Use negative value for preceding business days</param>
7259
/// <param name="holidays">Set of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
73-
/// /// <param name="bankHolidays">Set of bank holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
7460
/// <returns>The date-time after adding n business days</returns>
75-
public static DateTime AddBusinessDays(DateTime time, int n, HashSet<DateTime> holidays, HashSet<DateTime> bankHolidays)
61+
public static DateTime AddBusinessDays(DateTime time, int n, HashSet<DateTime> holidays)
7662
{
77-
var bankHolidaySet = bankHolidays ?? new HashSet<DateTime>();
7863
if (n < 0)
7964
{
8065
var businessDays = -n;
8166
var totalDays = 1;
8267
do
8368
{
8469
var previousDay = time.AddDays(-totalDays);
85-
if (!holidays.Contains(previousDay.Date) && !bankHolidaySet.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
70+
if (!holidays.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
8671
{
8772
businessDays--;
8873
}
@@ -99,7 +84,7 @@ public static DateTime AddBusinessDays(DateTime time, int n, HashSet<DateTime> h
9984
do
10085
{
10186
var previousDay = time.AddDays(totalDays);
102-
if (!holidays.Contains(previousDay.Date) && !bankHolidaySet.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
87+
if (!holidays.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
10388
{
10489
businessDays--;
10590
}
@@ -117,13 +102,12 @@ public static DateTime AddBusinessDays(DateTime time, int n, HashSet<DateTime> h
117102
/// <param name="time">The current Time</param>
118103
/// <param name="n">Number of business days succeeding current time. Use negative value for preceding business days</param>
119104
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
120-
/// <param name="bankHolidayList">Enumerable of bank holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
121105
/// <returns>The date-time after adding n business days</returns>
122-
public static DateTime AddBusinessDaysIfHoliday(DateTime time, int n, HashSet<DateTime> holidayList, HashSet<DateTime> bankHolidayList)
106+
public static DateTime AddBusinessDaysIfHoliday(DateTime time, int n, HashSet<DateTime> holidayList)
123107
{
124108
if (holidayList.Contains(time))
125109
{
126-
return AddBusinessDays(time, n, holidayList, bankHolidayList);
110+
return AddBusinessDays(time, n, holidayList);
127111
}
128112
else
129113
{
@@ -137,14 +121,12 @@ public static DateTime AddBusinessDaysIfHoliday(DateTime time, int n, HashSet<Da
137121
/// <param name="time">DateTime for delivery month</param>
138122
/// <param name="n">Number of days</param>
139123
/// <param name="holidayList">Holidays to use while calculating n^th business day. Useful for MHDB entries</param>
140-
/// <param name="bankHolidayList">Bank holidays to use while calculating n^th business day. Useful for MHDB entries</param>
141124
/// <returns>Nth Last Business day of the month</returns>
142-
public static DateTime NthLastBusinessDay(DateTime time, int n, IEnumerable<DateTime> holidayList, IEnumerable<DateTime> bankHolidayList)
125+
public static DateTime NthLastBusinessDay(DateTime time, int n, IEnumerable<DateTime> holidayList)
143126
{
144127
var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
145128
var lastDayOfMonth = new DateTime(time.Year, time.Month, daysInMonth);
146129
var holidays = holidayList.Select(x => x.Date);
147-
var bankHolidays = (bankHolidayList ?? Enumerable.Empty<DateTime>()).Select(x => x.Date);
148130

149131
if(n > daysInMonth)
150132
{
@@ -158,7 +140,7 @@ public static DateTime NthLastBusinessDay(DateTime time, int n, IEnumerable<Date
158140
do
159141
{
160142
var previousDay = lastDayOfMonth.AddDays(-totalDays);
161-
if (NotHoliday(previousDay, holidays, bankHolidays) && !holidays.Contains(previousDay) && !bankHolidays.Contains(previousDay))
143+
if (NotHoliday(previousDay, holidays) && !holidays.Contains(previousDay))
162144
{
163145
businessDays--;
164146
}
@@ -169,19 +151,16 @@ public static DateTime NthLastBusinessDay(DateTime time, int n, IEnumerable<Date
169151
}
170152

171153
/// <summary>
172-
/// Calculates the n^th business day of the month (includes checking for holidays and bankHolidays)
154+
/// Calculates the n^th business day of the month (includes checking for holidays)
173155
/// </summary>
174156
/// <param name="time">Month to calculate business day for</param>
175157
/// <param name="nthBusinessDay">n^th business day to get</param>
176158
/// <param name="holidayList"> Holidays to not count as business days</param>
177-
/// <param name="bankHolidayList"> Bank holidays to not count as business days</param>
178159
/// <returns>Nth business day of the month</returns>
179-
public static DateTime NthBusinessDay(DateTime time, int nthBusinessDay, IEnumerable<DateTime> holidayList, IEnumerable<DateTime> bankHolidayList)
160+
public static DateTime NthBusinessDay(DateTime time, int nthBusinessDay, IEnumerable<DateTime> holidayList)
180161
{
181162
var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
182163
var holidays = holidayList.Select(x => x.Date);
183-
var bankHolidays = (bankHolidayList ?? Enumerable.Empty<DateTime>()).Select(x => x.Date);
184-
185164
if (nthBusinessDay > daysInMonth)
186165
{
187166
throw new ArgumentOutOfRangeException(Invariant(
@@ -202,12 +181,12 @@ public static DateTime NthBusinessDay(DateTime time, int nthBusinessDay, IEnumer
202181

203182
// Check for holiday up here in case we want the first business day and it is a holiday so that we don't skip over it.
204183
// We also want to make sure that we don't stop on a weekend.
205-
while (daysCounted < nthBusinessDay || holidays.Contains(calculatedTime) || bankHolidays.Contains(calculatedTime) || !calculatedTime.IsCommonBusinessDay())
184+
while (daysCounted < nthBusinessDay || holidays.Contains(calculatedTime) || !calculatedTime.IsCommonBusinessDay())
206185
{
207186
// The asset continues trading on days contained within `USHoliday.Dates`, but
208187
// the last trade date is affected by those holidays. We check for
209188
// both MHDB entries and holidays to get accurate business days
210-
if (holidays.Contains(calculatedTime) || bankHolidays.Contains(calculatedTime))
189+
if (holidays.Contains(calculatedTime))
211190
{
212191
// Catches edge case where first day is on a friday
213192
if (i == 0 && calculatedTime.DayOfWeek == DayOfWeek.Friday)
@@ -227,7 +206,7 @@ public static DateTime NthBusinessDay(DateTime time, int nthBusinessDay, IEnumer
227206

228207
calculatedTime = calculatedTime.AddDays(1);
229208

230-
if (!holidays.Contains(calculatedTime) && !bankHolidays.Contains(calculatedTime) && NotHoliday(calculatedTime, holidays, bankHolidays))
209+
if (!holidays.Contains(calculatedTime) && NotHoliday(calculatedTime, holidays))
231210
{
232211
daysCounted++;
233212
}
@@ -321,21 +300,19 @@ public static DateTime LastWeekday(DateTime time, DayOfWeek dayOfWeek)
321300
/// </summary>
322301
/// <param name="time">The DateTime for consideration</param>
323302
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
324-
/// <param name="bankHolidayList">Enumerable of bank holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
325303
/// <returns>True if the time is not a holidays, otherwise returns false</returns>
326-
public static bool NotHoliday(DateTime time, IEnumerable<DateTime> holidayList, IEnumerable<DateTime> bankHolidayList)
304+
public static bool NotHoliday(DateTime time, IEnumerable<DateTime> holidayList)
327305
{
328-
return time.IsCommonBusinessDay() && !holidayList.Contains(time.Date) && (bankHolidayList == null || !bankHolidayList.Contains(time.Date));
306+
return time.IsCommonBusinessDay() && !holidayList.Contains(time.Date);
329307
}
330308

331309
/// <summary>
332310
/// This function takes Thursday as input and returns true if four weekdays preceding it are not Holidays
333311
/// </summary>
334312
/// <param name="thursday">DateTime of a given Thursday</param>
335313
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
336-
/// <param name="bankHolidayList">Enumerable of bank holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
337314
/// <returns>False if DayOfWeek is not Thursday or is not preceded by four weekdays,Otherwise returns True</returns>
338-
public static bool NotPrecededByHoliday(DateTime thursday, IEnumerable<DateTime> holidayList, IEnumerable<DateTime> bankHolidayList)
315+
public static bool NotPrecededByHoliday(DateTime thursday, IEnumerable<DateTime> holidayList)
339316
{
340317
if (thursday.DayOfWeek != DayOfWeek.Thursday)
341318
{
@@ -345,13 +322,13 @@ public static bool NotPrecededByHoliday(DateTime thursday, IEnumerable<DateTime>
345322
// for Monday, Tuesday and Wednesday
346323
for (var i = 1; i <= 3; i++)
347324
{
348-
if (!NotHoliday(thursday.AddDays(-i), holidayList, bankHolidayList))
325+
if (!NotHoliday(thursday.AddDays(-i), holidayList))
349326
{
350327
result = false;
351328
}
352329
}
353330
// for Friday
354-
if (!NotHoliday(thursday.AddDays(-6), holidayList, bankHolidayList))
331+
if (!NotHoliday(thursday.AddDays(-6), holidayList))
355332
{
356333
result = false;
357334
}
@@ -363,10 +340,9 @@ public static bool NotPrecededByHoliday(DateTime thursday, IEnumerable<DateTime>
363340
/// </summary>
364341
/// <param name="time">Contract month</param>
365342
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
366-
/// <param name="bankHolidayList">Enumerable of bank holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
367343
/// <param name="lastTradeTime">Time at which the dairy future contract stops trading (usually should be on 17:10:00 UTC)</param>
368344
/// <returns></returns>
369-
public static DateTime DairyLastTradeDate(DateTime time, IEnumerable<DateTime> holidayList, IEnumerable<DateTime> bankHolidayList, TimeSpan? lastTradeTime = null)
345+
public static DateTime DairyLastTradeDate(DateTime time, IEnumerable<DateTime> holidayList, TimeSpan? lastTradeTime = null)
370346
{
371347
// Trading shall terminate on the business day immediately preceding the day on which the USDA announces the <DAIRY_PRODUCT> price for that contract month. (LTD 12:10 p.m.)
372348
var contractMonth = new DateTime(time.Year, time.Month, 1);
@@ -378,7 +354,7 @@ public static DateTime DairyLastTradeDate(DateTime time, IEnumerable<DateTime> h
378354
{
379355
publicationDate = publicationDate.AddDays(-1);
380356
}
381-
while (holidayList.Contains(publicationDate) || (bankHolidayList != null && bankHolidayList.Contains(publicationDate)) || publicationDate.DayOfWeek == DayOfWeek.Saturday);
357+
while (holidayList.Contains(publicationDate) || publicationDate.DayOfWeek == DayOfWeek.Saturday);
382358
}
383359
else
384360
{

Common/Securities/FutureOption/FuturesOptionsExpiryFunctions.cs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,9 @@ public static class FuturesOptionsExpiryFunctions
4646
// Trading terminates 7 business days before the 26th calendar of the month prior to the contract month. https://www.cmegroup.com/trading/energy/crude-oil/light-sweet-crude_contractSpecs_options.html#optionProductId=190
4747
{_lo, expiryMonth => {
4848
var twentySixthDayOfPreviousMonthFromContractMonth = expiryMonth.AddMonths(-1).AddDays(-(expiryMonth.Day - 1)).AddDays(25);
49-
var holidays = _mhdb.GetEntry(_lo.ID.Market, _lo.Underlying, SecurityType.Future)
50-
.ExchangeHours
51-
.Holidays;
52-
var bankHolidays = _mhdb.GetEntry(_lo.ID.Market, _lo.Underlying, SecurityType.Future)
53-
.ExchangeHours
54-
.BankHolidays;
55-
56-
return FuturesExpiryUtilityFunctions.AddBusinessDays(twentySixthDayOfPreviousMonthFromContractMonth, -7, holidays, bankHolidays);
49+
var holidays = FuturesExpiryUtilityFunctions.GetExpirationHolidays(_lo.ID.Market, _lo.Underlying.ID.Symbol);
50+
51+
return FuturesExpiryUtilityFunctions.AddBusinessDays(twentySixthDayOfPreviousMonthFromContractMonth, -7, holidays);
5752
}},
5853
// Trading terminates on the 4th last business day of the month prior to the contract month (1 business day prior to the expiration of the underlying futures corresponding contract month).
5954
// https://www.cmegroup.com/trading/energy/natural-gas/natural-gas_contractSpecs_options.html
@@ -137,22 +132,17 @@ public static DateTime GetFutureOptionExpiryFromFutureExpiry(Symbol futureSymbol
137132
/// <returns>Expiry DateTime of the Future Option</returns>
138133
private static DateTime FridayBeforeTwoBusinessDaysBeforeEndOfMonth(Symbol underlyingFuture, DateTime expiryMonth)
139134
{
140-
var holidays = _mhdb.GetEntry(underlyingFuture.ID.Market, underlyingFuture, SecurityType.Future)
141-
.ExchangeHours
142-
.Holidays;
143-
var bankHolidays = _mhdb.GetEntry(underlyingFuture.ID.Market, underlyingFuture, SecurityType.Future)
144-
.ExchangeHours
145-
.BankHolidays;
135+
var holidays = FuturesExpiryUtilityFunctions.GetExpirationHolidays(underlyingFuture.ID.Market, underlyingFuture.ID.Symbol);
146136

147137
var expiryMonthPreceding = expiryMonth.AddMonths(-1).AddDays(-(expiryMonth.Day - 1));
148138
var fridayBeforeSecondLastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(
149139
expiryMonthPreceding,
150140
2,
151-
holidayList: holidays, bankHolidays).AddDays(-1);
141+
holidays).AddDays(-1);
152142

153143
while (fridayBeforeSecondLastBusinessDay.DayOfWeek != DayOfWeek.Friday)
154144
{
155-
fridayBeforeSecondLastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(fridayBeforeSecondLastBusinessDay, -1, holidays, bankHolidays);
145+
fridayBeforeSecondLastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(fridayBeforeSecondLastBusinessDay, -1, holidays);
156146
}
157147

158148
return fridayBeforeSecondLastBusinessDay;
@@ -171,21 +161,16 @@ private static DateTime FridayBeforeTwoBusinessDaysBeforeEndOfMonth(Symbol under
171161
/// <returns>Expiry DateTime of the Future Option</returns>
172162
private static DateTime FourthLastBusinessDayInPrecedingMonthFromContractMonth(Symbol underlyingFuture, DateTime expiryMonth, int hour, int minutes, bool noFridays = true)
173163
{
174-
var holidays = _mhdb.GetEntry(underlyingFuture.ID.Market, underlyingFuture, SecurityType.Future)
175-
.ExchangeHours
176-
.Holidays;
177-
var bankHolidays = _mhdb.GetEntry(underlyingFuture.ID.Market, underlyingFuture, SecurityType.Future)
178-
.ExchangeHours
179-
.BankHolidays;
164+
var holidays = FuturesExpiryUtilityFunctions.GetExpirationHolidays(underlyingFuture.ID.Market, underlyingFuture.ID.Symbol);
180165

181166
var expiryMonthPreceding = expiryMonth.AddMonths(-1);
182-
var fourthLastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(expiryMonthPreceding, 4, holidayList: holidays, bankHolidays);
167+
var fourthLastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(expiryMonthPreceding, 4, holidays);
183168

184169
if (noFridays)
185170
{
186171
while (fourthLastBusinessDay.DayOfWeek == DayOfWeek.Friday || holidays.Contains(fourthLastBusinessDay.AddDays(1)))
187172
{
188-
fourthLastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(fourthLastBusinessDay, -1, holidays, bankHolidays);
173+
fourthLastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(fourthLastBusinessDay, -1, holidays);
189174
}
190175
}
191176

Common/Securities/SecurityExchangeHours.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public HashSet<DateTime> Holidays
7272
/// <summary>
7373
/// Gets the bank holidays for the exchange
7474
/// </summary>
75+
/// <remarks>In some markets and assets, like CME futures, there are tradable dates (market open) which
76+
/// should not be considered for expiration rules due to banks being closed</remarks>
7577
public HashSet<DateTime> BankHolidays
7678
{
7779
get { return _bankHolidays.ToHashSet(x => new DateTime(x)); }

0 commit comments

Comments
 (0)