Lean  $LEAN_TAG$
DynamicSecurityData.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 System;
17 using System.Collections;
18 using System.Collections.Concurrent;
19 using System.Collections.Generic;
20 using System.Dynamic;
21 using System.Linq;
22 using System.Linq.Expressions;
23 using System.Reflection;
24 using Python.Runtime;
25 using QuantConnect.Data;
26 
28 {
29  /// <summary>
30  /// Provides access to a security's data via it's type. This implementation supports dynamic access
31  /// by type name.
32  /// </summary>
33  public class DynamicSecurityData : IDynamicMetaObjectProvider
34  {
35  private static readonly MethodInfo SetPropertyMethodInfo = typeof(DynamicSecurityData).GetMethod("SetProperty");
36  private static readonly MethodInfo GetPropertyMethodInfo = typeof(DynamicSecurityData).GetMethod("GetProperty");
37 
38  private readonly IRegisteredSecurityDataTypesProvider _registeredTypes;
39  private readonly ConcurrentDictionary<Type, Type> _genericTypes = new ConcurrentDictionary<Type, Type>();
40  private readonly SecurityCache _cache;
41 
42  /// <summary>
43  /// Initializes a new instance of the <see cref="DynamicSecurityData"/> class
44  /// </summary>
45  /// <param name="registeredTypes">Provides all the registered data types for the algorithm</param>
46  /// <param name="cache">The security cache</param>
48  {
49  _registeredTypes = registeredTypes;
50  _cache = cache;
51  }
52 
53  /// <summary>Returns the <see cref="T:System.Dynamic.DynamicMetaObject" /> responsible for binding operations performed on this object.</summary>
54  /// <returns>The <see cref="T:System.Dynamic.DynamicMetaObject" /> to bind this object.</returns>
55  /// <param name="parameter">The expression tree representation of the runtime value.</param>
56  public DynamicMetaObject GetMetaObject(Expression parameter)
57  {
58  return new GetSetPropertyDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo);
59  }
60 
61  /// <summary>
62  /// Gets whether or not this dynamic data instance has data stored for the specified type
63  /// </summary>
64  public bool HasData<T>()
65  {
66  return _cache.HasData(typeof(T));
67  }
68 
69  /// <summary>
70  /// Gets whether or not this dynamic data instance has a property with the specified name.
71  /// This is a case-insensitive search.
72  /// </summary>
73  /// <param name="name">The property name to check for</param>
74  /// <returns>True if the property exists, false otherwise</returns>
75  public bool HasProperty(string name)
76  {
77  Type type;
78  if (_registeredTypes.TryGetType(name, out type))
79  {
80  return _cache.HasData(type);
81  }
82  return false;
83  }
84 
85  /// <summary>
86  /// Gets the last item in the data list for the specified type
87  /// </summary>
88  public T Get<T>()
89  {
90  var list = GetAll<T>();
91  return list.LastOrDefault();
92  }
93 
94  /// <summary>
95  /// Gets the data list for the specified type
96  /// </summary>
97  public IReadOnlyList<T> GetAll<T>()
98  {
99  return GetAllImpl(typeof(T));
100  }
101 
102  /// <summary>
103  /// Get the matching cached object in a python friendly accessor
104  /// </summary>
105  /// <param name="type">Type to search for</param>
106  /// <returns>Matching object</returns>
107  public PyObject Get(Type type)
108  {
109  var list = GetAll(type);
110 
111  if (list.Count == 0)
112  {
113  return null;
114  }
115 
116  using (Py.GIL())
117  {
118  return list[list.Count - 1].ToPython();
119  }
120  }
121 
122  /// <summary>
123  /// Get all the matching types with a python friendly overload.
124  /// </summary>
125  /// <param name="type">Search type</param>
126  /// <returns>List of matching objects cached</returns>
127  public IList GetAll(Type type)
128  {
129  return GetAllImpl(type);
130  }
131 
132  /// <summary>
133  /// Sets the property with the specified name to the value. This is a case-insensitve search.
134  /// </summary>
135  /// <param name="name">The property name to set</param>
136  /// <param name="value">The new property value</param>
137  /// <returns>Returns the input value back to the caller</returns>
138  [Obsolete("DynamicSecurityData is a view of the SecurityCache. It is readonly, properties can not be set")]
139  public object SetProperty(string name, object value)
140  {
141  throw new InvalidOperationException(Messages.DynamicSecurityData.PropertiesCannotBeSet);
142  }
143 
144  /// <summary>
145  /// Gets the property's value with the specified name. This is a case-insensitve search.
146  /// </summary>
147  /// <param name="name">The property name to access</param>
148  /// <returns>object value of BaseData</returns>
149  public object GetProperty(string name)
150  {
151  // check to see if the requested name matches one of the algorithm registered data types and if
152  // so, we'll return a new empty list. this precludes us from always needing to check HasData<T>
153  Type type;
154  if (_registeredTypes.TryGetType(name, out type))
155  {
156  IReadOnlyList<BaseData> data;
157  if (_cache.TryGetValue(type, out data))
158  {
159  return data;
160  }
161 
162  var listType = GetGenericListType(type);
163  return Activator.CreateInstance(listType);
164  }
165 
166  throw new KeyNotFoundException(Messages.DynamicSecurityData.PropertyNotFound(name));
167  }
168 
169  /// <summary>
170  /// Get all implementation that covers both Python and C#
171  /// </summary>
172  private dynamic GetAllImpl(Type type)
173  {
174  var data = GetProperty(type.Name);
175 
176  var dataType = data.GetType();
177  if (dataType.GetElementType() == type // covers arrays
178  // covers lists
179  || dataType.GenericTypeArguments.Length == 1
180  && dataType.GenericTypeArguments[0] == type)
181  {
182  return data;
183  }
184 
185  var baseDataList = data as IReadOnlyList<BaseData>;
186  if (baseDataList != null)
187  {
188  var listType = GetGenericListType(type);
189  var list = (IList)Activator.CreateInstance(listType);
190  foreach (var baseData in baseDataList)
191  {
192  list.Add(baseData);
193  }
194  return list;
195  }
196 
197  throw new InvalidOperationException(Messages.DynamicSecurityData.UnexpectedTypesForGetAll(type, data));
198  }
199 
200  private Type GetGenericListType(Type type)
201  {
202  Type containerType;
203  if (!_genericTypes.TryGetValue(type, out containerType))
204  {
205  // for performance we keep the generic type
206  _genericTypes[type] = containerType = typeof(List<>).MakeGenericType(type);
207  }
208 
209  return containerType;
210  }
211  }
212 }