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