Files
quantconnect--lean/Common/Data/SliceExtensions.cs
T
Martin-Molinero 933e5ce6ca
Build & Test Lean / build (push) Has been cancelled
Regression Tests / build (push) Has been cancelled
Alpha Streams improvements (#5876)
* Alpha Streams improvements

- Warmup added securities so we can trigger an order right away
- Fix bug where AddSecurity returned a security which was not the one
  being used
- Fix bug where EWAS PCM would remove from a dictionary while iterating
  over it

* Fix unit tests

* Add QCAlgorithm.GetLastKnownPrices API

- Add new GetLastKnownPrices that will return the last known data point
  for all subscribed data types.
- Fix for MHDB GetDataTimeZone which was using an invalid entry key
  format for custom data.
- Fix for using GetLastKnownPrice/s to seed a security during creation,
  when it's not added in the Algorithm.Securities collection

* Adding more unit tests for GetLastKnownPrice

* Address reviews

- Refactor GetLastKnownPrice/s to perform a single history request for
  all data types
2021-08-27 13:50:45 -03:00

228 lines
10 KiB
C#

/*
* 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.Market;
using QuantConnect.Util;
namespace QuantConnect.Data
{
/// <summary>
/// Provides extension methods to slice enumerables
/// </summary>
public static class SliceExtensions
{
/// <summary>
/// Selects into the slice and returns the TradeBars that have data in order
/// </summary>
/// <param name="slices">The enumerable of slice</param>
/// <returns>An enumerable of TradeBars</returns>
public static IEnumerable<TradeBars> TradeBars(this IEnumerable<Slice> slices)
{
return slices.Where(x => x.Bars.Count > 0).Select(x => x.Bars);
}
/// <summary>
/// Selects into the slice and returns the Ticks that have data in order
/// </summary>
/// <param name="slices">The enumerable of slice</param>
/// <returns>An enumerable of Ticks</returns>
public static IEnumerable<Ticks> Ticks(this IEnumerable<Slice> slices)
{
return slices.Where(x => x.Ticks.Count > 0).Select(x => x.Ticks);
}
/// <summary>
/// Gets an enumerable of TradeBar for the given symbol. This method does not verify
/// that the specified symbol points to a TradeBar
/// </summary>
/// <param name="slices">The enumerable of slice</param>
/// <param name="symbol">The symbol to retrieve</param>
/// <returns>An enumerable of TradeBar for the matching symbol, of no TradeBar found for symbol, empty enumerable is returned</returns>
public static IEnumerable<TradeBar> Get(this IEnumerable<Slice> slices, Symbol symbol)
{
return slices.TradeBars().Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]);
}
/// <summary>
/// Gets an enumerable of T for the given symbol. This method does not vify
/// that the specified symbol points to a T
/// </summary>
/// <typeparam name="T">The data type</typeparam>
/// <param name="dataDictionaries">The data dictionary enumerable to access</param>
/// <param name="symbol">The symbol to retrieve</param>
/// <returns>An enumerable of T for the matching symbol, if no T is found for symbol, empty enumerable is returned</returns>
public static IEnumerable<T> Get<T>(this IEnumerable<DataDictionary<T>> dataDictionaries, Symbol symbol)
where T : IBaseData
{
return dataDictionaries.Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]);
}
/// <summary>
/// Gets an enumerable of decimals by accessing the specified field on data for the symbol
/// </summary>
/// <typeparam name="T">The data type</typeparam>
/// <param name="dataDictionaries">An enumerable of data dictionaries</param>
/// <param name="symbol">The symbol to retrieve</param>
/// <param name="field">The field to access</param>
/// <returns>An enumerable of decimals</returns>
public static IEnumerable<decimal> Get<T>(this IEnumerable<DataDictionary<T>> dataDictionaries, Symbol symbol, string field)
{
Func<T, decimal> 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<Tick>))
{
// 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<Tick, decimal>) ExpressionBuilder.MakePropertyOrFieldSelector(typeof (Tick), field).Compile();
selector = ticks => dataSelector(((List<Tick>) (object) ticks).Last());
}
else
{
selector = (Func<T, decimal>) ExpressionBuilder.MakePropertyOrFieldSelector(typeof (T), field).Compile();
}
foreach (var dataDictionary in dataDictionaries)
{
T item;
if (dataDictionary.TryGetValue(symbol, out item))
{
yield return selector(item);
}
}
}
/// <summary>
/// Gets the data dictionaries of the requested type in each slice
/// </summary>
/// <typeparam name="T">The data type</typeparam>
/// <param name="slices">The enumerable of slice</param>
/// <returns>An enumerable of data dictionary of the requested type</returns>
public static IEnumerable<DataDictionary<T>> Get<T>(this IEnumerable<Slice> slices)
where T : IBaseData
{
return slices.Select(x => x.Get<T>()).Where(x => x.Count > 0);
}
/// <summary>
/// Gets an enumerable of T by accessing the slices for the requested symbol
/// </summary>
/// <typeparam name="T">The data type</typeparam>
/// <param name="slices">The enumerable of slice</param>
/// <param name="symbol">The symbol to retrieve</param>
/// <returns>An enumerable of T by accessing each slice for the requested symbol</returns>
public static IEnumerable<T> Get<T>(this IEnumerable<Slice> slices, Symbol symbol)
where T : IBaseData
{
return slices.Select(x => x.Get<T>()).Where(x => x.ContainsKey(symbol)).Select(x => x[symbol]);
}
/// <summary>
/// Gets an enumerable of decimal by accessing the slice for the symbol and then retrieving the specified
/// field on each piece of data
/// </summary>
/// <param name="slices">The enumerable of slice</param>
/// <param name="symbol">The symbol to retrieve</param>
/// <param name="field">The field selector used to access the dats</param>
/// <returns>An enumerable of decimal</returns>
public static IEnumerable<decimal> Get(this IEnumerable<Slice> slices, Symbol symbol, Func<BaseData, decimal> field)
{
foreach (var slice in slices)
{
dynamic item;
if (slice.TryGetValue(symbol, out item))
{
if (item is List<Tick>) yield return field(item.Last());
else yield return field(item);
}
}
}
/// <summary>
/// Converts the specified enumerable of decimals into a double array
/// </summary>
/// <param name="decimals">The enumerable of decimal</param>
/// <returns>Double array representing the enumerable of decimal</returns>
public static double[] ToDoubleArray(this IEnumerable<decimal> decimals)
{
return decimals.Select(x => (double) x).ToArray();
}
/// <summary>
/// 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.
/// </summary>
/// <param name="slices">The data to send into the consolidators, likely result of a history request</param>
/// <param name="consolidatorsBySymbol">Dictionary of consolidators keyed by symbol</param>
public static void PushThroughConsolidators(this IEnumerable<Slice> slices, Dictionary<Symbol, IDataConsolidator> consolidatorsBySymbol)
{
PushThroughConsolidators(slices, symbol =>
{
IDataConsolidator consolidator;
consolidatorsBySymbol.TryGetValue(symbol, out consolidator);
return consolidator;
});
}
/// <summary>
/// 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.
/// </summary>
/// <param name="slices">The data to send into the consolidators, likely result of a history request</param>
/// <param name="consolidatorsProvider">Delegate that fetches the consolidators by a symbol</param>
public static void PushThroughConsolidators(this IEnumerable<Slice> slices, Func<Symbol, IDataConsolidator> consolidatorsProvider)
{
slices.PushThrough(data => consolidatorsProvider(data?.Symbol)?.Update(data));
}
/// <summary>
/// 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.
/// </summary>
/// <param name="slices">The data to send into the consolidators, likely result of a history request</param>
/// <param name="handler">Delegate handles each data piece from the slice</param>
public static void PushThrough(this IEnumerable<Slice> slices, Action<BaseData> handler)
{
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);
}
}
}
}
}