/* * 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 QuantConnect.Util; using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; namespace QuantConnect.Data.UniverseSelection { /// /// Represents a universe of futures data /// public class FutureUniverse : BaseChainUniverseData { /// /// Cache for the symbols to avoid creating them multiple times /// /// Key: securityType, market, ticker, expiry private static readonly Dictionary<(SecurityType, string, string, DateTime), Symbol> _symbolsCache = new(); /// /// Creates a new instance of the class /// public FutureUniverse() { } /// /// Creates a new instance of the class /// public FutureUniverse(DateTime date, Symbol symbol, string csv) : base(date, symbol, csv) { } /// /// Creates a new instance of the class as a copy of the given instance /// public FutureUniverse(FutureUniverse other) : base(other) { } /// /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object /// each time it is called. /// /// Subscription data config setup object /// Stream reader of the source document /// Date of the requested data /// true if we're in live mode, false for backtesting mode /// Instance of the T:BaseData object generated by this line of the CSV [StubsIgnore] public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode) { if (stream == null || stream.EndOfStream) { return null; } if (stream.Peek() == '#') { // Skip header stream.ReadLine(); return null; } var expiry = stream.GetDateTime("yyyyMMdd"); var cacheKey = (config.SecurityType, config.Market, config.Symbol.ID.Symbol, expiry); if (!TryGetCachedSymbol(cacheKey, out var symbol)) { symbol = Symbol.CreateFuture(config.Symbol.ID.Symbol, config.Symbol.ID.Market, expiry); CacheSymbol(cacheKey, symbol); } return new FutureUniverse(date, symbol, stream.ReadLine()); } /// /// Creates a copy of the instance /// /// Clone of the instance public override BaseData Clone() { return new FutureUniverse(this); } /// /// Gets the default resolution for this data and security type /// /// This is a method and not a property so that python /// custom data types can override it public override Resolution DefaultResolution() { return Resolution.Daily; } /// /// Implicit conversion into /// /// The option universe data to be converted #pragma warning disable CA2225 // Operator overloads have named alternates public static implicit operator Symbol(FutureUniverse data) #pragma warning restore CA2225 // Operator overloads have named alternates { return data.Symbol; } /// /// Gets the CSV string representation of this universe entry /// public static string ToCsv(Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume, decimal? openInterest) { return $"{symbol.ID.Date:yyyyMMdd},{open},{high},{low},{close},{volume},{openInterest}"; } /// /// Gets the CSV header string for this universe entry /// public static string CsvHeader => "expiry,open,high,low,close,volume,open_interest"; /// /// Tries to get a symbol from the cache /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static bool TryGetCachedSymbol((SecurityType, string, string, DateTime) key, out Symbol symbol) { lock (_symbolsCache) { return _symbolsCache.TryGetValue(key, out symbol); } } /// /// Caches a symbol /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static void CacheSymbol((SecurityType, string, string, DateTime) key, Symbol symbol) { lock (_symbolsCache) { // limit the cache size to help with memory usage if (_symbolsCache.Count >= 100000) { _symbolsCache.Clear(); } _symbolsCache.TryAdd(key, symbol); } } } }