/*
* 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.Collections.Specialized;
using System.Linq;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Util;
namespace QuantConnect.Securities
{
///
/// Manages the algorithm's collection of universes
///
public class UniverseManager : IDictionary, INotifyCollectionChanged
{
private readonly Queue _pendingChanges = new();
private readonly ConcurrentDictionary _universes;
///
/// Event fired when a universe is added or removed
///
public event NotifyCollectionChangedEventHandler CollectionChanged;
///
/// Read-only dictionary containing all active securities. An active security is
/// a security that is currently selected by the universe or has holdings or open orders.
///
public IReadOnlyDictionary ActiveSecurities => this
.SelectMany(ukvp => ukvp.Value.Members.Select(mkvp => mkvp.Value))
.DistinctBy(s => s.Symbol).ToDictionary(s => s.Symbol);
///
/// Initializes a new instance of the class
///
public UniverseManager()
{
_universes = new ConcurrentDictionary();
}
#region IDictionary implementation
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
/// 1
public IEnumerator> GetEnumerator()
{
return _universes.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 ((IEnumerable)_universes).GetEnumerator();
}
///
/// Adds an item to the .
///
/// The object to add to the .The is read-only.
public void Add(KeyValuePair item)
{
Add(item.Key, item.Value);
}
///
/// Removes all items from the .
///
/// The is read-only.
public void Clear()
{
_universes.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(KeyValuePair item)
{
return ContainsKey(item.Key);
}
///
/// 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(KeyValuePair[] array, int arrayIndex)
{
((IDictionary)_universes).CopyTo(array, arrayIndex);
}
///
/// 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(KeyValuePair item)
{
return Remove(item.Key);
}
///
/// Gets the number of elements contained in the .
///
///
/// The number of elements contained in the .
///
public int Count => _universes.Skip(0).Count();
///
/// Gets a value indicating whether the is read-only.
///
///
/// true if the is read-only; otherwise, false.
///
public bool IsReadOnly
{
get { return false; }
}
///
/// Determines whether the contains an element with the specified key.
///
///
/// true if the contains an element with the key; otherwise, false.
///
/// The key to locate in the . is null.
public bool ContainsKey(Symbol key)
{
return _universes.ContainsKey(key);
}
///
/// Adds an element with the provided key and value to the .
///
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
/// is null.
/// An element with the same key already exists in the .
/// The is read-only.
public void Add(Symbol key, Universe value)
{
if (_universes.TryAdd(key, value))
{
lock(_pendingChanges)
{
_pendingChanges.Enqueue(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
}
}
}
///
/// Will trigger collection changed event if required
///
public void ProcessChanges()
{
NotifyCollectionChangedEventArgs universeChange;
do
{
lock (_pendingChanges)
{
_pendingChanges.TryDequeue(out universeChange);
}
if (universeChange != null)
{
OnCollectionChanged(universeChange);
}
}
while (universeChange != null);
}
///
/// Removes the element with the specified key from the .
///
///
/// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original .
///
/// The key of the element to remove. is null.The is read-only.
public bool Remove(Symbol key)
{
Universe universe;
if (_universes.TryRemove(key, out universe))
{
universe.Dispose();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, universe));
return true;
}
return false;
}
///
/// Gets the value associated with the specified key.
///
///
/// true if the object that implements contains an element with the specified key; otherwise, false.
///
/// The key whose value to get.When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. is null.
public bool TryGetValue(Symbol key, out Universe value)
{
return _universes.TryGetValue(key, out value);
}
///
/// Gets or sets the element with the specified key.
///
///
/// The element with the specified key.
///
/// The key of the element to get or set. is null.The property is retrieved and is not found.The property is set and the is read-only.
public Universe this[Symbol symbol]
{
get
{
if (!_universes.ContainsKey(symbol))
{
throw new KeyNotFoundException($"This universe symbol ({symbol}) was not found in your universe list. Please add this security or check it exists before using it with 'Universes.ContainsKey(\"{SymbolCache.GetTicker(symbol)}\")'");
}
return _universes[symbol];
}
set
{
Universe existing;
if (_universes.TryGetValue(symbol, out existing) && existing != value)
{
throw new ArgumentException($"Unable to over write existing Universe: {symbol.Value}");
}
// no security exists for the specified symbol key, add it now
if (existing == null)
{
Add(symbol, value);
}
}
}
///
/// Gets an containing the keys of the .
///
///
/// An containing the keys of the object that implements .
///
public ICollection Keys => _universes.Select(x => x.Key).ToList();
///
/// Gets an containing the values in the .
///
///
/// An containing the values in the object that implements .
///
public ICollection Values => _universes.Select(x => x.Value).ToList();
#endregion
///
/// Event invocator for the event
///
///
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
}
}