/* * 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.IO; using System.Linq; using Ionic.Zip; using NodaTime; using QuantConnect.Data; using QuantConnect.Logging; using QuantConnect.Securities; using QuantConnect.Util; namespace QuantConnect.ToolBox { /// /// This class reads data directly from disk and returns the data without the data /// entering the Lean data enumeration stack /// public class LeanDataReader { private readonly DateTime _date; private readonly string _zipPath; private readonly string _zipentry; private readonly SubscriptionDataConfig _config; /// /// The LeanDataReader constructor /// /// The /// The that will be read /// The that will be read /// The that will be read /// The root data folder public LeanDataReader(SubscriptionDataConfig config, Symbol symbol, Resolution resolution, DateTime date, string dataFolder) { _date = date; _zipPath = LeanData.GenerateZipFilePath(dataFolder, symbol, date, resolution, config.TickType); _zipentry = LeanData.GenerateZipEntryName(symbol, date, resolution, config.TickType); _config = config; } /// /// Initialize a instance of LeanDataReader from a path to a zipped data file. /// It also supports declaring the zip entry CSV file for options and futures. /// /// Absolute or relative path to a zipped data file, optionally the zip entry file can be declared by using '#' as separator. /// /// var dataReader = LeanDataReader("../relative/path/to/file.zip") /// var dataReader = LeanDataReader("absolute/path/to/file.zip#zipEntry.csv") /// public LeanDataReader(string filepath) { Symbol symbol; DateTime date; Resolution resolution; string zipEntry = null; var isFutureOrOption = filepath.Contains('#', StringComparison.InvariantCulture); if (isFutureOrOption) { zipEntry = filepath.Split('#')[1]; filepath = filepath.Split('#')[0]; } var fileInfo = new FileInfo(filepath); if (!LeanData.TryParsePath(fileInfo.FullName, out symbol, out date, out resolution, out var tickType, out var dataType)) { throw new ArgumentException($"File {filepath} cannot be parsed."); } if (isFutureOrOption) { symbol = LeanData.ReadSymbolFromZipEntry(symbol, resolution, zipEntry); } var marketHoursDataBase = MarketHoursDatabase.FromDataFolder(); var dataTimeZone = marketHoursDataBase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType); var exchangeTimeZone = marketHoursDataBase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType).TimeZone; var config = new SubscriptionDataConfig(dataType, symbol, resolution, dataTimeZone, exchangeTimeZone, tickType: tickType, fillForward: false, extendedHours: true, isInternalFeed: true); _date = date; _zipPath = fileInfo.FullName; _zipentry = zipEntry; _config = config; } /// /// Enumerate over the tick zip file and return a list of BaseData. /// /// IEnumerable of ticks public IEnumerable Parse() { if (!File.Exists(_zipPath)) { Log.Error($"LeanDataReader.Parse(): File does not exist: {_zipPath}"); yield break; } var factory = (BaseData) ObjectActivator.GetActivator(_config.Type).Invoke(new object[0]); if (_config.Type.ImplementsStreamReader()) { using (var zip = new ZipFile(_zipPath)) { foreach (var zipEntry in zip.Where(x => _zipentry == null || string.Equals(x.FileName, _zipentry, StringComparison.OrdinalIgnoreCase))) { // we get the contract symbol from the zip entry if not already provided with the zip entry var symbol = _config.Symbol; if(_zipentry == null && (_config.SecurityType == SecurityType.Future || _config.SecurityType.IsOption())) { symbol = LeanData.ReadSymbolFromZipEntry(_config.Symbol, _config.Resolution, zipEntry.FileName); } using (var entryReader = new StreamReader(zipEntry.OpenReader())) { while (!entryReader.EndOfStream) { var dataPoint = factory.Reader(_config, entryReader, _date, false); dataPoint.Symbol = symbol; yield return dataPoint; } } } } } // for futures and options if no entry was provided we just read all else if (_zipentry == null && (_config.SecurityType == SecurityType.Future || _config.SecurityType.IsOption())) { foreach (var entries in Compression.Unzip(_zipPath)) { // we get the contract symbol from the zip entry var symbol = LeanData.ReadSymbolFromZipEntry(_config.Symbol, _config.Resolution, entries.Key); foreach (var line in entries.Value) { var dataPoint = factory.Reader(_config, line, _date, false); dataPoint.Symbol = symbol; yield return dataPoint; } } } else { ZipFile zipFile; using (var unzipped = Compression.Unzip(_zipPath, _zipentry, out zipFile)) { if (unzipped == null) yield break; string line; while ((line = unzipped.ReadLine()) != null) { yield return factory.Reader(_config, line, _date, false); } } zipFile.Dispose(); } } /// /// Returns the data time zone /// /// representing the data timezone public DateTimeZone GetDataTimeZone() { return _config.DataTimeZone; } /// /// Returns the Exchange time zone /// /// representing the exchange timezone public DateTimeZone GetExchangeTimeZone() { return _config.ExchangeTimeZone; } } }