Files
2026-07-13 13:02:50 +08:00

566 lines
32 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 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<String, List<Dates>> _data = new Dictionary<String, List<Dates>>();
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()
{
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);
}
}
// 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<ArgumentException>(() => { FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(badSymbol)); },
$"Expiry function not implemented for {badSymbol} in FuturesExpiryFunctions.FuturesExpiryDictionary");
}
[Test]
public void FuturesExpiryFunctions_AllFutures_ShouldHaveExpiryFunction()
{
var missingFutures = new List<string>();
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<string>();
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.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);
}
[TestCase(QuantConnect.Securities.Futures.Indices.NASDAQ100EMini, "20260302", "20260320")]
[TestCase(QuantConnect.Securities.Futures.Indices.NASDAQ100EMini, "20260602", "20260618")]
public void ExpirationUsesHolidays(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.Date, $"Failed for symbol: {symbol}. Date {dateStr}");
}
// 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.VIXMini, 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);
}
}
/// <summary>
/// Dates for Termination Conditions of futures
/// </summary>
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;
}
}
/// <summary>
/// Class to convert Array into Dictionary using XmlSerializer
/// </summary>
public class Item
{
[XmlAttribute]
public String Symbol { get; set; }
public List<Dates> 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);
}
}
}