dcb5f8ee4e
Research Regression Tests / build (push) Has been cancelled
Python Virtual Environments / build (push) Has been cancelled
Benchmarks / build (push) Has been cancelled
Build & Test Lean / build (push) Has been cancelled
Regression Tests / build (push) Has been cancelled
* Update Future-cme-[*] and Future-cme-ES Acoording to `pandas_market_calendars` there were some dates in Future-cme-[*] who wasn't early_closes, so they needed to be removed from there. On the other hand, the early closes list of Future-cme-ES were shifted by 1 hour according to CME webpage. Besides, there were some missing dates. * Update CME Future entries in MHDB * Rebase * nit change * Fix unit tests * Resume after early close/halts * Add missing dates in MHDB and fix bugs in it * Fix bug, add more unit tests and add docs * fix regression algos * address required changes * Update failing regression test stats After debugging the tests it was found they were failing due to the last change on SecurityExchangeHours.IsOpen(). That method wasn't taking into account that even if there is a late open after an early close if the timespan is after the early close but before the late open, the market is still close. * enhance solution * Update and fix bugs in MHDB * Address required changes and update stats * Update stats after rebase * Nit change * Missing update to regression test * Use MHDB instead of USHoliday for Expiration Dates VIX expiry function now relies completely on MHDB. However, it had to be created an entry in MHDB for VIX since there wasn't one for it. CBOE webpage only provided 2023 holidays so only those dates were considered in the Holidays entry in MHDB. Therefore, some unit tests failed so it was necessary to change also the VIX entry in FuturesExpiryFunctionsTestData.xml. * Remove Global.cs/USHolidays class * Use a lazy implementation * First draft of the solution * Use MHDB in FuturesExpiryFunctions.cs * Remove unused class and fix indentation errors * Fix indentation errors * Nit changes * Merge branches 7501 and 7506 * Merge changes in 7501 and 7506 In order to check compatibility between those branches, a new branch was created out of branch 7501 and then it was merged with branch 7506. 2 regression tests and 8 unit tests failed, the regression tests failed on the DataPoint stats. On the other hand, the unit tests failed since the default parameter UseEquityHoliday was removed from FuturesExpirtyUtilityFunctions.AddBusinessDays() and from other methods in the same class too. * Add missing changes * Remove repeated good fridays * Address minor review --------- Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
323 lines
16 KiB
C#
323 lines
16 KiB
C#
/*
|
|
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
|
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using NodaTime;
|
|
using NUnit.Framework;
|
|
using QuantConnect.Securities;
|
|
using QuantConnect.Tests.Common.Securities;
|
|
|
|
namespace QuantConnect.Tests.Common
|
|
{
|
|
[TestFixture]
|
|
public class TimeTests
|
|
{
|
|
[TestCase("20230605 08:00", "04:00")]
|
|
[TestCase("20230605 15:00", "21:00")]
|
|
public void AuxiliaryDataDueTime(string utcNow, string expectedDueTime)
|
|
{
|
|
var result = Time.GetNextLiveAuxiliaryDataDueTime(Time.ParseDate(utcNow));
|
|
var expected = TimeSpan.ParseExact(expectedDueTime, "hh\\:mm", CultureInfo.InvariantCulture);
|
|
|
|
Assert.AreEqual(expected, result);
|
|
}
|
|
|
|
[Test]
|
|
public void UnixTimeStampSecondsToDateTimeHasSubMillisecondPrecision()
|
|
{
|
|
const double stamp = 1520711961.00055;
|
|
var expected = new DateTime(2018, 3, 10, 19, 59, 21, 0).AddTicks(5500);
|
|
var time = Time.UnixTimeStampToDateTime(stamp);
|
|
Assert.AreEqual(expected, time);
|
|
}
|
|
|
|
[Test]
|
|
public void UnixTimeStampMillisecondsToDateTimeHasSubMillisecondPrecision()
|
|
{
|
|
const decimal stamp = 1520711961000.55m;
|
|
var expected = new DateTime(2018, 3, 10, 19, 59, 21, 0).AddTicks(5500);
|
|
var time = Time.UnixMillisecondTimeStampToDateTime(stamp);
|
|
Assert.AreEqual(expected, time);
|
|
}
|
|
|
|
|
|
[Test]
|
|
public void UnixTimeStampSecondsToDateTimeSubMillisecondPrecision()
|
|
{
|
|
const decimal stamp = 1520711961.00055m;
|
|
var expected = new DateTime(2018, 3, 10, 19, 59, 21, 0).AddTicks(5500);
|
|
var time = Time.UnixTimeStampToDateTime(stamp);
|
|
Assert.AreEqual(expected, time);
|
|
}
|
|
|
|
[Test]
|
|
public void UnixTimeStampSecondsToDateTime()
|
|
{
|
|
const long stamp = 1520711961000;
|
|
var expected = new DateTime(2018, 3, 10, 19, 59, 21, 0);
|
|
var time = Time.UnixMillisecondTimeStampToDateTime(stamp);
|
|
Assert.AreEqual(expected, time);
|
|
}
|
|
|
|
[Test]
|
|
public void GetStartTimeForTradeBarsRoundsDown()
|
|
{
|
|
// 2015.09.01 @ noon
|
|
var end = new DateTime(2015, 09, 01, 12, 0, 1);
|
|
var barSize = TimeSpan.FromMinutes(1);
|
|
var hours = SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork);
|
|
var start = Time.GetStartTimeForTradeBars(hours, end, barSize, 1, false, TimeZones.NewYork);
|
|
// round down and back up a single bar
|
|
Assert.AreEqual(end.RoundDown(barSize).Subtract(barSize), start);
|
|
}
|
|
|
|
[Test]
|
|
public void GetStartTimeForTradeBarsHandlesOverNight()
|
|
{
|
|
// 2015.09.01 @ noon
|
|
var end = new DateTime(2015, 09, 01, 12, 0, 0);
|
|
var barSize = TimeSpan.FromHours(1);
|
|
var hours = SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours();
|
|
var start = Time.GetStartTimeForTradeBars(hours, end, barSize, 7, false, hours.TimeZone);
|
|
// from noon, back up to 9am (3 hours) then skip night, so from 4pm, back up to noon, 4 more hours
|
|
Assert.AreEqual(end.AddDays(-1), start);
|
|
}
|
|
|
|
[Test]
|
|
public void GetStartTimeForTradeBarsHandlesWeekends()
|
|
{
|
|
// 2015.09.01 @ noon
|
|
var end = new DateTime(2015, 09, 01, 12, 0, 0);
|
|
var expectedStart = new DateTime(2015, 08, 21);
|
|
var barSize = TimeSpan.FromDays(1);
|
|
var hours = SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours();
|
|
var start = Time.GetStartTimeForTradeBars(hours, end, barSize, 7, false, hours.TimeZone);
|
|
// from noon, back up to 9am (3 hours) then skip night, so from 4pm, back up to noon, 4 more hours
|
|
Assert.AreEqual(expectedStart, start);
|
|
}
|
|
|
|
[Test, TestCaseSource(nameof(ForexHistoryDates))]
|
|
public void GetStartTimeForForexTradeBars(DateTime end, DateTime expectedStart, DateTimeZone dataTimeZone)
|
|
{
|
|
var barSize = TimeSpan.FromDays(1);
|
|
var hours = SecurityExchangeHoursTests.CreateForexSecurityExchangeHours();
|
|
var start = Time.GetStartTimeForTradeBars(hours, end, barSize, 1, false, dataTimeZone);
|
|
Assert.AreEqual(expectedStart, start);
|
|
}
|
|
|
|
[Test, TestCaseSource(nameof(EquityHistoryDates))]
|
|
public void GetStartTimeForEquityTradeBars(DateTime end, DateTime expectedStart, DateTimeZone dataTimeZone)
|
|
{
|
|
var barSize = TimeSpan.FromMinutes(1);
|
|
var hours = SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours();
|
|
var start = Time.GetStartTimeForTradeBars(hours, end, barSize, 10, false, dataTimeZone);
|
|
Assert.AreEqual(expectedStart, start);
|
|
}
|
|
|
|
[Test]
|
|
public void EachTradeableDayInTimeZoneIsSameForEqualTimeZones()
|
|
{
|
|
var start = new DateTime(2010, 01, 01);
|
|
var end = new DateTime(2016, 02, 12);
|
|
var entry = MarketHoursDatabase.FromDataFolder().ExchangeHoursListing.First().Value;
|
|
var expected = Time.EachTradeableDay(entry.ExchangeHours, start, end);
|
|
var actual = Time.EachTradeableDayInTimeZone(entry.ExchangeHours, start, end, entry.ExchangeHours.TimeZone, true);
|
|
CollectionAssert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[Test]
|
|
public void EachTradeableDayInTimeZoneWithOffsetPlus12()
|
|
{
|
|
var start = new DateTime(2016, 2, 11);
|
|
var end = new DateTime(2016, 2, 12);
|
|
var equityExchange = SecurityExchangeHours.AlwaysOpen(DateTimeZone.ForOffset(Offset.FromHours(-5)));
|
|
var dataTimeZone = DateTimeZone.ForOffset(Offset.FromHours(7));
|
|
|
|
// given this arrangement we should still start on the same date and end a day late
|
|
var expected = new[] { start, end, end.AddDays(1) };
|
|
var actual = Time.EachTradeableDayInTimeZone(equityExchange, start, end, dataTimeZone, true);
|
|
CollectionAssert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[Test]
|
|
public void EachTradeableDayInTimeZoneWithOffsetMinus12()
|
|
{
|
|
var start = new DateTime(2016, 2, 11);
|
|
var end = new DateTime(2016, 2, 12);
|
|
var exchange = SecurityExchangeHours.AlwaysOpen(DateTimeZone.ForOffset(Offset.FromHours(5)));
|
|
var dataTimeZone = DateTimeZone.ForOffset(Offset.FromHours(-7));
|
|
|
|
// given this arrangement we should still start a day early but still end on the same date
|
|
var expected = new[] { start.AddDays(-1), start, end };
|
|
var actual = Time.EachTradeableDayInTimeZone(exchange, start, end, dataTimeZone, true);
|
|
CollectionAssert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[Test]
|
|
public void EachTradeableDayInTimeZoneWithOffset25()
|
|
{
|
|
var start = new DateTime(2016, 2, 11);
|
|
var end = new DateTime(2016, 2, 12);
|
|
var exchange = SecurityExchangeHours.AlwaysOpen(DateTimeZone.ForOffset(Offset.FromHours(12)));
|
|
var dataTimeZone = DateTimeZone.ForOffset(Offset.FromHours(-13));
|
|
|
|
// given this arrangement we should still start a day early but still end on the same date
|
|
var expected = new[] { start.AddDays(-2), start.AddDays(-1), start };
|
|
var actual = Time.EachTradeableDayInTimeZone(exchange, start, end, dataTimeZone, true);
|
|
CollectionAssert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[Test]
|
|
public void MultipliesTimeSpans()
|
|
{
|
|
var interval = TimeSpan.FromSeconds(1);
|
|
var expected = TimeSpan.FromSeconds(5);
|
|
var actual = interval.Multiply(5d);
|
|
Assert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[Test]
|
|
[TestCase(2, 7, 3)]
|
|
[TestCase(2, 4, 2)]
|
|
[TestCase(6, 7, 1)]
|
|
public void GetNumberOfTradeBarsForIntervalUsingDailyStepSize(int startDay, int endDay, int expected)
|
|
{
|
|
var start = new DateTime(2018, 08, startDay);
|
|
var end = new DateTime(2018, 08, endDay);
|
|
var exchangeHours = CreateUsEquitySecurityExchangeHours();
|
|
var actual = Time.GetNumberOfTradeBarsInInterval(exchangeHours, start, end, Time.OneDay);
|
|
Assert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[Test]
|
|
[TestCase(2, 7, 21)]
|
|
[TestCase(2, 4, 14)]
|
|
[TestCase(6, 7, 07)]
|
|
public void GetNumberOfTradeBarsForIntervalUsingHourlyStepSize(int startDay, int endDay, int expected)
|
|
{
|
|
var start = new DateTime(2018, 08, startDay);
|
|
var end = new DateTime(2018, 08, endDay);
|
|
var exchangeHours = CreateUsEquitySecurityExchangeHours();
|
|
var actual = Time.GetNumberOfTradeBarsInInterval(exchangeHours, start, end, Time.OneHour);
|
|
Assert.AreEqual(expected, actual);
|
|
}
|
|
|
|
|
|
private static readonly TimeSpan USEquityPreOpen = new TimeSpan(4, 0, 0);
|
|
private static readonly TimeSpan USEquityOpen = new TimeSpan(9, 30, 0);
|
|
private static readonly TimeSpan USEquityClose = new TimeSpan(16, 0, 0);
|
|
private static readonly TimeSpan USEquityPostClose = new TimeSpan(20, 0, 0);
|
|
private static SecurityExchangeHours CreateUsEquitySecurityExchangeHours()
|
|
{
|
|
var sunday = LocalMarketHours.ClosedAllDay(DayOfWeek.Sunday);
|
|
var monday = new LocalMarketHours(DayOfWeek.Monday, USEquityPreOpen, USEquityOpen, USEquityClose, USEquityPostClose);
|
|
var tuesday = new LocalMarketHours(DayOfWeek.Tuesday, USEquityPreOpen, USEquityOpen, USEquityClose, USEquityPostClose);
|
|
var wednesday = new LocalMarketHours(DayOfWeek.Wednesday, USEquityPreOpen, USEquityOpen, USEquityClose, USEquityPostClose);
|
|
var thursday = new LocalMarketHours(DayOfWeek.Thursday, USEquityPreOpen, USEquityOpen, USEquityClose, USEquityPostClose);
|
|
var friday = new LocalMarketHours(DayOfWeek.Friday, USEquityPreOpen, USEquityOpen, USEquityClose, USEquityPostClose);
|
|
var saturday = LocalMarketHours.ClosedAllDay(DayOfWeek.Saturday);
|
|
|
|
var earlyCloses = new Dictionary<DateTime, TimeSpan>();
|
|
var lateOpens = new Dictionary<DateTime, TimeSpan>();
|
|
var holidays = MarketHoursDatabase.FromDataFolder()
|
|
.GetEntry(Market.USA, (string)null, SecurityType.Equity)
|
|
.ExchangeHours
|
|
.Holidays;
|
|
return new SecurityExchangeHours(TimeZones.NewYork, holidays, new[]
|
|
{
|
|
sunday, monday, tuesday, wednesday, thursday, friday, saturday
|
|
}.ToDictionary(x => x.DayOfWeek), earlyCloses, lateOpens);
|
|
}
|
|
|
|
[Test]
|
|
[TestCase("190120", 2019, 1, 20)]
|
|
[TestCase("20190120", 2019, 1, 20)]
|
|
[TestCase("20190120 00:00", 2019, 1, 20)]
|
|
[TestCase("2019-01-20T00:00:00.000Z", 2019, 1, 20)]
|
|
[TestCase("1/20/2019 00:00:00 AM", 2019, 1, 20)]
|
|
[TestCase("1/20/19 00:00 AM", 2019, 1, 20)]
|
|
[TestCase("1/20/19", 2019, 1, 20)]
|
|
public void ParseDate(string parseDate, int year, int month, int day)
|
|
{
|
|
Assert.AreEqual(new DateTime(year, month, day), Time.ParseDate(parseDate));
|
|
}
|
|
|
|
[Test]
|
|
[TestCase("20190120 02:30", 2019, 1, 20, 2, 30)]
|
|
[TestCase("1/20/2019 2:30:00 AM", 2019, 1, 20, 2, 30)]
|
|
[TestCase("1/20/2019 2:30:00 PM", 2019, 1, 20, 14, 30)]
|
|
[TestCase("1/20/19 2:30 PM", 2019, 1, 20, 14, 30)]
|
|
[TestCase("2019-01-20T02:30:00.000Z", 2019, 1, 20, 2, 30)]
|
|
public void ParseDateAndTime(string parseDate, int year, int month, int day, int hour, int minute)
|
|
{
|
|
Assert.AreEqual(new DateTime(year, month, day, hour, minute, 0), Time.ParseDate(parseDate));
|
|
}
|
|
|
|
[Test]
|
|
[TestCase("19981231-23:59:59", 1998, 12, 31, 23, 59, 59)]
|
|
[TestCase("19990101-00:00:00", 1999, 01, 01, 00, 00, 00)]
|
|
[TestCase("20210121-21:32:18", 2021, 01, 21, 21, 32, 18)]
|
|
public void ParseFIXUtcTimestamp(string parseDate, int year, int month, int day, int hour, int minute, int second)
|
|
{
|
|
var expected = new DateTime(year, month, day, hour, minute, second);
|
|
Assert.AreEqual(
|
|
expected,
|
|
Parse.DateTimeExact(parseDate, DateFormat.FIX));
|
|
|
|
Assert.AreEqual(
|
|
expected,
|
|
Time.ParseFIXUtcTimestamp(parseDate));
|
|
}
|
|
|
|
[Test]
|
|
[TestCase("19981231-23:59:59.000", 1998, 12, 31, 23, 59, 59, 0)]
|
|
[TestCase("19990101-00:00:00.000", 1999, 01, 01, 00, 00, 00, 0)]
|
|
[TestCase("20210121-21:32:18.610", 2021, 01, 21, 21, 32, 18, 610)]
|
|
public void ParseFIXUtcTimestampWithMillisecond(string parseDate, int year, int month, int day, int hour, int minute, int second, int millisecond)
|
|
{
|
|
var expected = new DateTime(year, month, day, hour, minute, second, millisecond);
|
|
Assert.AreEqual(
|
|
expected,
|
|
Parse.DateTimeExact(parseDate, DateFormat.FIXWithMillisecond));
|
|
|
|
Assert.AreEqual(
|
|
expected,
|
|
Time.ParseFIXUtcTimestamp(parseDate));
|
|
}
|
|
|
|
private static IEnumerable<TestCaseData> ForexHistoryDates => new List<TestCaseData>
|
|
{
|
|
new TestCaseData(new DateTime(2018, 04, 02, 1, 0, 0), new DateTime(2018, 04, 01, 01, 0, 0), DateTimeZone.ForOffset(Offset.FromHours(-5))),
|
|
new TestCaseData(new DateTime(2018, 04, 02, 0, 0, 0), new DateTime(2018, 03, 29, 01, 0, 0), DateTimeZone.ForOffset(Offset.FromHours(-5))),
|
|
new TestCaseData(new DateTime(2018, 04, 04, 0, 0, 0), new DateTime(2018, 04, 02, 01, 0, 0), DateTimeZone.ForOffset(Offset.FromHours(-5))),
|
|
new TestCaseData(new DateTime(2018, 04, 02, 1, 0, 0), new DateTime(2018, 03, 29, 15, 0, 0), DateTimeZone.ForOffset(Offset.FromHours(5))),
|
|
new TestCaseData(new DateTime(2018, 04, 02, 0, 0, 0), new DateTime(2018, 03, 29, 15, 0, 0), DateTimeZone.ForOffset(Offset.FromHours(5))),
|
|
new TestCaseData(new DateTime(2018, 04, 04, 0, 0, 0), new DateTime(2018, 04, 02, 15, 0, 0), DateTimeZone.ForOffset(Offset.FromHours(5))),
|
|
new TestCaseData(new DateTime(2018, 04, 02, 1, 0, 0), new DateTime(2018, 03, 31, 20, 0, 0), DateTimeZone.Utc),
|
|
new TestCaseData(new DateTime(2018, 04, 02, 0, 0, 0), new DateTime(2018, 03, 31, 20, 0, 0), DateTimeZone.Utc),
|
|
new TestCaseData(new DateTime(2018, 04, 04, 0, 0, 0), new DateTime(2018, 04, 02, 20, 0, 0), DateTimeZone.Utc)
|
|
};
|
|
|
|
private static IEnumerable<TestCaseData> EquityHistoryDates => new List<TestCaseData>
|
|
{
|
|
new TestCaseData(new DateTime(2013, 10, 08, 17, 0, 0), new DateTime(2013, 10, 08, 15, 50, 0), DateTimeZone.Utc),
|
|
new TestCaseData(new DateTime(2013, 10, 08, 13, 0, 0), new DateTime(2013, 10, 08, 12, 50, 0), DateTimeZone.Utc),
|
|
};
|
|
}
|
|
}
|