Lean  $LEAN_TAG$
ExtendedDictionary.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using Python.Runtime;
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
21 using System.Collections;
22 using QuantConnect.Python;
23 
24 namespace QuantConnect
25 {
26  /// <summary>
27  /// Provides a base class for types holding instances keyed by <see cref="Symbol"/>
28  /// </summary>
29  [PandasNonExpandable]
30  public abstract class ExtendedDictionary<T> : IExtendedDictionary<Symbol, T>
31  {
32  /// <summary>
33  /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
34  /// </summary>
35  /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
36  public virtual void Clear()
37  {
38  if (IsReadOnly)
39  {
40  throw new InvalidOperationException(Messages.ExtendedDictionary.ClearInvalidOperation(this));
41  }
42  throw new NotImplementedException(Messages.ExtendedDictionary.ClearMethodNotImplemented);
43  }
44 
45  /// <summary>
46  /// Gets the value associated with the specified Symbol.
47  /// </summary>
48  /// <returns>
49  /// true if the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified Symbol; otherwise, false.
50  /// </returns>
51  /// <param name="symbol">The Symbol whose value to get.</param><param name="value">When this method returns, the value associated with the specified Symbol, if the Symbol is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param><exception cref="T:System.ArgumentNullException"><paramref name="symbol"/> is null.</exception>
52  public abstract bool TryGetValue(Symbol symbol, out T value);
53 
54  /// <summary>
55  /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the Symbol objects of the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
56  /// </summary>
57  /// <returns>
58  /// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the Symbol objects of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.
59  /// </returns>
60  protected abstract IEnumerable<Symbol> GetKeys { get; }
61 
62  /// <summary>
63  /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
64  /// </summary>
65  /// <returns>
66  /// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.
67  /// </returns>
68  protected abstract IEnumerable<T> GetValues { get; }
69 
70  /// <summary>
71  /// Gets a value indicating whether the <see cref="IDictionary"/> object is read-only.
72  /// </summary>
73  /// <remarks>IDictionary implementation</remarks>
74  public virtual bool IsReadOnly => true;
75 
76  /// <summary>
77  /// Removes the value with the specified Symbol
78  /// </summary>
79  /// <param name="symbol">The Symbol object of the element to remove.</param>
80  /// <returns>true if the element is successfully found and removed; otherwise, false.</returns>
81  public virtual bool Remove(Symbol symbol)
82  {
83  if (IsReadOnly)
84  {
85  throw new InvalidOperationException(Messages.ExtendedDictionary.RemoveInvalidOperation(this));
86  }
87  throw new NotImplementedException(Messages.ExtendedDictionary.RemoveMethodNotImplemented);
88  }
89 
90  /// <summary>
91  /// Indexer method for the base dictioanry to access the objects by their symbol.
92  /// </summary>
93  /// <remarks>IDictionary implementation</remarks>
94  /// <param name="symbol">Symbol object indexer</param>
95  /// <returns>Object of <typeparamref name="T"/></returns>
96  public virtual T this[Symbol symbol]
97  {
98  get
99  {
100  throw new NotImplementedException(Messages.ExtendedDictionary.IndexerBySymbolNotImplemented);
101  }
102  set
103  {
104  throw new NotImplementedException(Messages.ExtendedDictionary.IndexerBySymbolNotImplemented);
105  }
106  }
107 
108  /// <summary>
109  /// Indexer method for the base dictioanry to access the objects by their symbol.
110  /// </summary>
111  /// <remarks>IDictionary implementation</remarks>
112  /// <param name="ticker">string ticker symbol indexer</param>
113  /// <returns>Object of <typeparamref name="T"/></returns>
114  public virtual T this[string ticker]
115  {
116  get
117  {
118  Symbol symbol;
119  if (!SymbolCache.TryGetSymbol(ticker, out symbol))
120  {
121  throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(ticker));
122  }
123  return this[symbol];
124  }
125  set
126  {
127  Symbol symbol;
128  if (!SymbolCache.TryGetSymbol(ticker, out symbol))
129  {
130  throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(ticker));
131  }
132  this[symbol] = value;
133  }
134  }
135 
136  /// <summary>
137  /// Removes all keys and values from the <see cref="IExtendedDictionary{TKey, TValue}"/>.
138  /// </summary>
139  public void clear()
140  {
141  Clear();
142  }
143 
144  /// <summary>
145  /// Creates a shallow copy of the <see cref="IExtendedDictionary{TKey, TValue}"/>.
146  /// </summary>
147  /// <returns>Returns a shallow copy of the dictionary. It doesn't modify the original dictionary.</returns>
148  public PyDict copy()
149  {
150  return fromkeys(GetKeys.ToArray());
151  }
152 
153  /// <summary>
154  /// Creates a new dictionary from the given sequence of elements.
155  /// </summary>
156  /// <param name="sequence">Sequence of elements which is to be used as keys for the new dictionary</param>
157  /// <returns>Returns a new dictionary with the given sequence of elements as the keys of the dictionary.</returns>
158  public PyDict fromkeys(Symbol[] sequence)
159  {
160  return fromkeys(sequence, default(T));
161  }
162 
163  /// <summary>
164  /// Creates a new dictionary from the given sequence of elements with a value provided by the user.
165  /// </summary>
166  /// <param name="sequence">Sequence of elements which is to be used as keys for the new dictionary</param>
167  /// <param name="value">Value which is set to each each element of the dictionary</param>
168  /// <returns>Returns a new dictionary with the given sequence of elements as the keys of the dictionary.
169  /// Each element of the newly created dictionary is set to the provided value.</returns>
170  public PyDict fromkeys(Symbol[] sequence, T value)
171  {
172  using (Py.GIL())
173  {
174  var dict = new PyDict();
175  foreach (var key in sequence)
176  {
177  var pyValue = get(key, value);
178  dict.SetItem(key.ToPython(), pyValue.ToPython());
179  }
180  return dict;
181  }
182  }
183 
184  /// <summary>
185  /// Returns the value for the specified Symbol if Symbol is in dictionary.
186  /// </summary>
187  /// <param name="symbol">Symbol to be searched in the dictionary</param>
188  /// <returns>The value for the specified Symbol if Symbol is in dictionary.
189  /// None if the Symbol is not found and value is not specified.</returns>
190  public T get(Symbol symbol)
191  {
192  T data;
193  TryGetValue(symbol, out data);
194  return data;
195  }
196 
197  /// <summary>
198  /// Returns the value for the specified Symbol if Symbol is in dictionary.
199  /// </summary>
200  /// <param name="symbol">Symbol to be searched in the dictionary</param>
201  /// <param name="value">Value to be returned if the Symbol is not found. The default value is null.</param>
202  /// <returns>The value for the specified Symbol if Symbol is in dictionary.
203  /// value if the Symbol is not found and value is specified.</returns>
204  public T get(Symbol symbol, T value)
205  {
206  T data;
207  if (TryGetValue(symbol, out data))
208  {
209  return data;
210  }
211  return value;
212  }
213 
214  /// <summary>
215  /// Returns a view object that displays a list of dictionary's (Symbol, value) tuple pairs.
216  /// </summary>
217  /// <returns>Returns a view object that displays a list of a given dictionary's (Symbol, value) tuple pair.</returns>
218  public PyList items()
219  {
220  using (Py.GIL())
221  {
222  var pyList = new PyList();
223  foreach (var key in GetKeys)
224  {
225  using (var pyKey = key.ToPython())
226  {
227  using (var pyValue = this[key].ToPython())
228  {
229  using (var pyObject = new PyTuple(new PyObject[] { pyKey, pyValue }))
230  {
231  pyList.Append(pyObject);
232  }
233  }
234  }
235  }
236  return pyList;
237  }
238  }
239 
240  /// <summary>
241  /// Returns and removes an arbitrary element (Symbol, value) pair from the dictionary.
242  /// </summary>
243  /// <returns>Returns an arbitrary element (Symbol, value) pair from the dictionary
244  /// removes an arbitrary element(the same element which is returned) from the dictionary.
245  /// Note: Arbitrary elements and random elements are not same.The popitem() doesn't return a random element.</returns>
246  public PyTuple popitem()
247  {
248  throw new NotSupportedException(Messages.ExtendedDictionary.PopitemMethodNotSupported(this));
249  }
250 
251  /// <summary>
252  /// Returns the value of a Symbol (if the Symbol is in dictionary). If not, it inserts Symbol with a value to the dictionary.
253  /// </summary>
254  /// <param name="symbol">Key with null/None value is inserted to the dictionary if Symbol is not in the dictionary.</param>
255  /// <returns>The value of the Symbol if it is in the dictionary
256  /// None if Symbol is not in the dictionary</returns>
257  public T setdefault(Symbol symbol)
258  {
259  return setdefault(symbol, default(T));
260  }
261 
262  /// <summary>
263  /// Returns the value of a Symbol (if the Symbol is in dictionary). If not, it inserts Symbol with a value to the dictionary.
264  /// </summary>
265  /// <param name="symbol">Key with a value default_value is inserted to the dictionary if Symbol is not in the dictionary.</param>
266  /// <param name="default_value">Default value</param>
267  /// <returns>The value of the Symbol if it is in the dictionary
268  /// default_value if Symbol is not in the dictionary and default_value is specified</returns>
269  public T setdefault(Symbol symbol, T default_value)
270  {
271  T data;
272  if (TryGetValue(symbol, out data))
273  {
274  return data;
275  }
276 
277  if (IsReadOnly)
278  {
279  throw new KeyNotFoundException(Messages.ExtendedDictionary.SymbolNotFoundDueToNoData(this, symbol));
280  }
281 
282  this[symbol] = default_value;
283  return default_value;
284  }
285 
286  /// <summary>
287  /// Removes and returns an element from a dictionary having the given Symbol.
288  /// </summary>
289  /// <param name="symbol">Key which is to be searched for removal</param>
290  /// <returns>If Symbol is found - removed/popped element from the dictionary
291  /// If Symbol is not found - KeyError exception is raised</returns>
292  public T pop(Symbol symbol)
293  {
294  return pop(symbol, default(T));
295  }
296 
297  /// <summary>
298  /// Removes and returns an element from a dictionary having the given Symbol.
299  /// </summary>
300  /// <param name="symbol">Key which is to be searched for removal</param>
301  /// <param name="default_value">Value which is to be returned when the Symbol is not in the dictionary</param>
302  /// <returns>If Symbol is found - removed/popped element from the dictionary
303  /// If Symbol is not found - value specified as the second argument(default)</returns>
304  public T pop(Symbol symbol, T default_value)
305  {
306  T data;
307  if (TryGetValue(symbol, out data))
308  {
309  Remove(symbol);
310  return data;
311  }
312  return default_value;
313  }
314 
315  /// <summary>
316  /// Updates the dictionary with the elements from the another dictionary object or from an iterable of Symbol/value pairs.
317  /// The update() method adds element(s) to the dictionary if the Symbol is not in the dictionary.If the Symbol is in the dictionary, it updates the Symbol with the new value.
318  /// </summary>
319  /// <param name="other">Takes either a dictionary or an iterable object of Symbol/value pairs (generally tuples).</param>
320  public void update(PyObject other)
321  {
322  if (IsReadOnly)
323  {
324  throw new InvalidOperationException(Messages.ExtendedDictionary.UpdateInvalidOperation(this));
325  }
326 
327  var dictionary = other.ConvertToDictionary<Symbol, T>();
328  foreach (var kvp in dictionary)
329  {
330  this[kvp.Key] = kvp.Value;
331  }
332  }
333 
334  /// <summary>
335  /// Returns a view object that displays a list of all the Symbol objects in the dictionary
336  /// </summary>
337  /// <returns>Returns a view object that displays a list of all the Symbol objects.
338  /// When the dictionary is changed, the view object also reflect these changes.</returns>
339  public PyList keys()
340  {
341  return GetKeys.ToPyList();
342  }
343 
344  /// <summary>
345  /// Returns a view object that displays a list of all the values in the dictionary.
346  /// </summary>
347  /// <returns>Returns a view object that displays a list of all values in a given dictionary.</returns>
348  public PyList values()
349  {
350  return GetValues.ToPyList();
351  }
352  }
353 }