Skip to content

Commit a70413f

Browse files
committed
Create testCases for earlyClose and lateOpen
1 parent 51e7cd1 commit a70413f

3 files changed

Lines changed: 135 additions & 31 deletions

File tree

Common/Securities/SecurityExchangeHours.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,6 @@ public bool IsOpen(DateTime localDateTime, bool extendedMarketHours)
187187
[MethodImpl(MethodImplOptions.AggressiveInlining)]
188188
public bool IsOpen(DateTime startLocalDateTime, DateTime endLocalDateTime, bool extendedMarketHours)
189189
{
190-
var hasLateOpen = _lateOpens.TryGetValue(startLocalDateTime.Date, out var lateOpenTime);
191-
if (hasLateOpen && lateOpenTime > endLocalDateTime.TimeOfDay)
192-
{
193-
// if the late open is after the end time, we're closed
194-
return false;
195-
}
196-
197190
if (startLocalDateTime == endLocalDateTime)
198191
{
199192
// if we're testing an instantaneous moment, use the other function

Data/market-hours/market-hours-database.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6131,7 +6131,6 @@
61316131
"1/20/2020": "17:00:00",
61326132
"2/17/2020": "17:00:00",
61336133
"5/25/2020": "17:00:00",
6134-
"7/3/2020": "17:00:00",
61356134
"9/7/2020": "17:00:00",
61366135
"11/26/2020": "17:00:00",
61376136
"1/18/2021": "17:00:00",

Tests/Common/Securities/SecurityExchangeHoursTests.cs

Lines changed: 135 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -575,24 +575,6 @@ public void RegularMarketDurationIsFromMostCommonLocalMarketHours()
575575
Assert.AreEqual(TimeSpan.FromHours(5), exchangeHours.RegularMarketDuration);
576576
}
577577

578-
[Test]
579-
public void IsOpenDuringBarIsFalseWhenLateOpenIsGreaterThanMarketHourEndTime()
580-
{
581-
// Create exchange hours for future 6J contract, with late open at 5pm on 7/3/2020
582-
var exchangeHours = CreateFuture6JExchangeHours();
583-
var exchange = new SecurityExchange(exchangeHours);
584-
585-
// Define the bar start and end times for July 3, 2020.
586-
// Regular market hours: from 8:30 AM to 4:00 PM
587-
DateTime barStartTime = new DateTime(2020, 7, 3, 8, 30, 0);
588-
DateTime bartEndTime = new DateTime(2020, 7, 3, 16, 0, 0);
589-
var _isExtendedMarketHours = false;
590-
591-
// Check if the exchange is open during the bar
592-
var isOpen = exchange.IsOpenDuringBar(barStartTime, bartEndTime, _isExtendedMarketHours);
593-
Assert.IsFalse(isOpen);
594-
}
595-
596578
[Test]
597579
public void FillForwardDoesNotOccurOnLateOpenDates()
598580
{
@@ -610,12 +592,12 @@ public void FillForwardDoesNotOccurOnLateOpenDates()
610592
new TradeBar { Time = new DateTime(2020, 7, 6, 8, 30, 0), EndTime = new DateTime(2020, 7, 6, 16, 0, 0), Value = 1, Volume = 100},
611593
}.GetEnumerator();
612594

613-
var exchangeHours = CreateFuture6JExchangeHours();
595+
var closeDate = new DateTime(2020, 7, 3);
596+
var exchangeHours = CreateFuture6JExchangeHours(new DateTime(), closeDate);
614597
var exchange = new SecurityExchange(exchangeHours);
615598
using var fillForwardEnumerator = new FillForwardEnumerator(enumerator, exchange, Ref.Create(fillForwardResolution), false, subscriptionEndTime, dataResolution, exchange.TimeZone, true);
616599

617600
// Date to check for late open
618-
var closeDate = new DateTime(2020, 7, 3);
619601
int dataCount = 0;
620602

621603
// Set to store unique dates
@@ -639,7 +621,134 @@ public void FillForwardDoesNotOccurOnLateOpenDates()
639621
Assert.AreEqual(dataCount, uniqueDates.Count);
640622
}
641623

642-
public static SecurityExchangeHours CreateFuture6JExchangeHours()
624+
[TestCaseSource(nameof(GetTestCases))]
625+
public void GetMarketHoursWorksCorrectly(DateTime earlyClose, DateTime lateOpen, LocalMarketHours expected)
626+
{
627+
var testDate = new DateTime(2020, 7, 3); // Friday
628+
var exchangeHours = CreateFuture6JExchangeHours(earlyClose, lateOpen);
629+
var actual = exchangeHours.GetMarketHours(testDate);
630+
631+
// Extracts the time segments for detailed comparison
632+
var actualSegments = actual.Segments;
633+
var expectedSegments = expected.Segments;
634+
635+
// Must have the same number of segments
636+
Assert.AreEqual(expectedSegments.Count, actualSegments.Count);
637+
638+
// 1. Market State (PreMarket/Market/PostMarket/Closed)
639+
// 2. Start Time (Validates late open adjustments)
640+
// 3. End Time (Validates early close adjustments)
641+
for (int i = 0; i < expectedSegments.Count; i++)
642+
{
643+
Assert.AreEqual(expectedSegments[i].State, actualSegments[i].State, $"Segment {i} state mismatch");
644+
Assert.AreEqual(expectedSegments[i].Start, actualSegments[i].Start, $"Segment {i} start time mismatch");
645+
Assert.AreEqual(expectedSegments[i].End, actualSegments[i].End, $"Segment {i} end time mismatch");
646+
}
647+
}
648+
649+
private static TestCaseData[] GetTestCases()
650+
{
651+
return new[]
652+
{
653+
// 1. Regular hours (no early close, no late open)
654+
new TestCaseData(
655+
new DateTime(), // No early close
656+
new DateTime(), // No late open
657+
new LocalMarketHours(DayOfWeek.Friday,
658+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(0, 0, 0), new TimeSpan(8, 30, 0)),
659+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(8, 30, 0), new TimeSpan(16, 0, 0)))
660+
),
661+
662+
// 2. Early close only scenarios
663+
// 2.1 Early close during regular market hours
664+
new TestCaseData(
665+
new DateTime(2020, 7, 3, 12, 0, 0), // Early close at noon
666+
new DateTime(),
667+
new LocalMarketHours(DayOfWeek.Friday,
668+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(0, 0, 0), new TimeSpan(8, 30, 0)),
669+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(8, 30, 0), new TimeSpan(12, 0, 0)))
670+
),
671+
// 2.2 Early close before market opens (should have no effect)
672+
new TestCaseData(
673+
new DateTime(2020, 7, 3, 7, 0, 0),
674+
new DateTime(),
675+
new LocalMarketHours(DayOfWeek.Friday,
676+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(0, 0, 0), new TimeSpan(7, 0, 0)))
677+
),
678+
// 2.3 Early close after market closes (should have no effect)
679+
new TestCaseData(
680+
new DateTime(2020, 7, 3, 17, 0, 0),
681+
new DateTime(),
682+
new LocalMarketHours(DayOfWeek.Friday,
683+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(0, 0, 0), new TimeSpan(8, 30, 0)),
684+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(8, 30, 0), new TimeSpan(16, 0, 0)))
685+
),
686+
687+
// 3. Late open only scenarios
688+
// 3.1 Late open during regular market hours
689+
new TestCaseData(
690+
new DateTime(),
691+
new DateTime(2020, 7, 3, 10, 0, 0), // Late open at 10am
692+
new LocalMarketHours(DayOfWeek.Friday,
693+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(10, 0, 0), new TimeSpan(16, 0, 0)))
694+
),
695+
// 3.2 Late open before market opens (should adjust pre-market)
696+
new TestCaseData(
697+
new DateTime(),
698+
new DateTime(2020, 7, 3, 7, 0, 0),
699+
new LocalMarketHours(DayOfWeek.Friday,
700+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(7, 0, 0), new TimeSpan(8, 30, 0)),
701+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(8, 30, 0), new TimeSpan(16, 0, 0)))
702+
),
703+
// 3.3 Late open after market closes (market should be closed)
704+
new TestCaseData(
705+
new DateTime(),
706+
new DateTime(2020, 7, 3, 17, 0, 0),
707+
LocalMarketHours.ClosedAllDay(DayOfWeek.Friday)
708+
),
709+
710+
// 4. Both early close and late open scenarios
711+
// 4.1 Early close before late open (market closes early and reopens)
712+
new TestCaseData(
713+
new DateTime(2020, 7, 3, 12, 0, 0), // Close at noon
714+
new DateTime(2020, 7, 3, 13, 0, 0), // Reopen at 1pm
715+
new LocalMarketHours(DayOfWeek.Friday,
716+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(0, 0, 0), new TimeSpan(8, 30, 0)),
717+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(8, 30, 0), new TimeSpan(12, 0, 0)),
718+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(13, 0, 0), new TimeSpan(16, 0, 0)))
719+
),
720+
// 4.2 Early close after late open (only early close applies)
721+
new TestCaseData(
722+
new DateTime(2020, 7, 3, 15, 0, 0), // Close at 3pm
723+
new DateTime(2020, 7, 3, 14, 0, 0), // Late open at 2pm
724+
new LocalMarketHours(DayOfWeek.Friday,
725+
new MarketHoursSegment(MarketHoursState.Market, new TimeSpan(14, 0, 0), new TimeSpan(15, 0, 0)))
726+
),
727+
// 4.3 Both outside market hours (market should be closed)
728+
new TestCaseData(
729+
new DateTime(2020, 7, 3, 7, 0, 0), // Before open
730+
new DateTime(2020, 7, 3, 17, 0, 0), // After close
731+
LocalMarketHours.ClosedAllDay(DayOfWeek.Friday)
732+
),
733+
734+
// 5. Edge cases
735+
// 5.1 Early close exactly at market open
736+
new TestCaseData(
737+
new DateTime(2020, 7, 3, 8, 30, 0),
738+
new DateTime(),
739+
new LocalMarketHours(DayOfWeek.Friday,
740+
new MarketHoursSegment(MarketHoursState.PreMarket, new TimeSpan(0, 0, 0), new TimeSpan(8, 30, 0)))
741+
),
742+
// 5.2 Late open exactly at market close
743+
new TestCaseData(
744+
new DateTime(),
745+
new DateTime(2020, 7, 3, 16, 0, 0),
746+
LocalMarketHours.ClosedAllDay(DayOfWeek.Friday)
747+
)
748+
};
749+
}
750+
751+
public static SecurityExchangeHours CreateFuture6JExchangeHours(DateTime earlyClose, DateTime lateOpen)
643752
{
644753
var sunday = new LocalMarketHours(
645754
DayOfWeek.Sunday,
@@ -682,11 +791,14 @@ public static SecurityExchangeHours CreateFuture6JExchangeHours()
682791

683792
var saturday = LocalMarketHours.ClosedAllDay(DayOfWeek.Saturday);
684793

685-
var earlyCloses = new Dictionary<DateTime, TimeSpan>();
794+
var earlyCloses = new Dictionary<DateTime, TimeSpan>
795+
{
796+
{ earlyClose.Date, earlyClose.TimeOfDay }
797+
};
686798

687799
var lateOpens = new Dictionary<DateTime, TimeSpan>
688800
{
689-
{ new DateTime(2020, 7, 3), new TimeSpan(17, 0, 0) }
801+
{ lateOpen.Date, lateOpen.TimeOfDay }
690802
};
691803

692804
var holidays = new List<DateTime>

0 commit comments

Comments
 (0)