/*
* 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;
}
}
}