/*
* 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 System.Reflection;
using QuantConnect.Data.Custom;
using QuantConnect.Data.Market;
using QuantConnect.Python;
namespace QuantConnect.Data
{
///
/// Provides a data structure for all of an algorithm's data at a single time step
///
public class Slice : ExtendedDictionary, IEnumerable>
{
private readonly Ticks _ticks;
private readonly TradeBars _bars;
private readonly QuoteBars _quoteBars;
private readonly OptionChains _optionChains;
private readonly FuturesChains _futuresChains;
// aux data
private readonly Splits _splits;
private readonly Dividends _dividends;
private readonly Delistings _delistings;
private readonly SymbolChangedEvents _symbolChangedEvents;
// string -> data for non-tick data
// string -> list{data} for tick data
private readonly Lazy> _data;
// Quandl -> DataDictonary
private Dictionary _dataByType;
///
/// Gets the timestamp for this slice of data
///
public DateTime Time
{
get; private set;
}
///
/// Gets whether or not this slice has data
///
public bool HasData
{
get; private set;
}
///
/// Gets the for this slice of data
///
public TradeBars Bars
{
get { return _bars; }
}
///
/// Gets the for this slice of data
///
public QuoteBars QuoteBars
{
get { return _quoteBars; }
}
///
/// Gets the for this slice of data
///
public Ticks Ticks
{
get { return _ticks; }
}
///
/// Gets the for this slice of data
///
public OptionChains OptionChains
{
get { return _optionChains; }
}
///
/// Gets the for this slice of data
///
public FuturesChains FuturesChains
{
get { return _futuresChains; }
}
///
/// Gets the for this slice of data
///
public FuturesChains FutureChains
{
get { return _futuresChains; }
}
///
/// Gets the for this slice of data
///
public Splits Splits
{
get { return _splits; }
}
///
/// Gets the for this slice of data
///
public Dividends Dividends
{
get { return _dividends; }
}
///
/// Gets the for this slice of data
///
public Delistings Delistings
{
get { return _delistings; }
}
///
/// Gets the for this slice of data
///
public SymbolChangedEvents SymbolChangedEvents
{
get { return _symbolChangedEvents; }
}
///
/// Gets the number of symbols held in this slice
///
public virtual int Count
{
get { return _data.Value.Count; }
}
///
/// Gets all the symbols in this slice
///
public virtual IReadOnlyList Keys
{
get { return new List(_data.Value.Keys); }
}
///
/// Gets an containing the Symbol objects of the .
///
///
/// An containing the Symbol objects of the object that implements .
///
protected override IEnumerable GetKeys => _data.Value.Keys;
///
/// Gets an containing the values in the .
///
///
/// An containing the values in the object that implements .
///
protected override IEnumerable GetValues => GetKeyValuePairEnumerable().Select(data => (dynamic)data.Value);
///
/// Gets a list of all the data in this slice
///
public virtual IReadOnlyList Values
{
get { return GetKeyValuePairEnumerable().Select(x => x.Value).ToList(); }
}
///
/// Initializes a new instance of the class, lazily
/// instantiating the and
/// collections on demand
///
/// The timestamp for this slice of data
/// The raw data in this slice
public Slice(DateTime time, IEnumerable data)
: this(time, data.ToList())
{
}
///
/// Initializes a new instance of the class, lazily
/// instantiating the and
/// collections on demand
///
/// The timestamp for this slice of data
/// The raw data in this slice
public Slice(DateTime time, List data)
: this(time, data, CreateCollection(data),
CreateCollection(data),
CreateTicksCollection(data),
CreateCollection(data),
CreateCollection(data),
CreateCollection(data),
CreateCollection(data),
CreateCollection(data),
CreateCollection(data))
{
}
///
/// Initializes a new instance used by the
///
/// slice object to wrap
/// This is required so that python slice enumeration works correctly since it relies on the private collection
protected Slice(Slice slice)
{
Time = slice.Time;
_dataByType = slice._dataByType;
_data = slice._data;
HasData = slice.HasData;
_ticks = slice._ticks;
_bars = slice._bars;
_quoteBars = slice._quoteBars;
_optionChains = slice._optionChains;
_futuresChains = slice._futuresChains;
// auxiliary data
_splits = slice._splits;
_dividends = slice._dividends;
_delistings = slice._delistings;
_symbolChangedEvents = slice._symbolChangedEvents;
}
///
/// Initializes a new instance of the class
///
/// The timestamp for this slice of data
/// The raw data in this slice
/// The trade bars for this slice
/// The quote bars for this slice
/// This ticks for this slice
/// The option chains for this slice
/// The futures chains for this slice
/// The splits for this slice
/// The dividends for this slice
/// The delistings for this slice
/// The symbol changed events for this slice
/// true if this slice contains data
public Slice(DateTime time, IEnumerable data, TradeBars tradeBars, QuoteBars quoteBars, Ticks ticks, OptionChains optionChains, FuturesChains futuresChains, Splits splits, Dividends dividends, Delistings delistings, SymbolChangedEvents symbolChanges, bool? hasData = null)
{
Time = time;
// market data
_data = new Lazy>(() => CreateDynamicDataDictionary(data));
HasData = hasData ?? _data.Value.Count > 0;
_ticks = ticks;
_bars = tradeBars;
_quoteBars = quoteBars;
_optionChains = optionChains;
_futuresChains = futuresChains;
// auxiliary data
_splits = splits;
_dividends = dividends;
_delistings = delistings;
_symbolChangedEvents = symbolChanges;
}
///
/// Gets the data corresponding to the specified symbol. If the requested data
/// is of , then a will
/// be returned, otherwise, it will be the subscribed type, for example,
/// or event for custom data.
///
/// The data's symbols
/// The data for the specified symbol
public override dynamic this[Symbol symbol]
{
get
{
SymbolData value;
if (_data.Value.TryGetValue(symbol, out value))
{
return value.GetData();
}
throw new KeyNotFoundException($"'{symbol}' wasn't found in the Slice object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey(\"{symbol}\")");
}
}
///
/// Gets the for all data of the specified type
///
/// The type of data we want, for example, or , ect...
/// The containing the data of the specified type
public DataDictionary Get()
where T : IBaseData
{
return GetImpl(typeof(T), this);
}
///
/// Gets the data of the specified type.
///
/// The type of data we seek
/// The instance for the requested type
public dynamic Get(Type type)
{
return GetImpl(type, this);
}
///
/// Gets the data of the specified type.
///
/// Supports both C# and Python use cases
protected static dynamic GetImpl(Type type, Slice instance)
{
if (instance._dataByType == null)
{
// for performance we only really create this collection if someone used it
instance._dataByType = new Dictionary(1);
}
object dictionary;
if (!instance._dataByType.TryGetValue(type, out dictionary))
{
var requestedOpenInterest = type == typeof(OpenInterest);
if (type == typeof(Tick) || requestedOpenInterest)
{
var dataDictionaryCache = GenericDataDictionary.Get(type);
dictionary = Activator.CreateInstance(dataDictionaryCache.GenericType);
foreach (var data in instance.Ticks)
{
var symbol = data.Key;
// preserving existing behavior we will return the last data point, users expect a 'DataDictionary : IDictionary'.
// openInterest is stored with the Ticks collection
var lastDataPoint = data.Value.LastOrDefault(tick => requestedOpenInterest && tick.TickType == TickType.OpenInterest || !requestedOpenInterest && tick.TickType != TickType.OpenInterest);
if (lastDataPoint == null)
{
continue;
}
dataDictionaryCache.MethodInfo.Invoke(dictionary, new object[] { symbol, lastDataPoint });
}
}
else if (type == typeof(TradeBar))
{
dictionary = instance.Bars;
}
else if (type == typeof(QuoteBar))
{
dictionary = instance.QuoteBars;
}
else if (type == typeof(Delisting))
{
dictionary = instance.Delistings;
}
else if (type == typeof(Split))
{
dictionary = instance.Splits;
}
else if (type == typeof(OptionChain))
{
dictionary = instance.OptionChains;
}
else if (type == typeof(FuturesChain))
{
dictionary = instance.FuturesChains;
}
else if (type == typeof(Dividend))
{
dictionary = instance.Dividends;
}
else if (type == typeof(SymbolChangedEvent))
{
dictionary = instance.SymbolChangedEvents;
}
else
{
var dataDictionaryCache = GenericDataDictionary.Get(type);
dictionary = Activator.CreateInstance(dataDictionaryCache.GenericType);
foreach (var data in instance._data.Value.Values.Select(x => x.Custom).Where(o => o != null && o.GetType() == type))
{
dataDictionaryCache.MethodInfo.Invoke(dictionary, new object[] { data.Symbol, data });
}
}
instance._dataByType[type] = dictionary;
}
return dictionary;
}
///
/// Gets the data of the specified symbol and type.
///
/// The type of data we seek
/// The specific symbol was seek
/// The data for the requested symbol
public T Get(Symbol symbol)
where T : BaseData
{
return Get()[symbol];
}
///
/// Determines whether this instance contains data for the specified symbol
///
/// The symbol we seek data for
/// True if this instance contains data for the symbol, false otherwise
public virtual bool ContainsKey(Symbol symbol)
{
return _data.Value.ContainsKey(symbol);
}
///
/// Gets the data associated with the specified symbol
///
/// The symbol we want data for
/// The data for the specifed symbol, or null if no data was found
/// True if data was found, false otherwise
public override bool TryGetValue(Symbol symbol, out dynamic data)
{
data = null;
SymbolData symbolData;
if (_data.Value.TryGetValue(symbol, out symbolData))
{
data = symbolData.GetData();
return data != null;
}
return false;
}
///
/// Produces the dynamic data dictionary from the input data
///
private static DataDictionary CreateDynamicDataDictionary(IEnumerable data)
{
var allData = new DataDictionary();
foreach (var datum in data)
{
// we only will cache the default data type to preserve determinism and backwards compatibility
if (!SubscriptionManager.IsDefaultDataType(datum))
{
continue;
}
SymbolData symbolData;
if (!allData.TryGetValue(datum.Symbol, out symbolData))
{
symbolData = new SymbolData(datum.Symbol);
allData[datum.Symbol] = symbolData;
}
switch (datum.DataType)
{
case MarketDataType.Base:
symbolData.Type = SubscriptionType.Custom;
symbolData.Custom = datum;
break;
case MarketDataType.TradeBar:
symbolData.Type = SubscriptionType.TradeBar;
symbolData.TradeBar = (TradeBar)datum;
break;
case MarketDataType.QuoteBar:
symbolData.Type = SubscriptionType.QuoteBar;
symbolData.QuoteBar = (QuoteBar)datum;
break;
case MarketDataType.Tick:
symbolData.Type = SubscriptionType.Tick;
symbolData.Ticks.Add((Tick)datum);
break;
case MarketDataType.Auxiliary:
symbolData.AuxilliaryData.Add(datum);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return allData;
}
///
/// Dynamically produces a data dictionary using the provided data
///
private static Ticks CreateTicksCollection(IEnumerable data)
{
var ticks = new Ticks();
foreach (var tick in data.OfType())
{
List listTicks;
if (!ticks.TryGetValue(tick.Symbol, out listTicks))
{
ticks[tick.Symbol] = listTicks = new List();
}
listTicks.Add(tick);
}
return ticks;
}
///
/// Dynamically produces a data dictionary for the requested type using the provided data
///
/// The data dictionary type
/// The item type of the data dictionary
/// The current slice time
/// The data to create the collection
/// The data dictionary of containing all the data of that type in this slice
private static T CreateCollection(IEnumerable data)
where T : DataDictionary, new()
where TItem : BaseData
{
var collection = new T();
foreach (var item in data.OfType())
{
collection[item.Symbol] = item;
}
return collection;
}
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
/// 1
public IEnumerator> GetEnumerator()
{
return GetKeyValuePairEnumerable().GetEnumerator();
}
///
/// Returns an enumerator that iterates through a collection.
///
///
/// An object that can be used to iterate through the collection.
///
/// 2
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private IEnumerable> GetKeyValuePairEnumerable()
{
// this will not enumerate auxilliary data!
foreach (var kvp in _data.Value)
{
var data = kvp.Value.GetData();
var dataPoints = data as IEnumerable;
if (dataPoints != null)
{
foreach (var dataPoint in dataPoints)
{
yield return new KeyValuePair(kvp.Key, dataPoint);
}
}
else if (data != null)
{
yield return new KeyValuePair(kvp.Key, data);
}
}
}
private enum SubscriptionType { TradeBar, QuoteBar, Tick, Custom };
private class SymbolData
{
public SubscriptionType Type;
public readonly Symbol Symbol;
// data
public BaseData Custom;
public TradeBar TradeBar;
public QuoteBar QuoteBar;
public readonly List Ticks;
public readonly List AuxilliaryData;
public SymbolData(Symbol symbol)
{
Symbol = symbol;
Ticks = new List();
AuxilliaryData = new List();
}
public dynamic GetData()
{
switch (Type)
{
case SubscriptionType.TradeBar:
return TradeBar;
case SubscriptionType.QuoteBar:
return QuoteBar;
case SubscriptionType.Tick:
return Ticks;
case SubscriptionType.Custom:
return Custom;
default:
throw new ArgumentOutOfRangeException();
}
}
}
///
/// Helper class for generic
///
/// The value of this class is primarily performance since it keeps a cache
/// of the generic types instances and there add methods.
private class GenericDataDictionary
{
private static readonly Dictionary _genericCache = new Dictionary();
///
/// The method
///
public MethodInfo MethodInfo { get; }
///
/// The type
///
public Type GenericType { get; }
private GenericDataDictionary(Type genericType, MethodInfo methodInfo)
{
GenericType = genericType;
MethodInfo = methodInfo;
}
///
/// Provides a instance for a given
///
///
/// A new instance or retrieved from the cache
public static GenericDataDictionary Get(Type type)
{
GenericDataDictionary dataDictionaryCache;
if (!_genericCache.TryGetValue(type, out dataDictionaryCache))
{
var generic = typeof(DataDictionary<>).MakeGenericType(type);
var method = generic.GetMethod("Add", new[] { typeof(Symbol), type });
_genericCache[type] = dataDictionaryCache = new GenericDataDictionary(generic, method);
}
return dataDictionaryCache;
}
}
}
}