/* * 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; using System.Collections.Generic; using System.Linq; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Util; namespace QuantConnect.Data { /// /// Provides extension methods to slices and slice enumerables /// public static class SliceExtensions { /// /// Selects into the slice and returns the TradeBars that have data in order /// /// The enumerable of slice /// An enumerable of TradeBars public static IEnumerable TradeBars(this IEnumerable slices) { return slices.Where(x => x.Bars.Count > 0).Select(x => x.Bars); } /// /// Selects into the slice and returns the Ticks that have data in order /// /// The enumerable of slice /// An enumerable of Ticks public static IEnumerable Ticks(this IEnumerable slices) { return slices.Where(x => x.Ticks.Count > 0).Select(x => x.Ticks); } /// /// Gets the data dictionaries or points of the requested type in each slice /// /// The enumerable of slice /// An enumerable of data dictionary or data point of the requested type public static IEnumerable> GetUniverseData(this IEnumerable slices) { return slices.SelectMany(x => x.AllData).Select(x => { // we wrap the universe data collection into a data dictionary so it fits the api pattern return new DataDictionary(new[] { (BaseDataCollection)x }, (y) => y.Symbol); }); } /// /// Gets the data dictionaries or points of the requested type in each slice /// /// The enumerable of slice /// Data type of the data that will be fetched /// The symbol to retrieve /// An enumerable of data dictionary or data point of the requested type public static IEnumerable Get(this IEnumerable slices, Type type, Symbol symbol = null) { var result = slices.Select(x => x.Get(type)); if (symbol == null) { return result; } return result.Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]); } /// /// Gets an enumerable of TradeBar for the given symbol. This method does not verify /// that the specified symbol points to a TradeBar /// /// The enumerable of slice /// The symbol to retrieve /// An enumerable of TradeBar for the matching symbol, of no TradeBar found for symbol, empty enumerable is returned public static IEnumerable Get(this IEnumerable slices, Symbol symbol) { return slices.TradeBars().Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]); } /// /// Gets an enumerable of T for the given symbol. This method does not vify /// that the specified symbol points to a T /// /// The data type /// The data dictionary enumerable to access /// The symbol to retrieve /// An enumerable of T for the matching symbol, if no T is found for symbol, empty enumerable is returned public static IEnumerable Get(this IEnumerable> dataDictionaries, Symbol symbol) where T : IBaseData { return dataDictionaries.Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]); } /// /// Gets an enumerable of decimals by accessing the specified field on data for the symbol /// /// The data type /// An enumerable of data dictionaries /// The symbol to retrieve /// The field to access /// An enumerable of decimals public static IEnumerable Get(this IEnumerable> dataDictionaries, Symbol symbol, string field) { Func selector; if (typeof (DynamicData).IsAssignableFrom(typeof (T))) { selector = data => { var dyn = (DynamicData) (object) data; return (decimal) dyn.GetProperty(field); }; } else if (typeof (T) == typeof (List)) { // perform the selection on the last tick // NOTE: This is a known bug, should be updated to perform the selection on each item in the list var dataSelector = (Func) ExpressionBuilder.MakePropertyOrFieldSelector(typeof (Tick), field).Compile(); selector = ticks => dataSelector(((List) (object) ticks).Last()); } else { selector = (Func) ExpressionBuilder.MakePropertyOrFieldSelector(typeof (T), field).Compile(); } foreach (var dataDictionary in dataDictionaries) { T item; if (dataDictionary.TryGetValue(symbol, out item)) { yield return selector(item); } } } /// /// Gets the data dictionaries of the requested type in each slice /// /// The data type /// The enumerable of slice /// An enumerable of data dictionary of the requested type public static IEnumerable> Get(this IEnumerable slices) where T : IBaseData { return slices.Select(x => x.Get()).Where(x => x.Count > 0); } /// /// Gets an enumerable of T by accessing the slices for the requested symbol /// /// The data type /// The enumerable of slice /// The symbol to retrieve /// An enumerable of T by accessing each slice for the requested symbol public static IEnumerable Get(this IEnumerable slices, Symbol symbol) where T : IBaseData { return slices.Select(x => x.Get()).Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]); } /// /// Gets an enumerable of decimal by accessing the slice for the symbol and then retrieving the specified /// field on each piece of data /// /// The enumerable of slice /// The symbol to retrieve /// The field selector used to access the dats /// An enumerable of decimal public static IEnumerable Get(this IEnumerable slices, Symbol symbol, Func field) { foreach (var slice in slices) { dynamic item; if (slice.TryGetValue(symbol, out item)) { if (item is List) yield return field(item.Last()); else yield return field(item); } } } /// /// Tries to get the data for the specified symbol and type /// /// The type of data we want, for example, or , etc... /// The slice /// The symbol data is sought for /// The found data /// True if data was found for the specified type and symbol public static bool TryGet(this Slice slice, Symbol symbol, out T data) where T : IBaseData { data = default(T); var typeData = slice.Get(typeof(T)) as DataDictionary; if (typeData.ContainsKey(symbol)) { data = typeData[symbol]; return true; } return false; } /// /// Tries to get the data for the specified symbol and type /// /// The slice /// The type of data we seek /// The symbol data is sought for /// The found data /// True if data was found for the specified type and symbol public static bool TryGet(this Slice slice, Type type, Symbol symbol, out dynamic data) { data = null; var typeData = slice.Get(type); if (typeData.ContainsKey(symbol)) { data = typeData[symbol]; return true; } return false; } /// /// Converts the specified enumerable of decimals into a double array /// /// The enumerable of decimal /// Double array representing the enumerable of decimal public static double[] ToDoubleArray(this IEnumerable decimals) { return decimals.Select(x => (double) x).ToArray(); } /// /// Loops through the specified slices and pushes the data into the consolidators. This can be used to /// easily warm up indicators from a history call that returns slice objects. /// /// The data to send into the consolidators, likely result of a history request /// Dictionary of consolidators keyed by symbol public static void PushThroughConsolidators(this IEnumerable slices, Dictionary consolidatorsBySymbol) { PushThroughConsolidators(slices, symbol => { IDataConsolidator consolidator; consolidatorsBySymbol.TryGetValue(symbol, out consolidator); return consolidator; }); } /// /// Loops through the specified slices and pushes the data into the consolidators. This can be used to /// easily warm up indicators from a history call that returns slice objects. /// /// The data to send into the consolidators, likely result of a history request /// Delegate that fetches the consolidators by a symbol public static void PushThroughConsolidators(this IEnumerable slices, Func consolidatorsProvider) { slices.PushThrough(data => consolidatorsProvider(data?.Symbol)?.Update(data)); } /// /// Loops through the specified slices and pushes the data into the consolidators. This can be used to /// easily warm up indicators from a history call that returns slice objects. /// /// The data to send into the consolidators, likely result of a history request /// Delegate handles each data piece from the slice /// Defines the type of the data that should be pushed public static void PushThrough(this IEnumerable slices, Action handler, Type dataType = null) { if (dataType != null) { Func> dataSelector = default; if (dataType == typeof(QuoteBar)) { dataSelector = slice => slice.QuoteBars.Values; } else if (dataType == typeof(Tick)) { dataSelector = slice => slice.Ticks.Values.Select(x => x.Last()); } else if (dataType == typeof(TradeBar)) { dataSelector = slice => slice.Bars.Values; } else { dataSelector = slice => slice.Get(dataType).Values; } foreach (var slice in slices) { foreach (BaseData baseData in dataSelector(slice)) { handler(baseData); } } } else { foreach (var slice in slices) { foreach (var symbol in slice.Keys) { dynamic value; if (!slice.TryGetValue(symbol, out value)) { continue; } var list = value as IList; var data = (BaseData)(list != null ? list[list.Count - 1] : value); handler(data); } } } } } }