/* * 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 Python.Runtime; using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Interfaces; using System.Collections; using QuantConnect.Python; namespace QuantConnect { /// /// Provides a base class for types holding key value pairs with helper methods for easy usage in Python /// [PandasNonExpandable] #pragma warning disable CA1708 // Identifiers should differ by more than case public abstract class ExtendedDictionary : IExtendedDictionary #pragma warning restore CA1708 // Identifiers should differ by more than case { /// /// Gets the number of elements contained in the dictionary /// public abstract int Count { get; } /// /// Removes all items from the . /// /// The is read-only. public virtual void Clear() { if (IsReadOnly) { throw new InvalidOperationException(Messages.ExtendedDictionary.ClearInvalidOperation(this)); } throw new NotImplementedException(Messages.ExtendedDictionary.ClearMethodNotImplemented); } /// /// 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 abstract bool TryGetValue(TKey key, out TValue value); /// /// Checks if the dictionary contains the specified key. /// /// The key to locate in the dictionary /// true if the dictionary contains an element with the specified key; otherwise, false. public virtual bool ContainsKey(TKey key) { return TryGetValue(key, out _); } /// /// Gets all the items in the dictionary /// /// All the items in the dictionary public abstract IEnumerable> GetItems(); /// /// Gets an containing the key objects of the . /// /// /// A containing the key objects of the object that implements . /// protected abstract IEnumerable GetKeys { get; } /// /// Gets an containing the values in the . /// /// /// An containing the values in the object that implements . /// protected abstract IEnumerable GetValues { get; } /// /// Gets a value indicating whether the object is read-only. /// /// IDictionary implementation public virtual bool IsReadOnly => true; /// /// Removes the value with the specified key /// /// The key object of the element to remove. /// true if the element is successfully found and removed; otherwise, false. public virtual bool Remove(TKey key) { if (IsReadOnly) { throw new InvalidOperationException(Messages.ExtendedDictionary.RemoveInvalidOperation(this)); } throw new NotImplementedException(Messages.ExtendedDictionary.RemoveMethodNotImplemented); } /// /// Indexer method for the base dictioanry to access the objects by their symbol. /// /// IDictionary implementation /// Key object indexer /// Object of public abstract TValue this[TKey key] { get; set; } /// /// Removes all keys and values from the . /// public void clear() { Clear(); } /// /// Creates a shallow copy of the . /// /// Returns a shallow copy of the dictionary. It doesn't modify the original dictionary. public PyDict copy() { return fromkeys(GetKeys.ToArray()); } /// /// Creates a new dictionary from the given sequence of elements. /// /// Sequence of elements which is to be used as keys for the new dictionary /// Returns a new dictionary with the given sequence of elements as the keys of the dictionary. public PyDict fromkeys(TKey[] sequence) { return fromkeys(sequence, default); } /// /// Creates a new dictionary from the given sequence of elements with a value provided by the user. /// /// Sequence of elements which is to be used as keys for the new dictionary /// Value which is set to each each element of the dictionary /// Returns a new dictionary with the given sequence of elements as the keys of the dictionary. /// Each element of the newly created dictionary is set to the provided value. public PyDict fromkeys(TKey[] sequence, TValue value) { using (Py.GIL()) { var dict = new PyDict(); foreach (var key in sequence) { var pyValue = get(key, value); dict.SetItem(key.ToPython(), pyValue.ToPython()); } return dict; } } /// /// Returns the value for the specified key if key is in dictionary. /// /// key to be searched in the dictionary /// The value for the specified key if key is in dictionary. /// None if the key is not found and value is not specified. public TValue get(TKey key) { TValue data; TryGetValue(key, out data); return data; } /// /// Returns the value for the specified key if key is in dictionary. /// /// key to be searched in the dictionary /// Value to be returned if the key is not found. The default value is null. /// The value for the specified key if key is in dictionary. /// value if the key is not found and value is specified. public TValue get(TKey key, TValue value) { TValue data; if (TryGetValue(key, out data)) { return data; } return value; } /// /// Returns a view object that displays a list of dictionary's (key, value) tuple pairs. /// /// Returns a view object that displays a list of a given dictionary's (key, value) tuple pair. public PyList items() { using (Py.GIL()) { var pyList = new PyList(); foreach (var (key, value) in GetItems()) { using var pyKey = key.ToPython(); using var pyValue = value.ToPython(); using var pyKvp = new PyTuple([pyKey, pyValue]); pyList.Append(pyKvp); } return pyList; } } /// /// Returns and removes an arbitrary element (key, value) pair from the dictionary. /// /// Returns an arbitrary element (key, value) pair from the dictionary /// removes an arbitrary element(the same element which is returned) from the dictionary. /// Note: Arbitrary elements and random elements are not same.The popitem() doesn't return a random element. public PyTuple popitem() { throw new NotSupportedException(Messages.ExtendedDictionary.PopitemMethodNotSupported(this)); } /// /// Returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary. /// /// Key with null/None value is inserted to the dictionary if key is not in the dictionary. /// The value of the key if it is in the dictionary /// None if key is not in the dictionary public TValue setdefault(TKey key) { return setdefault(key, default); } /// /// Returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary. /// /// Key with a value default_value is inserted to the dictionary if key is not in the dictionary. /// Default value /// The value of the key if it is in the dictionary /// default_value if key is not in the dictionary and default_value is specified public TValue setdefault(TKey key, TValue default_value) { TValue data; if (TryGetValue(key, out data)) { return data; } if (IsReadOnly) { throw new KeyNotFoundException(Messages.ExtendedDictionary.KeyNotFoundDueToNoData(this, key)); } this[key] = default_value; return default_value; } /// /// Removes and returns an element from a dictionary having the given key. /// /// Key which is to be searched for removal /// If key is found - removed/popped element from the dictionary /// If key is not found - KeyError exception is raised public TValue pop(TKey key) { return pop(key, default); } /// /// Removes and returns an element from a dictionary having the given key. /// /// Key which is to be searched for removal /// Value which is to be returned when the key is not in the dictionary /// If key is found - removed/popped element from the dictionary /// If key is not found - value specified as the second argument(default) public TValue pop(TKey key, TValue default_value) { TValue data; if (TryGetValue(key, out data)) { Remove(key); return data; } return default_value; } /// /// Updates the dictionary with the elements from the another dictionary object or from an iterable of key/value pairs. /// The update() method adds element(s) to the dictionary if the key is not in the dictionary.If the key is in the dictionary, it updates the key with the new value. /// /// Takes either a dictionary or an iterable object of key/value pairs (generally tuples). public void update(PyObject other) { if (IsReadOnly) { throw new InvalidOperationException(Messages.ExtendedDictionary.UpdateInvalidOperation(this)); } var dictionary = other.ConvertToDictionary(); foreach (var kvp in dictionary) { this[kvp.Key] = kvp.Value; } } /// /// Returns a view object that displays a list of all the key objects in the dictionary /// /// Returns a view object that displays a list of all the key objects. /// When the dictionary is changed, the view object also reflect these changes. public PyList keys() { return GetKeys.ToPyList(); } /// /// Returns a view object that displays a list of all the values in the dictionary. /// /// Returns a view object that displays a list of all values in a given dictionary. public PyList values() { return GetValues.ToPyList(); } /// /// Checks if the symbol is implicitly created from a string, in which case it is not in the symbol cache, /// and throws a KeyNotFoundException. /// protected void CheckForImplicitlyCreatedSymbol(Symbol symbol) { if (symbol.ID == new SecurityIdentifier(symbol.ID.Symbol, 0)) { throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(symbol.ID.Symbol)); } } } }