Lean  $LEAN_TAG$
PythonData.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 QuantConnect.Data;
18 using System;
19 using System.Collections.Generic;
20 
21 namespace QuantConnect.Python
22 {
23  /// <summary>
24  /// Dynamic data class for Python algorithms.
25  /// Stores properties of python instances in DynamicData dictionary
26  /// </summary>
27  public class PythonData : DynamicData
28  {
29  private readonly string _pythonTypeName;
30  private readonly dynamic _pythonReader;
31  private readonly dynamic _pythonGetSource;
32  private readonly dynamic _pythonData;
33  private readonly dynamic _defaultResolution;
34  private readonly dynamic _supportedResolutions;
35  private readonly dynamic _isSparseData;
36  private readonly dynamic _requiresMapping;
37  private DateTime _endTime;
38 
39  /// <summary>
40  /// The end time of this data. Some data covers spans (trade bars)
41  /// and as such we want to know the entire time span covered
42  /// </summary>
43  /// <remarks>
44  /// This property is overriden to allow different values for Time and EndTime
45  /// if they are set in the Reader. In the base implementation EndTime equals Time
46  /// </remarks>
47  public override DateTime EndTime
48  {
49  get
50  {
51  return _endTime == default ? Time : _endTime;
52  }
53  set
54  {
55  _endTime = value;
56  if(Time == default)
57  {
58  // if Time hasn't been set let's set it, like BaseData does. If the user overrides it that's okay
59  Time = value;
60  }
61  }
62  }
63 
64  /// <summary>
65  /// Constructor for initializing the PythonData class
66  /// </summary>
67  public PythonData()
68  {
69  //Empty constructor required for fast-reflection initialization
70  }
71 
72  /// <summary>
73  /// Constructor for initializing the PythonData class with wrapped PyObject
74  /// </summary>
75  /// <param name="pythonData"></param>
76  public PythonData(PyObject pythonData)
77  {
78  _pythonData = pythonData;
79  using (Py.GIL())
80  {
81  // these methods rely on the Symbol so we can call them yet but we can get them
82  _requiresMapping = pythonData.GetMethod("RequiresMapping");
83  _isSparseData = pythonData.GetMethod("IsSparseData");
84  _defaultResolution = pythonData.GetMethod("DefaultResolution");
85  _supportedResolutions = pythonData.GetMethod("SupportedResolutions");
86  _pythonReader = pythonData.GetMethod("Reader");
87  _pythonGetSource = pythonData.GetMethod("GetSource");
88  _pythonTypeName = pythonData.GetPythonType().GetAssemblyName().Name;
89  }
90  }
91 
92  /// <summary>
93  /// Source Locator for algorithm written in Python.
94  /// </summary>
95  /// <param name="config">Subscription configuration object</param>
96  /// <param name="date">Date of the data file we're looking for</param>
97  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
98  /// <returns>STRING API Url.</returns>
99  public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
100  {
101  using (Py.GIL())
102  {
103  var source = _pythonGetSource(config, date, isLiveMode);
104  return (source as PyObject).GetAndDispose<SubscriptionDataSource>();
105  }
106  }
107 
108  /// <summary>
109  /// Generic Reader Implementation for Python Custom Data.
110  /// </summary>
111  /// <param name="config">Subscription configuration</param>
112  /// <param name="line">CSV line of data from the source</param>
113  /// <param name="date">Date of the requested line</param>
114  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
115  /// <returns></returns>
116  public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
117  {
118  using (Py.GIL())
119  {
120  var data = _pythonReader(config, line, date, isLiveMode);
121  var result = (data as PyObject).GetAndDispose<BaseData>();
122 
123  (result as PythonData)?.SetProperty("__typename", _pythonTypeName);
124 
125  return result;
126  }
127  }
128 
129  /// <summary>
130  /// Indicates if there is support for mapping
131  /// </summary>
132  /// <returns>True indicates mapping should be used</returns>
133  public override bool RequiresMapping()
134  {
135  if (_requiresMapping == null)
136  {
137  return base.RequiresMapping();
138  }
139  using (Py.GIL())
140  {
141  return _requiresMapping();
142  }
143  }
144 
145  /// <summary>
146  /// Indicates that the data set is expected to be sparse
147  /// </summary>
148  /// <remarks>Relies on the <see cref="Symbol"/> property value</remarks>
149  /// <returns>True if the data set represented by this type is expected to be sparse</returns>
150  public override bool IsSparseData()
151  {
152  if (_isSparseData == null)
153  {
154  return base.IsSparseData();
155  }
156  using (Py.GIL())
157  {
158  return _isSparseData();
159  }
160  }
161 
162  /// <summary>
163  /// Gets the default resolution for this data and security type
164  /// </summary>
165  /// <remarks>This is a method and not a property so that python
166  /// custom data types can override it</remarks>
167  public override Resolution DefaultResolution()
168  {
169  if (_defaultResolution == null)
170  {
171  return base.DefaultResolution();
172  }
173  using (Py.GIL())
174  {
175  return _defaultResolution();
176  }
177  }
178 
179  /// <summary>
180  /// Gets the supported resolution for this data and security type
181  /// </summary>
182  /// <remarks>This is a method and not a property so that python
183  /// custom data types can override it</remarks>
184  public override List<Resolution> SupportedResolutions()
185  {
186  if (_supportedResolutions == null)
187  {
188  return base.SupportedResolutions();
189  }
190  using (Py.GIL())
191  {
192  return _supportedResolutions();
193  }
194  }
195 
196  /// <summary>
197  /// Indexes into this PythonData, where index is key to the dynamic property
198  /// </summary>
199  /// <param name="index">the index</param>
200  /// <returns>Dynamic property of a given index</returns>
201  public object this[string index]
202  {
203  get
204  {
205  return GetProperty(index);
206  }
207 
208  set
209  {
210  SetProperty(index, value is double ? value.ConvertInvariant<decimal>() : value);
211  }
212  }
213 
214  /// <summary>
215  /// Helper method to determine if the current instance is of the provided type
216  /// </summary>
217  /// <param name="type">Target type to check against</param>
218  /// <returns>True if this instance is of the provided type</returns>
219  public bool IsOfType(Type type)
220  {
221  if (HasProperty("__typename"))
222  {
223  return (string)GetProperty("__typename") == type.FullName;
224  }
225  return GetType() == type;
226  }
227  }
228 }