Lean  $LEAN_TAG$
DynamicData.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.Generic;
18 using System.Dynamic;
19 using System.Linq.Expressions;
20 using System.Reflection;
21 using Python.Runtime;
22 using QuantConnect.Util;
23 
24 namespace QuantConnect.Data
25 {
26  /// <summary>
27  /// Dynamic Data Class: Accept flexible data, adapting to the columns provided by source.
28  /// </summary>
29  /// <remarks>Intended for use with Quandl class.</remarks>
30  public abstract class DynamicData : BaseData, IDynamicMetaObjectProvider
31  {
32  private static readonly MethodInfo SetPropertyMethodInfo = typeof(DynamicData).GetMethod("SetProperty");
33  private static readonly MethodInfo GetPropertyMethodInfo = typeof(DynamicData).GetMethod("GetProperty");
34 
35  private readonly IDictionary<string, object> _storage = new Dictionary<string, object>();
36 
37  /// <summary>
38  /// Get the metaObject required for Dynamism.
39  /// </summary>
40  public DynamicMetaObject GetMetaObject(Expression parameter)
41  {
42  return new GetSetPropertyDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo);
43  }
44 
45  /// <summary>
46  /// Sets the property with the specified name to the value. This is a case-insensitve search.
47  /// </summary>
48  /// <param name="name">The property name to set</param>
49  /// <param name="value">The new property value</param>
50  /// <returns>Returns the input value back to the caller</returns>
51  public object SetProperty(string name, object value)
52  {
53  name = name.LazyToLower();
54 
55  if (name == "time")
56  {
57  if (value is PyObject pyobject)
58  {
59  Time = pyobject.As<DateTime>();
60  }
61  else
62  {
63  Time = (DateTime)value;
64  }
65  }
66  else if (name == "endtime" || name == "end_time")
67  {
68  if (value is PyObject pyobject)
69  {
70  EndTime = pyobject.As<DateTime>();
71  }
72  else
73  {
74  EndTime = (DateTime)value;
75  }
76  }
77  else if (name == "value")
78  {
79  if (value is PyObject pyobject)
80  {
81  Value = pyobject.As<decimal>();
82  }
83  else
84  {
85  Value = (decimal)value;
86  }
87  }
88  else if (name == "symbol")
89  {
90  if (value is string)
91  {
92  Symbol = SymbolCache.GetSymbol((string) value);
93  }
94  else
95  {
96  if (value is PyObject pyobject)
97  {
98  Symbol = pyobject.As<Symbol>();
99  }
100  else
101  {
102  Symbol = (Symbol)value;
103  }
104  }
105  }
106 
107  _storage[name] = value;
108  return value;
109  }
110 
111  /// <summary>
112  /// Gets the property's value with the specified name. This is a case-insensitve search.
113  /// </summary>
114  /// <param name="name">The property name to access</param>
115  /// <returns>object value of BaseData</returns>
116  public object GetProperty(string name)
117  {
118  name = name.ToLowerInvariant();
119 
120  // redirect these calls to the base types properties
121  if (name == "time")
122  {
123  return Time;
124  }
125  if (name == "endtime")
126  {
127  return EndTime;
128  }
129  if (name == "value")
130  {
131  return Value;
132  }
133  if (name == "symbol")
134  {
135  return Symbol;
136  }
137  if (name == "price")
138  {
139  return Price;
140  }
141 
142  object value;
143  if (!_storage.TryGetValue(name, out value))
144  {
145  // let the user know the property name that we couldn't find
146  throw new KeyNotFoundException(
147  $"Property with name \'{name}\' does not exist. Properties: Time, Symbol, Value {string.Join(", ", _storage.Keys)}"
148  );
149  }
150 
151  return value;
152  }
153 
154  /// <summary>
155  /// Gets whether or not this dynamic data instance has a property with the specified name.
156  /// This is a case-insensitve search.
157  /// </summary>
158  /// <param name="name">The property name to check for</param>
159  /// <returns>True if the property exists, false otherwise</returns>
160  public bool HasProperty(string name)
161  {
162  return _storage.ContainsKey(name.ToLowerInvariant());
163  }
164 
165  /// <summary>
166  /// Gets the storage dictionary
167  /// Python algorithms need this information since DynamicMetaObject does not work
168  /// </summary>
169  /// <returns>Dictionary that stores the paramenters names and values</returns>
170  public IDictionary<string, object> GetStorageDictionary()
171  {
172  return _storage;
173  }
174 
175  /// <summary>
176  /// Return a new instance clone of this object, used in fill forward
177  /// </summary>
178  /// <remarks>
179  /// This base implementation uses reflection to copy all public fields and properties
180  /// </remarks>
181  /// <returns>A clone of the current object</returns>
182  public override BaseData Clone()
183  {
184  var clone = ObjectActivator.Clone(this);
185  foreach (var kvp in _storage)
186  {
187  // don't forget to add the dynamic members!
188  clone._storage.Add(kvp);
189  }
190  return clone;
191  }
192  }
193 }