/* * 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.Concurrent; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Algorithm.Framework.Alphas { /// /// Provides a collection for managing insights. This type provides collection access semantics /// as well as dictionary access semantics through TryGetValue, ContainsKey, and this[symbol] /// public class InsightCollection : ICollection { // for performance lets keep the next insight expiration time private DateTime? _nextExpiryTime; private readonly ConcurrentDictionary> _insights = new ConcurrentDictionary>(); /// Gets the number of elements contained in the . /// The number of elements contained in the . public int Count => _insights.Aggregate(0, (i, kvp) => i + kvp.Value.Count); /// Gets a value indicating whether the is read-only. /// true if the is read-only; otherwise, false. public bool IsReadOnly => false; /// Adds an item to the . /// The object to add to the . /// The is read-only. public void Add(Insight item) { _nextExpiryTime = null; _insights.AddOrUpdate(item.Symbol, s => new List {item}, (s, list) => { list.Add(item); return list; }); } /// /// Adds each item in the specified enumerable of insights to this collection /// /// The insights to add to this collection public void AddRange(IEnumerable insights) { foreach (var insight in insights) { Add(insight); } } /// Removes all items from the . /// The is read-only. public void Clear() { _nextExpiryTime = null; _insights.Clear(); } /// Determines whether the contains a specific value. /// true if is found in the ; otherwise, false. /// The object to locate in the . public bool Contains(Insight item) { List symbolInsights; if (_insights.TryGetValue(item.Symbol, out symbolInsights)) { return symbolInsights.Contains(item); } return false; } /// /// Determines whether insights exist in this collection for the specified symbol /// /// The symbol key /// True if there are insights for the symbol in this collection public bool ContainsKey(Symbol symbol) { List insights; return _insights.TryGetValue(symbol, out insights) && insights.Count > 0; } /// Copies the elements of the to an , starting at a particular index. /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. /// The zero-based index in at which copying begins. /// /// is null. /// /// is less than 0. /// The number of elements in the source is greater than the available space from to the end of the destination . public void CopyTo(Insight[] array, int arrayIndex) { // Avoid calling `ToList` on insights to avoid potential infinite loop (issue #3168) Array.Copy(_insights.SelectMany(kvp => kvp.Value).ToArray(), 0, array, arrayIndex, Count); } /// Removes the first occurrence of a specific object from the . /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// The object to remove from the . /// The is read-only. public bool Remove(Insight item) { List symbolInsights; if (_insights.TryGetValue(item.Symbol, out symbolInsights)) { _nextExpiryTime = null; if (symbolInsights.Remove(item)) { // remove empty list from dictionary if (symbolInsights.Count == 0) { _insights.TryRemove(item.Symbol, out symbolInsights); } return true; } } return false; } /// /// Dictionary accessor returns a list of insights for the specified symbol /// /// The symbol key /// List of insights for the symbol public List this[Symbol symbol] { get { return _insights[symbol]; } set { _insights[symbol] = value; } } /// /// Attempts to get the list of insights with the specified symbol key /// /// The symbol key /// The insights for the specified symbol, or null if not found /// True if insights for the specified symbol were found, false otherwise public bool TryGetValue(Symbol symbol, out List insights) { return _insights.TryGetValue(symbol, out insights); } /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. /// 1 public IEnumerator GetEnumerator() { return _insights.SelectMany(kvp => kvp.Value).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(); } /// /// Removes the symbol and its insights /// /// List of symbols that will be removed public void Clear(Symbol[] symbols) { _nextExpiryTime = null; foreach (var symbol in symbols) { List insights; _insights.TryRemove(symbol, out insights); } } /// /// Gets the next expiry time UTC /// public DateTime? GetNextExpiryTime() { if (Count == 0) { return null; } if (_nextExpiryTime != null) { return _nextExpiryTime; } _nextExpiryTime = _insights.Min(x => x.Value.Min(i => i.CloseTimeUtc)); return _nextExpiryTime; } /// /// Gets the last generated active insight /// /// Collection of insights that are active public ICollection GetActiveInsights(DateTime utcTime) { var activeInsights = new List(); foreach (var kvp in _insights) { foreach (var insight in kvp.Value) { if (insight.IsActive(utcTime)) { activeInsights.Add(insight); } } } return activeInsights; } /// /// Returns true if there are active insights for a given symbol and time /// /// The symbol key /// Time that determines whether the insight has expired /// public bool HasActiveInsights(Symbol symbol, DateTime utcTime) { List insights; if (TryGetValue(symbol, out insights)) { return insights.Any(i => i.IsActive(utcTime)); } return false; } /// /// Remove all expired insights from the collection and retuns them /// /// Time that determines whether the insight has expired /// Expired insights that were removed public ICollection RemoveExpiredInsights(DateTime utcTime) { var removedInsights = new List(); foreach (var kvp in _insights) { foreach (var insight in kvp.Value) { if (insight.IsExpired(utcTime)) { removedInsights.Add(insight); } } } foreach (var insight in removedInsights) { Remove(insight); } return removedInsights; } } }