/* * 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.IO; using NUnit.Framework; using System.Threading; using QuantConnect.Util; using System.Threading.Tasks; using System.Collections.Generic; using QuantConnect.Lean.Engine.DataFeeds; namespace QuantConnect.Tests.Engine.DataProviders { [TestFixture, Explicit("Requires configured api access, and also makes calls to data endpoints which are charging transactions")] public class ApiDataProviderTests { private ApiDataProvider _dataProvider; [OneTimeSetUp] public void Setup() { _dataProvider = new ApiDataProvider(); } // Note: Most of these cases reflect real data files included in the repo. // May be useful for other tests. Only ones that aren't included are map and factor file zips // CFD Cases [TestCase("cfd/oanda/daily/xauusd.zip", 2, true)] // Daily stale data [TestCase("cfd/oanda/daily/xauusd.zip", 0, false)] // Daily fresh data [TestCase("cfd/oanda/hour/xauusd.zip", 6, true)] // Hourly stale data [TestCase("cfd/oanda/hour/xauusd.zip", 0, false)] // Hourly fresh data [TestCase("cfd/oanda/minute/xauusd/20140501_quote.zip", 10, false)] // Date based minute data [TestCase("cfd/oanda/second/xauusd/20140501_quote.zip", 10, false)] // Date based second data [TestCase("cfd/oanda/tick/xauusd/20140501_quote.zip", 10, false)] // Date based tick data // Crypto Cases [TestCase("crypto/coinbase/daily/btcusd_quote.zip", 6, true)] // Daily stale data [TestCase("crypto/coinbase/daily/btcusd_quote.zip", 0, false)] // Daily fresh data [TestCase("crypto/coinbase/minute/btcusd/20161007_trade.zip", 6, false)] // Date based minute data [TestCase("crypto/coinbase/second/btcusd/20161007_trade.zip", 6, false)] // Date based second data // Equity Cases [TestCase("equity/usa/daily/aaa.zip", 4, true)] // Daily stale data [TestCase("equity/usa/daily/aaa.zip", 0, false)] // Daily fresh data [TestCase("equity/usa/hour/aapl.zip", 4, true)] // Hourly stale data [TestCase("equity/usa/hour/aapl.zip", 0, false)] // Hourly fresh data [TestCase("equity/usa/minute/aapl/20140605_quote.zip", 0, false)] // Date Based minute data [TestCase("equity/usa/second/aig/20131004_trade.zip", 0, false)] // Date Based second data [TestCase("equity/usa/tick/aig/20131007_quote.zip", 0, false)] // Date Based tick data [TestCase("equity/usa/factor_files/aaa.csv", 5, true)] // Stale factor file [TestCase("equity/usa/factor_files/aaa.csv", 0, false)] // Fresh factor file [TestCase("equity/usa/factor_files/factor_files_20210601.zip", 25, false)] // Date based factor files ** Not included in repo [TestCase("equity/usa/map_files/aaa.csv", 5, true)] // Stale map file [TestCase("equity/usa/map_files/aaa.csv", 0, false)] // Fresh map file [TestCase("equity/usa/map_files/map_files_20210601.zip", 12, false)] // Date based map files ** Not included in repo [TestCase("equity/usa/fundamental/coarse/20140324.csv", 5, false)] // Date based fundamental coarse files [TestCase("equity/usa/fundamental/fine/aapl/20140228.csv", 30, false)] // Date based fundamental fine files (Always false because we don't support fine files) [TestCase("equity/usa/shortable/testbrokerage/dates/20140325.csv", 3, false)] // Date based shortable files [TestCase("equity/usa/shortable/testbrokerage/symbols/aapl.csv", 10, true)] // Symbol based shortable files [TestCase("equity/usa/universes/daily/qctest/20131007.csv", 10, false)] // Date based universe file // Equity Option Cases [TestCase("option/usa/minute/aapl/20140606_openinterest_american.zip", 10, false)] // Date based minute data // Forex Cases [TestCase("forex/oanda/daily/eurgbp.zip", 0, false)] // Daily fresh Forex data [TestCase("forex/oanda/daily/eurgbp.zip", 10, true)] // Daily stale Forex data [TestCase("forex/oanda/hour/eurusd.zip", 0, false)] // Hourly fresh Forex data [TestCase("forex/oanda/hour/eurusd.zip", 10, true)] // Hourly stale Forex data [TestCase("forex/oanda/minute/eurusd/20140501_quote.zip", 10, false)] // Date based Forex minute data [TestCase("forex/oanda/second/eurusd/20140501_quote.zip", 10, false)] // Date based Forex second data [TestCase("forex/oanda/tick/eurusd/20140501_quote.zip", 10, false)] // Date based Forex tick data // Price Future Cases False because Unsupported [TestCase("future/cboe/margins/VX.csv", 0, false)] // Fresh Margins data [TestCase("future/cboe/margins/VX.csv", 10, false)] // Stale Margins data [TestCase("future/comex/minute/gc/20131007_openinterest.zip", 10, false)] // Date based minute data [TestCase("future/comex/tick/gc/20131007_openinterest.zip", 10, false)] // Date based tick data // Future Option Cases ** All False because Unsupported [TestCase("futureoption/cme/minute/es/20200320/20200105_quote_american.zip", 10, false)] // Date based minute data // Index Cases / Index Option Cases *Not included because not allowed to download public void ShouldDownloadTest(string file, int days, bool expected) { // First create our test file and set its time var path = Path.Combine("./ApiDataTests", file); var time = DateTime.Now - TimeSpan.FromDays(days); var directory = Path.GetDirectoryName(path); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.Create(path).Dispose(); File.SetLastWriteTime(path, time); using var dataProvider = new ApiDataProviderTest(); var result = dataProvider.NeedToDownloadExposed(path); Assert.AreEqual(expected, result); // Cleanup after test File.Delete(path); } // Alternative [TestCase("alternative/sec/aapl/19980108_8K.zip")] // CFD [TestCase("cfd/oanda/daily/ch20hkd.zip")] // Crypto [TestCase("crypto/coinbase/minute/btcusd/20150114_trade.zip")] // Equities [TestCase("equity/usa/shortable/atreyu/dates/20180117.csv")] [TestCase("equity/usa/factor_files/tsla.csv")] [TestCase("equity/usa/factor_files/factor_files_20210601.zip")] [TestCase("equity/usa/map_files/googl.csv")] [TestCase("equity/usa/map_files/map_files_20210601.zip")] // Equity Options [TestCase("option/usa/minute/aapl/20100603_quote_american.zip")] // Forex [TestCase("forex/oanda/minute/eurusd/20020516_quote.zip")] // Price Futures * False because unsupported [TestCase("future/cbot/minute/zs/20090501_trade.zip", false)] [TestCase("future/sgx/margins/IN.csv", false)] // Auxiliary Data Future Cases true [TestCase("future/comex/map_files/map_files_20211225.zip", true)] [TestCase("future/cme/factor_files/factor_files_20211225.zip", true)] // Future Options * False because unsupported [TestCase("futureoption/comex/minute/og/20120227/20120105_quote_american.zip", false)] // Index * False because unsupported [TestCase("index/usa/minute/spx/19980217_trade.zip", false)] // Index Options * False because unsupported [TestCase("indexoption/usa/minute/spx/20100603_quote_european.zip", false)] public void DownloadFiles(string file, bool expectedToExist = true) { var path = Path.Combine(Globals.DataFolder, file); // Delete it if we already have it if (File.Exists(path)) { File.Delete(path); Assert.IsFalse(File.Exists(path)); } // Go get the file var stream = _dataProvider.Fetch(path); if (expectedToExist) { Assert.IsNotNull(stream); stream.Dispose(); Assert.IsTrue(File.Exists(path)); } else { Assert.IsNull(stream); Assert.IsFalse(File.Exists(path)); } } [Test] public void DownloadsFileOnceConcurrently() { # pragma warning disable CA2000 var dataProvider = new ApiDataProviderTest(); #pragma warning restore CA2000 var tasks = new List(); for (var i = 0; i < 10; i++) { tasks.Add(Task.Factory.StartNew(() => dataProvider.Fetch(Path.Combine(Globals.DataFolder, "equity", Market.USA, "hour", "f.zip")))); } tasks.ForEach(task => task.Wait()); dataProvider.DisposeSafely(); Assert.AreEqual(1, dataProvider.DownLoadCount); } private class ApiDataProviderTest : ApiDataProvider { public int DownLoadCount; public bool NeedToDownloadExposed(string filePath) { return base.NeedToDownload(filePath); } protected override bool DownloadData(string filePath) { Interlocked.Increment(ref DownLoadCount); // simulate download delay Thread.Sleep(100); return false; } } } }