/* * 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 NUnit.Framework; using QuantConnect.Securities.Future; using System.Collections.Generic; using System.Linq; using System.Xml; using System.Xml.Serialization; using System.IO; using QuantConnect.Brokerages; using QuantConnect.Securities; namespace QuantConnect.Tests.Common.Securities.Futures { [TestFixture, Parallelizable(ParallelScope.Fixtures)] public class FuturesExpiryFunctionsTests { private IDictionary> _data = new Dictionary>(); private const string Zero = "00:00:00"; private const string ElevenAmHongKongTime = "03:00:00"; private const string ElevenOclockMoscowTime = "08:00:00"; private const string TenSixteen = "10:16:00"; private const string ElevenOclock = "11:00:00"; private const string NineFifteenCentralTime = "14:15:00"; private const string NineSixteenCentralTime = "14:16:00"; private const string TwelvePMCentralTime = "17:00:00"; private const string TwelveFivePMCentralTime = "17:05:00"; private const string TwelveTenCentralTime = "17:10:00"; private const string OneThirtyPMCentralTime = "18:30:00"; private const string OneFortyPMCentralTime = "18:40:00"; private const string TwoPMCentralTime = "19:00:00"; private const string ThreePMCentralTime = "20:00:00"; private const string NineThirtyEasternTime = "13:30:00"; private const string FiveOClockPMEasternTime = "21:00:00"; private const string EightOClockChicagoTime = "13:00:00"; private const string TwelveOclock = "12:00:00"; private const string TwelveOne = "12:01:00"; private const string FourPMLondonTime = "15:00:00"; private const string OneTwentyFivePM = "13:25:00"; private const string OneThirtyPM = "13:30:00"; private const string TwoThirtyPM = "14:30:00"; private const string OneFortyFivePM = "13:45:00"; private const string ThreeThirtyPM = "15:30:00"; private const string FourFifteenPM = "16:15:00"; private readonly SymbolPropertiesDatabase _symbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder(); [OneTimeSetUp] public void Init() { FuturesExpiryUtilityFunctions.BankHolidays = true; var path = Path.Combine("TestData", "FuturesExpiryFunctionsTestData.xml"); using (var reader = XmlReader.Create(path)) { var serializer = new XmlSerializer(typeof(Item[])); _data = ((Item[])serializer.Deserialize(reader)).ToDictionary(i=>i.Symbol,i=>i.SymbolDates); } } [OneTimeTearDown] public void TearDown() { FuturesExpiryUtilityFunctions.BankHolidays = false; } // last day and previous are holidays [TestCase("20250101", "20250127")] // normal case [TestCase("20250201", "20250227")] [TestCase("20250301", "20250328")] [TestCase("20250401", "20250429")] [TestCase("20250501", "20250529")] [TestCase("20250601", "20250627")] [TestCase("20250701", "20250730")] [TestCase("20250801", "20250828")] [TestCase("20250901", "20250929")] [TestCase("20251001", "20251030")] [TestCase("20251101", "20251127")] [TestCase("20251201", "20251230")] public void HSIFutures(string input, string expectedStr) { var date = Time.ParseDate(input); var expected = Time.ParseDate(expectedStr); var canonical = Symbol.Create("HSI", SecurityType.Future, Market.HKFE); var expiration = FuturesExpiryFunctions.FuturesExpiryDictionary[canonical]; var result = expiration(date); Assert.AreEqual(expected, result.Date); } [Test] public void MCLFutures() { var canonical = Symbol.Create("MCL", SecurityType.Future, Market.NYMEX); var expiration = FuturesExpiryFunctions.FuturesExpiryDictionary[canonical]; // 1/25 is Saturday and 1/20 is a holiday Assert.AreEqual(new DateTime(2025, 1, 17, 0, 0, 0), expiration(new DateTime(2025, 2, 1))); // Whole weekend in between Assert.AreEqual(new DateTime(2025, 2, 19, 0, 0, 0), expiration(new DateTime(2025, 3, 1))); // Normal case Assert.AreEqual(new DateTime(2025, 4, 21, 0, 0, 0), expiration(new DateTime(2025, 5, 1))); } [Test] public void FuturesExpiryFunction_MissingSymbol_ShouldThrowArgumentException() { const string badSymbol = "AAAAA"; Assert.Throws(() => { FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(badSymbol)); }, $"Expiry function not implemented for {badSymbol} in FuturesExpiryFunctions.FuturesExpiryDictionary"); } [Test] public void FuturesExpiryFunctions_AllFutures_ShouldHaveExpiryFunction() { var missingFutures = new List(); var futuresSymbols = typeof(QuantConnect.Securities.Futures).GetNestedTypes() .SelectMany(x => x.GetFields()) .Select(x => x.GetValue(null)) // null for obj in GetValue indicates static field .Cast(); foreach (var futuresSymbol in futuresSymbols) { try { FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(futuresSymbol)); } catch (ArgumentException) { missingFutures.Add(futuresSymbol); } } Assert.IsEmpty(missingFutures, $"The following symbols do not have an expiry function defined in FuturesExpiryFunction.FuturesExpiryDictionary: {string.Join(", ", missingFutures)}"); } [TestCase(QuantConnect.Securities.Futures.Grains.BlackSeaCornFinanciallySettledPlatts)] [TestCase(QuantConnect.Securities.Futures.Grains.SRWWheat)] [TestCase(QuantConnect.Securities.Futures.Grains.Wheat)] [TestCase(QuantConnect.Securities.Futures.Grains.HRWWheat)] [TestCase(QuantConnect.Securities.Futures.Grains.Corn)] [TestCase(QuantConnect.Securities.Futures.Grains.Soybeans)] [TestCase(QuantConnect.Securities.Futures.Grains.SoybeanMeal)] [TestCase(QuantConnect.Securities.Futures.Grains.SoybeanOil)] [TestCase(QuantConnect.Securities.Futures.Grains.Oats)] [TestCase(QuantConnect.Securities.Futures.Grains.BlackSeaWheatFinanciallySettledPlatts)] public void GrainsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade; //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Currencies.USD, TenSixteen)] [TestCase(QuantConnect.Securities.Futures.Currencies.GBP, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.CAD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.JPY, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.CHF, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.EUR, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.AUD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.NZD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.RUB, ElevenOclockMoscowTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.BRL, NineFifteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.MXN, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.ZAR, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.AUDCAD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.AUDJPY, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.AUDNZD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.BTC, FourPMLondonTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.ETH, FourPMLondonTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.CADJPY, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.StandardSizeUSDOffshoreRMBCNH, ElevenAmHongKongTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.EuroFXEmini, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.EURAUD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.EURCAD, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.EURSEK, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.JapaneseYenEmini, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.MicroEUR, NineSixteenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.MicroBTC, FourPMLondonTime)] [TestCase(QuantConnect.Securities.Futures.Currencies.MicroEther, FourPMLondonTime)] public void CurrenciesExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Energy.PropaneNonLDHMontBelvieu, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ArgusPropaneFarEastIndexBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MiniEuropeanThreePointPercentFiveFuelOilBargesPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MiniSingaporeFuelOil180CstPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GulfCoastULSDPlattsUpDownBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GulfCoastJetPlattsUpDownBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.PropaneNonLDHMontBelvieuOPIS, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EuropeanPropaneCIFARAArgusBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.PremiumUnleadedGasoline10ppmFOBMEDPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ArgusPropaneFarEastIndex, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GasolineEurobobOxyNWEBargesArgusCrackSpreadBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuNaturalGasolineOPIS, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuNormalButaneOPISBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ConwayPropaneOPIS, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuLDHPropaneOPISBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ArgusPropaneFarEastIndexVsEuropeanPropaneCIFARAArgus, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ArgusPropaneSaudiAramco, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GroupThreeULSDPlattsVsNYHarborULSD, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GroupThreeSuboctaneGasolinePlattsVsRBOB, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.SingaporeFuelOil180cstPlattsBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.SingaporeFuelOil380cstPlattsBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuEthaneOPIS, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuNormalButaneOPIS, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.BrentCrudeOilVsDubaiCrudeOilPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ArgusLLSvsWTIArgusTradeMonth, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.SingaporeGasoilPlattsVsLowSulphurGasoilFutures, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.LosAngelesCARBOBGasolineOPISvsRBOBGasoline, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.LosAngelesJetOPISvsNYHarborULSD, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.LosAngelesCARBDieselOPISvsNYHarborULSD, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EuropeanNaphthaPlattsBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EuropeanPropaneCIFARAArgus, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuNaturalGasolineOPISBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.RBOBGasolineCrackSpread, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GulfCoastHSFOPlattsBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MarsArgusVsWTITradeMonth, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MarsArgusVsWTIFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EthanolT2FOBRdamIncludingDutyPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.MontBelvieuLDHPropaneOPIS, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GasolineEurobobOxyNWEBargesArgus, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.WTIBrentFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ThreePointFivePercentFuelOilBargesFOBRdamPlattsCrackSpread1000mt, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GasolineEurobobOxyNWEBargesArgusBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.BrentLastDayFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.CrudeOilWTI, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GulfCoastCBOBGasolineA2PlattsVsRBOBGasoline, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ClearbrookBakkenSweetCrudeOilMonthlyIndexNetEnergy, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.WTIFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ChicagoEthanolPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.SingaporeMogas92UnleadedPlattsBrentCrackSpread, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.DubaiCrudeOilPlattsFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.JapanCnFNaphthaPlattsBALMO, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.Ethanol, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EuropeanNaphthaPlattsCrackSpread, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EuropeanPropaneCIFARAArgusVsNaphthaCargoesCIFNWEPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.SingaporeFuelOil380cstPlattsVsEuropeanThreePointFivePercentFuelOilBargesFOBRdamPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EastWestGasolineSpreadPlattsArgus, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.EastWestNaphthaJapanCFvsCargoesCIFNWESpreadPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.RBOBGasolineVsEurobobOxyNWEBargesArgusThreeHundredFiftyThousandGallons, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.ThreePointFivePercentFuelOilBargesFOBRdamPlattsCrackSpread, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.FreightRouteTC14Baltic, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.OnePercentFuelOilCargoesFOBNWEPlattsVsThreePointFivePercentFuelOilBargesFOBRdamPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.GulfCoastHSFOPlattsVsEuropeanThreePointFivePercentFuelOilBargesFOBRdamPlatts, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.WTIHoustonCrudeOil, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.NaturalGasHenryHubLastDayFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.HeatingOil, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.NaturalGasHenryHubPenultimateFinancial, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.WTIHoustonArgusVsWTITradeMonth, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.Gasoline, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.NaturalGas, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.BrentCrude, Zero)] [TestCase(QuantConnect.Securities.Futures.Energy.LowSulfurGasoil, TwelveOclock)] [TestCase(QuantConnect.Securities.Futures.Energy.MicroCrudeOilWTI, Zero)] public void EnergyExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [Test] public void BankHolidaysAreRespected() { //Arrange var futureSymbol = GetFutureSymbol("6E", new DateTime(2025, 2, 1)); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol("6E")); // Expiry date is the second business day immediately preceding the third Wednesday of the contract month(usually Monday). // The third wednesday is the 19th so the expiry date should be monday 17th, but that day is a bank holiday // so the real expiry date is the 14th var expiryDate = func(futureSymbol.ID.Date); //Assert Assert.AreEqual(new DateTime(2025, 2, 14), expiryDate.Date); } // 25th is a sunday [TestCase(QuantConnect.Securities.Futures.Energy.MicroCrudeOilWTI, "20221001", "20220919")] [TestCase(QuantConnect.Securities.Futures.Energy.CrudeOilWTI, "20221001", "20220920")] // 25th is a tuesday [TestCase(QuantConnect.Securities.Futures.Energy.MicroCrudeOilWTI, "20221101", "20221019")] [TestCase(QuantConnect.Securities.Futures.Energy.CrudeOilWTI, "20221101", "20221020")] // 25th is a friday but includes thanks giving [TestCase(QuantConnect.Securities.Futures.Energy.MicroCrudeOilWTI, "20221201", "20221118")] [TestCase(QuantConnect.Securities.Futures.Energy.CrudeOilWTI, "20221201", "20221121")] public void MicroCrudeOilExpiration(string symbol, string dateStr, string expectedDate) { var date = Time.ParseDate(dateStr); var expected = Time.ParseDate(expectedDate); var futureSymbol = GetFutureSymbol(symbol, date); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); var actual = func(futureSymbol.ID.Date); Assert.AreEqual(expected, actual, $"Failed for symbol: {symbol}. Date {dateStr}"); } [TestCase(QuantConnect.Securities.Futures.Financials.EuroDollar, ElevenOclock)] [TestCase(QuantConnect.Securities.Futures.Financials.Y30TreasuryBond, TwelveOne)] [TestCase(QuantConnect.Securities.Futures.Financials.Y10TreasuryNote, TwelveOne)] [TestCase(QuantConnect.Securities.Futures.Financials.Y5TreasuryNote, TwelveOne)] [TestCase(QuantConnect.Securities.Futures.Financials.Y2TreasuryNote, TwelveOne)] [TestCase(QuantConnect.Securities.Futures.Financials.FiveYearUSDMACSwap, TwoPMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Financials.UltraUSTreasuryBond, TwelveOne)] [TestCase(QuantConnect.Securities.Futures.Financials.UltraTenYearUSTreasuryNote, Zero)] [TestCase(QuantConnect.Securities.Futures.Financials.MicroY10TreasuryNote, Zero)] public void FinancialsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Indices.BloombergCommodityIndex, OneThirtyPMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Indices.NASDAQ100BiotechnologyEMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.FTSEEmergingEmini, ThreePMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Indices.SP400MidCapEmini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.SPGSCICommodity, OneFortyPMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Indices.USDDenominatedIbovespa, ThreePMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Indices.SP500EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.NASDAQ100EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.Dow30EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.Russell2000EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.Nikkei225Dollar, FiveOClockPMEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.VIX, EightOClockChicagoTime)] [TestCase(QuantConnect.Securities.Futures.Indices.Nikkei225Yen, TwoThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCITaiwanIndex, OneFortyFivePM)] [TestCase(QuantConnect.Securities.Futures.Indices.Nifty50, ThreeThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Indices.BankNifty, ThreeThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Indices.BseSensex, ThreeThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MicroSP500EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.MicroDow30EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCIEuropeNTR, FourFifteenPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCIJapanNTR, FourFifteenPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCIUsaIndex, FourFifteenPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCIEmergingMarketsAsiaNTR, FourFifteenPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCIEmergingMarketsIndex, FourFifteenPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCIEafeIndex, FourFifteenPM)] public void IndicesExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Meats.LiveCattle, TwelveOclock)] [TestCase(QuantConnect.Securities.Futures.Meats.LeanHogs, TwelveOclock)] [TestCase(QuantConnect.Securities.Futures.Meats.FeederCattle, Zero)] public void MeatsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Forestry.Lumber, TwelveFivePMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Forestry.RandomLengthLumber, TwelveFivePMCentralTime)] public void LumberPulpExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Metals.Gold, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.Silver, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.Platinum, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.Palladium, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.AluminumMWUSTransactionPremiumPlatts25MT, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.AluminiumEuropeanPremiumDutyPaidMetalBulletin, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.Copper, TwelvePMCentralTime)] [TestCase(QuantConnect.Securities.Futures.Metals.USMidwestDomesticHotRolledCoilSteelCRUIndex, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.MicroGold, Zero)] [TestCase(QuantConnect.Securities.Futures.Metals.MiniNYGold, OneThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Metals.MiniNYSilver, OneTwentyFivePM)] [TestCase(QuantConnect.Securities.Futures.Metals.Gold100Oz, OneThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Metals.Silver5000Oz, OneTwentyFivePM)] public void MetalsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Softs.Cotton2)] [TestCase(QuantConnect.Securities.Futures.Softs.OrangeJuice)] [TestCase(QuantConnect.Securities.Futures.Softs.Coffee)] [TestCase(QuantConnect.Securities.Futures.Softs.Sugar11)] [TestCase(QuantConnect.Securities.Futures.Softs.Sugar11CME)] [TestCase(QuantConnect.Securities.Futures.Softs.Cocoa)] public void SoftsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade; //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } [TestCase(QuantConnect.Securities.Futures.Dairy.CashSettledButter, TwelveTenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Dairy.CashSettledCheese, TwelveTenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Dairy.ClassIIIMilk, TwelveTenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Dairy.DryWhey, TwelveTenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Dairy.ClassIVMilk, TwelveTenCentralTime)] [TestCase(QuantConnect.Securities.Futures.Dairy.NonfatDryMilk, TwelveTenCentralTime)] public void DairyExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { // Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); // Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); // Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } } /// /// Dates for Termination Conditions of futures /// public class Dates { public DateTime ContractMonth { get; set; } public DateTime LastTrade { get; set; } public Dates() { } public Dates(DateTime c, DateTime l) { ContractMonth = c; LastTrade = l; } } /// /// Class to convert Array into Dictionary using XmlSerializer /// public class Item { [XmlAttribute] public String Symbol { get; set; } public List SymbolDates { get; set; } } private Symbol GetFutureSymbol(string symbol, DateTime? date =null) { string market; if (!_symbolPropertiesDatabase.TryGetMarket(symbol, SecurityType.Future, out market)) { market = DefaultBrokerageModel.DefaultMarketMap[SecurityType.Future]; } if (date.HasValue) { return Symbol.CreateFuture(symbol, market, date.Value); } return Symbol.Create(symbol, SecurityType.Future, market); } } }