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

307 lines
13 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.Linq;
using NUnit.Framework;
using QuantConnect.Data;
using System.Globalization;
using QuantConnect.Securities;
using QuantConnect.Interfaces;
using QuantConnect.Data.Market;
using System.Collections.Generic;
using QuantConnect.Data.Auxiliary;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Lean.Engine.DataFeeds.Enumerators;
namespace QuantConnect.Tests.Engine.DataFeeds.Enumerators
{
[TestFixture]
public class LiveAuxiliaryDataEnumeratorTests
{
[TestCase(DataMappingMode.OpenInterest, "20130616", false)]
[TestCase(DataMappingMode.FirstDayMonth, "20130602", false)]
[TestCase(DataMappingMode.LastTradingDay, "20130623", false)]
[TestCase(DataMappingMode.OpenInterest, "20130616", true)]
[TestCase(DataMappingMode.FirstDayMonth, "20130602", true)]
[TestCase(DataMappingMode.LastTradingDay, "20130623", true)]
public void EmitsMappingEventsBasedOnCurrentMapFileAndTime(DataMappingMode dataMappingMode, string mappingDate, bool delayed)
{
var config = new SubscriptionDataConfig(typeof(TradeBar),
Symbols.ES_Future_Chain,
Resolution.Daily,
TimeZones.NewYork,
TimeZones.NewYork,
true,
true,
false,
dataMappingMode: dataMappingMode);
var symbolMaps = new List<SubscriptionDataConfig.NewSymbolEventArgs>();
config.NewSymbol += (sender, args) => symbolMaps.Add(args);
var time = new DateTime(2013, 05, 28);
var cache = new SecurityCache();
cache.AddData(new Tick(time, config.Symbol, 20, 10));
var timeProvider = new ManualTimeProvider(time);
var futureTicker1 = "es vhle2yxr5blt";
TestMapFileResolver.MapFile = new MapFile(Futures.Indices.SP500EMini, new[]
{
new MapFileRow(Time.BeginningOfTime, Futures.Indices.SP500EMini, Exchange.CME),
new MapFileRow(new DateTime(2013,06,01), futureTicker1, Exchange.CME, DataMappingMode.FirstDayMonth),
new MapFileRow(new DateTime(2013,06,15), futureTicker1, Exchange.CME, DataMappingMode.OpenInterest),
new MapFileRow(new DateTime(2013,06,22), futureTicker1, Exchange.CME, DataMappingMode.LastTradingDay),
});
IEnumerator<BaseData> enumerator;
Assert.IsTrue(LiveAuxiliaryDataEnumerator.TryCreate(config, timeProvider, cache, new TestMapFileProvider(), TestGlobals.FactorFileProvider, time, out enumerator));
// get's mapped right away!
Assert.AreEqual(futureTicker1.ToUpper(), config.MappedSymbol);
Assert.AreEqual(1, symbolMaps.Count);
Assert.AreEqual(Symbols.ES_Future_Chain, symbolMaps[0].Old);
Assert.AreEqual(Futures.Indices.SP500EMini, symbolMaps[0].Old.ID.Symbol);
Assert.AreEqual(Symbols.ES_Future_Chain, symbolMaps[0].New);
Assert.AreEqual(futureTicker1.ToUpper(), symbolMaps[0].New.Underlying.ID.ToString());
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
var expectedMappingDate = DateTime.ParseExact(mappingDate, DateFormat.EightCharacter, CultureInfo.InvariantCulture);
if (delayed)
{
// we advance to the mapping date, without any new mapFile!
timeProvider.Advance(expectedMappingDate.ConvertToUtc(config.ExchangeTimeZone) - timeProvider.GetUtcNow() + Time.LiveAuxiliaryDataOffset);
}
else
{
// just advance a day to show nothing happens until mapping time
timeProvider.Advance(TimeSpan.FromDays(1));
}
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
var futureTicker2 = "es vk2zrh843z7l";
TestMapFileResolver.MapFile = new MapFile(Futures.Indices.SP500EMini, TestMapFileResolver.MapFile.Concat(
new[]
{
new MapFileRow(new DateTime(2013,09,01), futureTicker2, Exchange.CME, DataMappingMode.FirstDayMonth),
new MapFileRow(new DateTime(2013,09,14), futureTicker2, Exchange.CME, DataMappingMode.OpenInterest),
new MapFileRow(new DateTime(2013,09,21), futureTicker2, Exchange.CME, DataMappingMode.LastTradingDay),
}));
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
if (delayed)
{
// we got a new mapFile! advance the date and expect mapping to have happened
timeProvider.Advance(TimeSpan.FromDays(1));
}
else
{
// we advance to the mapping date
timeProvider.Advance(expectedMappingDate.ConvertToUtc(config.ExchangeTimeZone) - timeProvider.GetUtcNow() + Time.LiveAuxiliaryDataOffset);
}
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNotNull(enumerator.Current);
Assert.AreEqual(2, symbolMaps.Count);
Assert.AreEqual(Symbols.ES_Future_Chain, symbolMaps[1].Old);
Assert.AreEqual(futureTicker1.ToUpper(), symbolMaps[1].Old.Underlying.ID.ToString());
Assert.AreEqual(Symbols.ES_Future_Chain, symbolMaps[1].New);
Assert.AreEqual(futureTicker2.ToUpper(), symbolMaps[1].New.Underlying.ID.ToString());
Assert.AreEqual(futureTicker2.ToUpper(), config.MappedSymbol);
Assert.AreEqual(futureTicker2.ToUpper(), (enumerator.Current as SymbolChangedEvent).NewSymbol);
Assert.AreEqual(futureTicker1.ToUpper(), (enumerator.Current as SymbolChangedEvent).OldSymbol);
Assert.AreEqual(config.Symbol, (enumerator.Current as SymbolChangedEvent).Symbol);
Assert.AreEqual(timeProvider.GetUtcNow().Date, (enumerator.Current as SymbolChangedEvent).Time);
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
enumerator.Dispose();
}
[Test]
public void EmitsDelistingEventsBasedOnCurrentTime()
{
var config = new SubscriptionDataConfig(typeof(TradeBar),
Symbols.SPY_C_192_Feb19_2016,
Resolution.Daily,
TimeZones.NewYork,
TimeZones.NewYork,
true,
true,
false);
var delistingDate = config.Symbol.GetDelistingDate();
var time = delistingDate.AddDays(-10);
var cache = new SecurityCache();
cache.AddData(new Tick(DateTime.UtcNow, config.Symbol, 20, 10));
var timeProvider = new ManualTimeProvider(time);
IEnumerator<BaseData> enumerator;
Assert.IsTrue(LiveAuxiliaryDataEnumerator.TryCreate(config, timeProvider, cache, TestGlobals.MapFileProvider, TestGlobals.FactorFileProvider, config.Symbol.ID.Date, out enumerator));
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
// advance until delisting date, take into account 5 hour offset of NY + TradableDateOffset
timeProvider.Advance(TimeSpan.FromDays(10));
timeProvider.Advance(TimeSpan.FromHours(5));
timeProvider.Advance(Time.LiveAuxiliaryDataOffset);
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(DelistingType.Warning, (enumerator.Current as Delisting).Type);
Assert.AreEqual(config.Symbol, (enumerator.Current as Delisting).Symbol);
Assert.AreEqual(delistingDate, (enumerator.Current as Delisting).Time);
Assert.AreEqual(15, (enumerator.Current as Delisting).Price);
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
// when the day ends the delisted event will pass through, respecting the offset
timeProvider.Advance(TimeSpan.FromDays(1));
cache.AddData(new Tick(DateTime.UtcNow, config.Symbol, 40, 20));
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(DelistingType.Delisted, (enumerator.Current as Delisting).Type);
Assert.AreEqual(config.Symbol, (enumerator.Current as Delisting).Symbol);
Assert.AreEqual(delistingDate.AddDays(1), (enumerator.Current as Delisting).Time);
Assert.AreEqual(30, (enumerator.Current as Delisting).Price);
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
enumerator.Dispose();
}
[TestCase(false)]
[TestCase(true)]
public void EquityEmitsDelistingEventsBasedOnCurrentTime(bool delayed)
{
var config = new SubscriptionDataConfig(typeof(TradeBar),
Symbols.SPY,
Resolution.Daily,
TimeZones.NewYork,
TimeZones.NewYork,
true,
true,
false);
var time = new DateTime(2013, 05, 28);
var cache = new SecurityCache();
cache.AddData(new Tick(time, config.Symbol, 20, 10));
var timeProvider = new ManualTimeProvider(time);
TestMapFileResolver.MapFile = new MapFile(config.Symbol.ID.Symbol, new[]
{
new MapFileRow(Time.BeginningOfTime, config.Symbol.ID.Symbol),
new MapFileRow(Time.EndOfTime, config.Symbol.ID.Symbol),
});
IEnumerator<BaseData> enumerator;
Assert.IsTrue(LiveAuxiliaryDataEnumerator.TryCreate(config, timeProvider, cache, new TestMapFileProvider(), TestGlobals.FactorFileProvider, time, out enumerator));
// get's mapped right away!
Assert.AreEqual("SPY", config.MappedSymbol);
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
var delistingDate = time.AddDays(2);
var delistedMapFile = new MapFile(config.Symbol.ID.Symbol, new[]
{
new MapFileRow(Time.BeginningOfTime, config.Symbol.ID.Symbol),
new MapFileRow(delistingDate, config.Symbol.ID.Symbol),
});
// just advance a day to show nothing happens until mapping time
timeProvider.Advance(TimeSpan.FromDays(1));
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
if (!delayed)
{
TestMapFileResolver.MapFile = delistedMapFile;
}
// we advance to the mapping date, without any new mapFile!
timeProvider.Advance(delistingDate.ConvertToUtc(config.ExchangeTimeZone) - timeProvider.GetUtcNow() + Time.LiveAuxiliaryDataOffset);
if (delayed)
{
// nothing happens
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
TestMapFileResolver.MapFile = delistedMapFile;
timeProvider.Advance(Time.OneDay);
}
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNotNull(enumerator.Current);
var delisted = enumerator.Current as Delisting;
Assert.IsNotNull(delisted);
Assert.AreEqual(DelistingType.Warning, delisted.Type);
if (!delayed)
{
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNull(enumerator.Current);
// delisting passed
timeProvider.Advance(TimeSpan.FromDays(1));
}
Assert.IsTrue(enumerator.MoveNext());
Assert.IsNotNull(enumerator.Current);
delisted = enumerator.Current as Delisting;
Assert.IsNotNull(delisted);
Assert.AreEqual(DelistingType.Delisted, delisted.Type);
enumerator.Dispose();
}
private class TestMapFileProvider : IMapFileProvider
{
public void Initialize(IDataProvider dataProvider)
{
}
public MapFileResolver Get(AuxiliaryDataKey auxiliaryDataKey)
{
return new TestMapFileResolver();
}
}
private class TestMapFileResolver : MapFileResolver
{
public static MapFile MapFile { get; set; }
public TestMapFileResolver()
: base(new[] { MapFile })
{
}
}
}
}