|
14 | 14 | */ |
15 | 15 |
|
16 | 16 | using System; |
| 17 | +using System.Collections.Generic; |
17 | 18 | using System.Globalization; |
18 | 19 | using System.IO; |
19 | 20 | using System.Linq; |
@@ -438,56 +439,74 @@ public void CustomEntriesAreNotLostWhenReset(string ticker, SecurityType securit |
438 | 439 | [Test] |
439 | 440 | public void VerifyMarketHoursDataIntegrityForAllSymbols() |
440 | 441 | { |
| 442 | + // Register the "nfo" market |
| 443 | + Market.Add("nfo", 100); |
| 444 | + |
441 | 445 | // Load the market hours database |
442 | 446 | var marketHoursDatabase = MarketHoursDatabase.FromDataFolder(); |
443 | 447 |
|
| 448 | + // Test all specific entries |
| 449 | + foreach (var entry in marketHoursDatabase.ExchangeHoursListing) |
| 450 | + { |
| 451 | + var securityType = entry.Key.SecurityType; |
| 452 | + var ticker = entry.Key.Symbol; |
| 453 | + Assert.IsFalse(string.IsNullOrEmpty(ticker), $"Ticker is null or empty"); |
| 454 | + var market = entry.Key.Market; |
| 455 | + |
| 456 | + // Create symbol |
| 457 | + Symbol symbol; |
| 458 | + if (ticker.Contains("[*]") || ticker == "*") |
| 459 | + { |
| 460 | + symbol = Symbol.Create("TEST_SYMBOL", securityType, market); |
| 461 | + } |
| 462 | + else |
| 463 | + { |
| 464 | + symbol = Symbol.Create(ticker, securityType, market); |
| 465 | + } |
| 466 | + |
| 467 | + TestMarketHoursForSymbol(marketHoursDatabase, market, symbol, securityType); |
| 468 | + } |
| 469 | + } |
| 470 | + |
| 471 | + private static void TestMarketHoursForSymbol(MarketHoursDatabase marketHoursDatabase, string market, Symbol symbol, SecurityType securityType) |
| 472 | + { |
444 | 473 | // Define date range (1998-01-01 to today, checking daily) |
445 | 474 | var startDate = new DateTime(1998, 1, 1); |
446 | 475 | var endDate = DateTime.Now; |
447 | 476 |
|
448 | | - // Iterate through all entries in the database |
449 | | - foreach (var entry in marketHoursDatabase.ExchangeHoursListing) |
| 477 | + var exchangeHours = marketHoursDatabase.GetExchangeHours(market, symbol, securityType); |
| 478 | + |
| 479 | + // Check every day |
| 480 | + for (var date = startDate; date <= endDate; date = date.AddDays(1)) |
450 | 481 | { |
451 | | - var market = entry.Key.Market; |
452 | | - var symbol = entry.Key.Symbol; |
453 | | - var securityType = entry.Key.SecurityType; |
| 482 | + // Get market hours for this date |
| 483 | + var marketHours = exchangeHours.GetMarketHours(date); |
| 484 | + |
| 485 | + // Ensure market hours exist for the date |
| 486 | + Assert.IsNotNull(exchangeHours, "Exchange hours should not be null."); |
454 | 487 |
|
455 | | - // Skip entries with null symbol |
456 | | - if (string.IsNullOrEmpty(symbol)) |
| 488 | + var segments = marketHours.Segments; |
| 489 | + for (int i = 1; i < segments.Count; i++) |
457 | 490 | { |
458 | | - continue; |
| 491 | + // Ensure segments do not overlap |
| 492 | + Assert.LessOrEqual(segments[i - 1].End, segments[i].Start, |
| 493 | + $"Segments overlap for {symbol} on {date:yyyy-MM-dd} between {segments[i - 1]} and {segments[i]}"); |
| 494 | + } |
| 495 | + |
| 496 | + bool hasEarlyClose = exchangeHours.EarlyCloses.TryGetValue(date, out var earlyCloseTime); |
| 497 | + bool hasLateOpen = exchangeHours.LateOpens.TryGetValue(date, out var lateOpenTime); |
| 498 | + if (hasEarlyClose && hasLateOpen && segments.Count > 0) |
| 499 | + { |
| 500 | + // Ensure LateOpen time is not after market close, but only when there is an EarlyClose |
| 501 | + Assert.LessOrEqual(lateOpenTime, segments[^1].End, |
| 502 | + $"Late open time {lateOpenTime} is after market close {segments[^1].End} for {symbol} on {date:yyyy-MM-dd}"); |
459 | 503 | } |
460 | 504 |
|
461 | | - // Check every day |
462 | | - for (var date = startDate; date <= endDate; date = date.AddDays(1)) |
| 505 | + if (exchangeHours.Holidays.Contains(date)) |
463 | 506 | { |
464 | | - // Get market hours for this date |
465 | | - var exchangeHours = marketHoursDatabase.GetExchangeHours(market, symbol, securityType); |
466 | | - var marketHours = exchangeHours.GetMarketHours(date); |
467 | | - |
468 | | - // Ensure market hours exist for the date |
469 | | - Assert.IsNotNull(marketHours, $"Market hours should not be null for {symbol} on {date:yyyy-MM-dd}."); |
470 | | - |
471 | | - var segments = marketHours.Segments; |
472 | | - for (int i = 1; i < segments.Count; i++) |
473 | | - { |
474 | | - // Ensure segments do not overlap |
475 | | - Assert.IsTrue(segments[i].Start >= segments[i - 1].End); |
476 | | - } |
477 | | - |
478 | | - bool hasEarlyClose = exchangeHours.EarlyCloses.TryGetValue(date, out var earlyCloseTime); |
479 | | - bool hasLateOpen = exchangeHours.LateOpens.TryGetValue(date, out var lateOpenTime); |
480 | | - if (hasEarlyClose && hasLateOpen && segments.Count > 0) |
481 | | - { |
482 | | - // Ensure LateOpen time is not after market close, but only when there is an EarlyClose |
483 | | - Assert.IsTrue(lateOpenTime <= segments[^1].End); |
484 | | - } |
485 | | - |
486 | | - if (exchangeHours.Holidays.Contains(date)) |
487 | | - { |
488 | | - // Ensure market is fully closed on holidays |
489 | | - Assert.IsTrue(marketHours.IsClosedAllDay); |
490 | | - } |
| 507 | + // Ensure market is fully closed on holidays |
| 508 | + Assert.IsTrue(marketHours.IsClosedAllDay, |
| 509 | + $"Market should be fully closed on holiday {date:yyyy-MM-dd} for {symbol}"); |
491 | 510 | } |
492 | 511 | } |
493 | 512 | } |
|
0 commit comments