/* * 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.Linq; using Newtonsoft.Json; using NodaTime; using NUnit.Framework; using QuantConnect.Algorithm; using QuantConnect.AlgorithmFactory.Python.Wrappers; using QuantConnect.Configuration; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Lean.Engine.DataFeeds; using QuantConnect.Securities; using QuantConnect.Tests.Engine.DataFeeds; using QuantConnect.Util; using Bitcoin = QuantConnect.Algorithm.CSharp.LiveTradingFeaturesAlgorithm.Bitcoin; using HistoryRequest = QuantConnect.Data.HistoryRequest; namespace QuantConnect.Tests.Algorithm { [TestFixture] public class AlgorithmAddDataTests { [Test] public void DefaultDataFeeds_CanBeOverwritten_Successfully() { Config.Set("security-data-feeds", "{ Forex: [\"Trade\"] }"); var algo = new QCAlgorithm(); algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo)); // forex defult - should be tradebar var forexTrade = algo.AddForex("EURUSD"); Assert.IsTrue(forexTrade.Subscriptions.Count() == 1); Assert.IsTrue(GetMatchingSubscription(forexTrade, typeof(QuoteBar)) != null); // Change var dataFeedsConfigString = Config.Get("security-data-feeds"); Dictionary> dataFeeds = new Dictionary>(); if (dataFeedsConfigString != string.Empty) { dataFeeds = JsonConvert.DeserializeObject>>(dataFeedsConfigString); } algo.SetAvailableDataTypes(dataFeeds); // new forex - should be quotebar var forexQuote = algo.AddForex("EURUSD"); Assert.IsTrue(forexQuote.Subscriptions.Count() == 1); Assert.IsTrue(GetMatchingSubscription(forexQuote, typeof(TradeBar)) != null); } [Test] public void DefaultDataFeeds_AreAdded_Successfully() { var algo = new QCAlgorithm(); algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo)); // forex var forex = algo.AddSecurity(SecurityType.Forex, "eurusd"); Assert.IsTrue(forex.Subscriptions.Count() == 1); Assert.IsTrue(GetMatchingSubscription(forex, typeof(QuoteBar)) != null); // equity var equity = algo.AddSecurity(SecurityType.Equity, "goog"); Assert.IsTrue(equity.Subscriptions.Count() == 1); Assert.IsTrue(GetMatchingSubscription(equity, typeof(TradeBar)) != null); // option var option = algo.AddSecurity(SecurityType.Option, "goog"); Assert.IsTrue(option.Subscriptions.Count() == 1); Assert.IsTrue(GetMatchingSubscription(option, typeof(ZipEntryName)) != null); // cfd var cfd = algo.AddSecurity(SecurityType.Cfd, "abc"); Assert.IsTrue(cfd.Subscriptions.Count() == 1); Assert.IsTrue(GetMatchingSubscription(cfd, typeof(QuoteBar)) != null); // future var future = algo.AddSecurity(SecurityType.Future, "ES"); Assert.IsTrue(future.Subscriptions.Count() == 1); Assert.IsTrue(future.Subscriptions.FirstOrDefault(x => typeof(ZipEntryName).IsAssignableFrom(x.Type)) != null); // Crypto var crypto = algo.AddSecurity(SecurityType.Crypto, "btcusd", Resolution.Daily); Assert.IsTrue(crypto.Subscriptions.Count() == 2); Assert.IsTrue(GetMatchingSubscription(crypto, typeof(QuoteBar)) != null); Assert.IsTrue(GetMatchingSubscription(crypto, typeof(TradeBar)) != null); } [Test] public void CustomDataTypes_AreAddedToSubscriptions_Successfully() { var qcAlgorithm = new QCAlgorithm(); qcAlgorithm.SubscriptionManager.SetDataManager(new DataManagerStub(qcAlgorithm)); // Add a bitcoin subscription qcAlgorithm.AddData("BTC"); var bitcoinSubscription = qcAlgorithm.SubscriptionManager.Subscriptions.FirstOrDefault(x => x.Type == typeof(Bitcoin)); Assert.AreEqual(bitcoinSubscription.Type, typeof(Bitcoin)); // Add a quandl subscription qcAlgorithm.AddData("EURCAD"); var quandlSubscription = qcAlgorithm.SubscriptionManager.Subscriptions.FirstOrDefault(x => x.Type == typeof(Quandl)); Assert.AreEqual(quandlSubscription.Type, typeof(Quandl)); } [Test] public void OnEndOfTimeStepSeedsUnderlyingSecuritiesThatHaveNoData() { var qcAlgorithm = new QCAlgorithm(); qcAlgorithm.SubscriptionManager.SetDataManager(new DataManagerStub(qcAlgorithm, new MockDataFeed())); qcAlgorithm.SetLiveMode(true); var testHistoryProvider = new TestHistoryProvider(); qcAlgorithm.HistoryProvider = testHistoryProvider; var option = qcAlgorithm.AddSecurity(SecurityType.Option, testHistoryProvider.underlyingSymbol); var option2 = qcAlgorithm.AddSecurity(SecurityType.Option, testHistoryProvider.underlyingSymbol2); Assert.IsFalse(qcAlgorithm.Securities.ContainsKey(option.Symbol.Underlying)); Assert.IsFalse(qcAlgorithm.Securities.ContainsKey(option2.Symbol.Underlying)); qcAlgorithm.OnEndOfTimeStep(); var data = qcAlgorithm.Securities[testHistoryProvider.underlyingSymbol].GetLastData(); var data2 = qcAlgorithm.Securities[testHistoryProvider.underlyingSymbol2].GetLastData(); Assert.IsNotNull(data); Assert.IsNotNull(data2); Assert.AreEqual(data.Price, 2); Assert.AreEqual(data2.Price, 3); } [Test] public void OnEndOfTimeStepDoesNotThrowWhenSeedsSameUnderlyingForTwoSecurities() { var qcAlgorithm = new QCAlgorithm(); qcAlgorithm.SubscriptionManager.SetDataManager(new DataManagerStub(qcAlgorithm, new MockDataFeed())); qcAlgorithm.SetLiveMode(true); var testHistoryProvider = new TestHistoryProvider(); qcAlgorithm.HistoryProvider = testHistoryProvider; var option = qcAlgorithm.AddOption(testHistoryProvider.underlyingSymbol); var symbol = Symbol.CreateOption(testHistoryProvider.underlyingSymbol, Market.USA, OptionStyle.American, OptionRight.Call, 1, new DateTime(2015, 12, 24)); var symbol2 = Symbol.CreateOption(testHistoryProvider.underlyingSymbol, Market.USA, OptionStyle.American, OptionRight.Put, 1, new DateTime(2015, 12, 24)); var optionContract = qcAlgorithm.AddOptionContract(symbol, Resolution.Daily); var optionContract2 = qcAlgorithm.AddOptionContract(symbol2, Resolution.Minute); qcAlgorithm.OnEndOfTimeStep(); var data = qcAlgorithm.Securities[testHistoryProvider.underlyingSymbol].GetLastData(); Assert.AreEqual(testHistoryProvider.LastResolutionRequest, Resolution.Minute); Assert.IsNotNull(data); Assert.AreEqual(data.Price, 2); } [Test] public void PythonCustomDataTypes_AreAddedToSubscriptions_Successfully() { var qcAlgorithm = new AlgorithmPythonWrapper("Test_CustomDataAlgorithm"); qcAlgorithm.SubscriptionManager.SetDataManager(new DataManagerStub(qcAlgorithm)); // Initialize contains the statements: // self.AddData(Nifty, "NIFTY") // self.AddData(QuandlFuture, "SCF/CME_CL1_ON", Resolution.Daily) qcAlgorithm.Initialize(); var niftySubscription = qcAlgorithm.SubscriptionManager.Subscriptions.FirstOrDefault(x => x.Symbol.Value == "NIFTY"); Assert.IsNotNull(niftySubscription); var niftyFactory = (BaseData)ObjectActivator.GetActivator(niftySubscription.Type).Invoke(new object[] { niftySubscription.Type }); Assert.DoesNotThrow(() => niftyFactory.GetSource(niftySubscription, DateTime.UtcNow, false)); var quandlSubscription = qcAlgorithm.SubscriptionManager.Subscriptions.FirstOrDefault(x => x.Symbol.Value == "SCF/CME_CL1_ON"); Assert.IsNotNull(quandlSubscription); var quandlFactory = (BaseData)ObjectActivator.GetActivator(quandlSubscription.Type).Invoke(new object[] { quandlSubscription.Type }); Assert.DoesNotThrow(() => quandlFactory.GetSource(quandlSubscription, DateTime.UtcNow, false)); } [Test] public void PythonCustomDataTypes_AreAddedToConsolidator_Successfully() { var qcAlgorithm = new AlgorithmPythonWrapper("Test_CustomDataAlgorithm"); qcAlgorithm.SubscriptionManager.SetDataManager(new DataManagerStub(qcAlgorithm)); // Initialize contains the statements: // self.AddData(Nifty, "NIFTY") // self.AddData(QuandlFuture, "SCF/CME_CL1_ON", Resolution.Daily) qcAlgorithm.Initialize(); var niftyConsolidator = new DynamicDataConsolidator(TimeSpan.FromDays(2)); Assert.DoesNotThrow(() => qcAlgorithm.SubscriptionManager.AddConsolidator("NIFTY", niftyConsolidator)); var quandlConsolidator = new DynamicDataConsolidator(TimeSpan.FromDays(2)); Assert.DoesNotThrow(() => qcAlgorithm.SubscriptionManager.AddConsolidator("SCF/CME_CL1_ON", quandlConsolidator)); } private static SubscriptionDataConfig GetMatchingSubscription(Security security, Type type) { // find a subscription matchin the requested type with a higher resolution than requested return (from sub in security.Subscriptions.OrderByDescending(s => s.Resolution) where type.IsAssignableFrom(sub.Type) select sub).FirstOrDefault(); } private class TestHistoryProvider : HistoryProviderBase { public string underlyingSymbol = "GOOG"; public string underlyingSymbol2 = "AAPL"; public override int DataPointCount { get; } public Resolution LastResolutionRequest; public override void Initialize(HistoryProviderInitializeParameters parameters) { throw new NotImplementedException(); } public override IEnumerable GetHistory(IEnumerable requests, DateTimeZone sliceTimeZone) { var now = DateTime.UtcNow; LastResolutionRequest = requests.First().Resolution; var tradeBar1 = new TradeBar(now, underlyingSymbol, 1, 1, 1, 1, 1, TimeSpan.FromDays(1)); var tradeBar2 = new TradeBar(now, underlyingSymbol2, 3, 3, 3, 3, 3, TimeSpan.FromDays(1)); var slice1 = new Slice(now, new List { tradeBar1, tradeBar2 }, new TradeBars(now), new QuoteBars(), new Ticks(), new OptionChains(), new FuturesChains(), new Splits(), new Dividends(now), new Delistings(), new SymbolChangedEvents()); var tradeBar1_2 = new TradeBar(now, underlyingSymbol, 2, 2, 2, 2, 2, TimeSpan.FromDays(1)); var slice2 = new Slice(now, new List { tradeBar1_2 }, new TradeBars(now), new QuoteBars(), new Ticks(), new OptionChains(), new FuturesChains(), new Splits(), new Dividends(now), new Delistings(), new SymbolChangedEvents()); return new[] { slice1, slice2 }; } } } }