Lean  $LEAN_TAG$
BaseData.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 NodaTime;
18 using ProtoBuf;
19 using System.IO;
20 using System.Linq;
21 using Newtonsoft.Json;
22 using QuantConnect.Util;
24 using System.Collections.Generic;
25 
26 namespace QuantConnect.Data
27 {
28  /// <summary>
29  /// Abstract base data class of QuantConnect. It is intended to be extended to define
30  /// generic user customizable data types while at the same time implementing the basics of data where possible
31  /// </summary>
32  [ProtoContract(SkipConstructor = true)]
33  [ProtoInclude(8, typeof(Tick))]
34  [ProtoInclude(100, typeof(TradeBar))]
35  [ProtoInclude(200, typeof(QuoteBar))]
36  [ProtoInclude(300, typeof(Dividend))]
37  [ProtoInclude(400, typeof(Split))]
38  public abstract class BaseData : IBaseData
39  {
40  private decimal _value;
41 
42  /// <summary>
43  /// A list of all <see cref="Resolution"/>
44  /// </summary>
45  protected static readonly List<Resolution> AllResolutions =
46  Enum.GetValues(typeof(Resolution)).Cast<Resolution>().ToList();
47 
48  /// <summary>
49  /// A list of <see cref="Resolution.Daily"/>
50  /// </summary>
51  protected static readonly List<Resolution> DailyResolution = new List<Resolution> { Resolution.Daily };
52 
53  /// <summary>
54  /// A list of <see cref="Resolution.Minute"/>
55  /// </summary>
56  protected static readonly List<Resolution> MinuteResolution = new List<Resolution> { Resolution.Minute };
57 
58  /// <summary>
59  /// A list of high <see cref="Resolution"/>, including minute, second, and tick.
60  /// </summary>
61  protected static readonly List<Resolution> HighResolution = new List<Resolution> { Resolution.Minute, Resolution.Second, Resolution.Tick };
62 
63  /// <summary>
64  /// A list of resolutions support by Options
65  /// </summary>
66  protected static readonly List<Resolution> OptionResolutions = new List<Resolution> { Resolution.Daily, Resolution.Hour, Resolution.Minute };
67 
68  /// <summary>
69  /// Market Data Type of this data - does it come in individual price packets or is it grouped into OHLC.
70  /// </summary>
71  /// <remarks>Data is classed into two categories - streams of instantaneous prices and groups of OHLC data.</remarks>
72  [ProtoMember(1)]
73  public MarketDataType DataType { get; set; } = MarketDataType.Base;
74 
75  /// <summary>
76  /// True if this is a fill forward piece of data
77  /// </summary>
78  public bool IsFillForward { get; private set; }
79 
80  /// <summary>
81  /// Current time marker of this data packet.
82  /// </summary>
83  /// <remarks>All data is timeseries based.</remarks>
84  [ProtoMember(2)]
85  public DateTime Time { get; set; }
86 
87  /// <summary>
88  /// The end time of this data. Some data covers spans (trade bars) and as such we want
89  /// to know the entire time span covered
90  /// </summary>
91  public virtual DateTime EndTime
92  {
93  get { return Time; }
94  set { Time = value; }
95  }
96 
97  /// <summary>
98  /// Symbol representation for underlying Security
99  /// </summary>
100  public Symbol Symbol { get; set; } = Symbol.Empty;
101 
102  /// <summary>
103  /// Value representation of this data packet. All data requires a representative value for this moment in time.
104  /// For streams of data this is the price now, for OHLC packets this is the closing price.
105  /// </summary>
106  [ProtoMember(4)]
107  public virtual decimal Value
108  {
109  get
110  {
111  return _value;
112  }
113  set
114  {
115  _value = value;
116  }
117  }
118 
119  /// <summary>
120  /// As this is a backtesting platform we'll provide an alias of value as price.
121  /// </summary>
122  public virtual decimal Price => Value;
123 
124  /// <summary>
125  /// Constructor for initialising the dase data class
126  /// </summary>
127  public BaseData()
128  {
129  //Empty constructor required for fast-reflection initialization
130  }
131 
132  /// <summary>
133  /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
134  /// each time it is called. The returned object is assumed to be time stamped in the config.ExchangeTimeZone.
135  /// </summary>
136  /// <param name="config">Subscription data config setup object</param>
137  /// <param name="line">Line of the source document</param>
138  /// <param name="date">Date of the requested data</param>
139  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
140  /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
141  public virtual BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
142  {
143  // stub implementation to prevent compile errors in user algorithms
144  var dataFeed = isLiveMode ? DataFeedEndpoint.LiveTrading : DataFeedEndpoint.Backtesting;
145 #pragma warning disable 618 // This implementation is left here for backwards compatibility of the BaseData API
146  return Reader(config, line, date, dataFeed);
147 #pragma warning restore 618
148  }
149 
150  /// <summary>
151  /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
152  /// each time it is called. The returned object is assumed to be time stamped in the config.ExchangeTimeZone.
153  /// </summary>
154  /// <param name="config">Subscription data config setup object</param>
155  /// <param name="stream">The data stream</param>
156  /// <param name="date">Date of the requested data</param>
157  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
158  /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
159  public virtual BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
160  {
161  throw new NotImplementedException("Each data types has to implement is own Stream reader");
162  }
163 
164  /// <summary>
165  /// Return the URL string source of the file. This will be converted to a stream
166  /// </summary>
167  /// <param name="config">Configuration object</param>
168  /// <param name="date">Date of this source file</param>
169  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
170  /// <returns>String URL of source file.</returns>
171  public virtual SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
172  {
173  // stub implementation to prevent compile errors in user algorithms
174  var dataFeed = isLiveMode ? DataFeedEndpoint.LiveTrading : DataFeedEndpoint.Backtesting;
175 #pragma warning disable 618 // This implementation is left here for backwards compatibility of the BaseData API
176  var source = GetSource(config, date, dataFeed);
177 #pragma warning restore 618
178 
179  if (isLiveMode)
180  {
181  // live trading by default always gets a rest endpoint
182  return new SubscriptionDataSource(source, SubscriptionTransportMedium.Rest);
183  }
184 
185  // construct a uri to determine if we have a local or remote file
186  var uri = new Uri(source, UriKind.RelativeOrAbsolute);
187 
188  if (uri.IsAbsoluteUri && !uri.IsLoopback)
189  {
190  return new SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile);
191  }
192 
193  return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile);
194  }
195 
196  /// <summary>
197  /// Indicates if there is support for mapping
198  /// </summary>
199  /// <remarks>Relies on the <see cref="Symbol"/> property value</remarks>
200  /// <returns>True indicates mapping should be used</returns>
201  public virtual bool RequiresMapping()
202  {
203  return Symbol.RequiresMapping();
204  }
205 
206  /// <summary>
207  /// Indicates that the data set is expected to be sparse
208  /// </summary>
209  /// <remarks>Relies on the <see cref="Symbol"/> property value</remarks>
210  /// <remarks>This is a method and not a property so that python
211  /// custom data types can override it</remarks>
212  /// <returns>True if the data set represented by this type is expected to be sparse</returns>
213  public virtual bool IsSparseData()
214  {
215  // by default, we'll assume all custom data is sparse data
216  return Symbol.SecurityType == SecurityType.Base;
217  }
218 
219  /// <summary>
220  /// Indicates whether this contains data that should be stored in the security cache
221  /// </summary>
222  /// <returns>Whether this contains data that should be stored in the security cache</returns>
223  public virtual bool ShouldCacheToSecurity()
224  {
225  return true;
226  }
227 
228  /// <summary>
229  /// Gets the default resolution for this data and security type
230  /// </summary>
231  /// <remarks>This is a method and not a property so that python
232  /// custom data types can override it</remarks>
233  public virtual Resolution DefaultResolution()
234  {
235  return Resolution.Minute;
236  }
237 
238  /// <summary>
239  /// Gets the supported resolution for this data and security type
240  /// </summary>
241  /// <remarks>Relies on the <see cref="Symbol"/> property value</remarks>
242  /// <remarks>This is a method and not a property so that python
243  /// custom data types can override it</remarks>
244  public virtual List<Resolution> SupportedResolutions()
245  {
246  if (Symbol.SecurityType.IsOption() || Symbol.SecurityType == SecurityType.Index)
247  {
248  return OptionResolutions;
249  }
250 
251  return AllResolutions;
252  }
253 
254  /// <summary>
255  /// Specifies the data time zone for this data type. This is useful for custom data types
256  /// </summary>
257  /// <remarks>Will throw <see cref="InvalidOperationException"/> for security types
258  /// other than <see cref="SecurityType.Base"/></remarks>
259  /// <returns>The <see cref="DateTimeZone"/> of this data type</returns>
260  public virtual DateTimeZone DataTimeZone()
261  {
262  if (Symbol.SecurityType != SecurityType.Base)
263  {
264  throw new InvalidOperationException("BaseData.DataTimeZone(): is only valid for base data types");
265  }
266  return TimeZones.NewYork;
267  }
268 
269  /// <summary>
270  /// Updates this base data with a new trade
271  /// </summary>
272  /// <param name="lastTrade">The price of the last trade</param>
273  /// <param name="tradeSize">The quantity traded</param>
274  public void UpdateTrade(decimal lastTrade, decimal tradeSize)
275  {
276  Update(lastTrade, 0, 0, tradeSize, 0, 0);
277  }
278 
279  /// <summary>
280  /// Updates this base data with new quote information
281  /// </summary>
282  /// <param name="bidPrice">The current bid price</param>
283  /// <param name="bidSize">The current bid size</param>
284  /// <param name="askPrice">The current ask price</param>
285  /// <param name="askSize">The current ask size</param>
286  public void UpdateQuote(decimal bidPrice, decimal bidSize, decimal askPrice, decimal askSize)
287  {
288  Update(0, bidPrice, askPrice, 0, bidSize, askSize);
289  }
290 
291  /// <summary>
292  /// Updates this base data with the new quote bid information
293  /// </summary>
294  /// <param name="bidPrice">The current bid price</param>
295  /// <param name="bidSize">The current bid size</param>
296  public void UpdateBid(decimal bidPrice, decimal bidSize)
297  {
298  Update(0, bidPrice, 0, 0, bidSize, 0);
299  }
300 
301  /// <summary>
302  /// Updates this base data with the new quote ask information
303  /// </summary>
304  /// <param name="askPrice">The current ask price</param>
305  /// <param name="askSize">The current ask size</param>
306  public void UpdateAsk(decimal askPrice, decimal askSize)
307  {
308  Update(0, 0, askPrice, 0, 0, askSize);
309  }
310 
311  /// <summary>
312  /// Update routine to build a bar/tick from a data update.
313  /// </summary>
314  /// <param name="lastTrade">The last trade price</param>
315  /// <param name="bidPrice">Current bid price</param>
316  /// <param name="askPrice">Current asking price</param>
317  /// <param name="volume">Volume of this trade</param>
318  /// <param name="bidSize">The size of the current bid, if available</param>
319  /// <param name="askSize">The size of the current ask, if available</param>
320  public virtual void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
321  {
322  Value = lastTrade;
323  }
324 
325  /// <summary>
326  /// Return a new instance clone of this object, used in fill forward
327  /// </summary>
328  /// <remarks>
329  /// This base implementation uses reflection to copy all public fields and properties
330  /// </remarks>
331  /// <param name="fillForward">True if this is a fill forward clone</param>
332  /// <returns>A clone of the current object</returns>
333  public virtual BaseData Clone(bool fillForward)
334  {
335  var clone = Clone();
336  clone.IsFillForward = fillForward;
337  return clone;
338  }
339 
340  /// <summary>
341  /// Return a new instance clone of this object, used in fill forward
342  /// </summary>
343  /// <remarks>
344  /// This base implementation uses reflection to copy all public fields and properties
345  /// </remarks>
346  /// <returns>A clone of the current object</returns>
347  public virtual BaseData Clone()
348  {
349  return (BaseData) ObjectActivator.Clone((object)this);
350  }
351 
352  /// <summary>
353  /// Formats a string with the symbol and value.
354  /// </summary>
355  /// <returns>string - a string formatted as SPY: 167.753</returns>
356  public override string ToString()
357  {
358  return $"{Symbol}: {Value.ToStringInvariant("C")}";
359  }
360 
361  /// <summary>
362  /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
363  /// each time it is called.
364  /// </summary>
365  /// <remarks>OBSOLETE:: This implementation is added for backward/forward compatibility purposes. This function is no longer called by the LEAN engine.</remarks>
366  /// <param name="config">Subscription data config setup object</param>
367  /// <param name="line">Line of the source document</param>
368  /// <param name="date">Date of the requested data</param>
369  /// <param name="dataFeed">Type of datafeed we're requesting - a live or backtest feed.</param>
370  /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
371  [Obsolete("Reader(SubscriptionDataConfig, string, DateTime, DataFeedEndpoint) method has been made obsolete, use Reader(SubscriptionDataConfig, string, DateTime, bool) instead.")]
372  public virtual BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, DataFeedEndpoint dataFeed)
373  {
374  throw new InvalidOperationException(
375  $"Please implement Reader(SubscriptionDataConfig, string, DateTime, bool) on your custom data type: {GetType().Name}"
376  );
377  }
378 
379  /// <summary>
380  /// Return the URL string source of the file. This will be converted to a stream
381  /// </summary>
382  /// <remarks>OBSOLETE:: This implementation is added for backward/forward compatibility purposes. This function is no longer called by the LEAN engine.</remarks>
383  /// <param name="config">Configuration object</param>
384  /// <param name="date">Date of this source file</param>
385  /// <param name="datafeed">Type of datafeed we're reqesting - backtest or live</param>
386  /// <returns>String URL of source file.</returns>
387  [Obsolete("GetSource(SubscriptionDataConfig, DateTime, DataFeedEndpoint) method has been made obsolete, use GetSource(SubscriptionDataConfig, DateTime, bool) instead.")]
388  public virtual string GetSource(SubscriptionDataConfig config, DateTime date, DataFeedEndpoint datafeed)
389  {
390  throw new InvalidOperationException(
391  $"Please implement GetSource(SubscriptionDataConfig, DateTime, bool) on your custom data type: {GetType().Name}"
392  );
393  }
394 
395  /// <summary>
396  /// Deserialize the message from the data server
397  /// </summary>
398  /// <param name="serialized">The data server's message</param>
399  /// <returns>An enumerable of base data, if unsuccessful, returns an empty enumerable</returns>
400  public static IEnumerable<BaseData> DeserializeMessage(string serialized)
401  {
402  var deserialized = JsonConvert.DeserializeObject(serialized, JsonSerializerSettings);
403 
404  var enumerable = deserialized as IEnumerable<BaseData>;
405  if (enumerable != null)
406  {
407  return enumerable;
408  }
409 
410  var data = deserialized as BaseData;
411  if (data != null)
412  {
413  return new[] { data };
414  }
415 
416  return Enumerable.Empty<BaseData>();
417  }
418 
419  private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
420  {
421  TypeNameHandling = TypeNameHandling.All
422  };
423  }
424 }