Lean  $LEAN_TAG$
TradeBar.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 ProtoBuf;
18 using System.IO;
19 using System.Threading;
20 using QuantConnect.Util;
21 using System.Globalization;
22 using QuantConnect.Logging;
23 using static QuantConnect.StringExtensions;
24 
26 {
27  /// <summary>
28  /// TradeBar class for second and minute resolution data:
29  /// An OHLC implementation of the QuantConnect BaseData class with parameters for candles.
30  /// </summary>
31  [ProtoContract(SkipConstructor = true)]
32  public class TradeBar : BaseData, IBaseDataBar
33  {
34  // scale factor used in QC equity/forex data files
35  private const decimal _scaleFactor = 1 / 10000m;
36 
37  private int _initialized;
38  private decimal _open;
39  private decimal _high;
40  private decimal _low;
41 
42  /// <summary>
43  /// Volume:
44  /// </summary>
45  [ProtoMember(101)]
46  public virtual decimal Volume { get; set; }
47 
48  /// <summary>
49  /// Opening price of the bar: Defined as the price at the start of the time period.
50  /// </summary>
51  [ProtoMember(102)]
52  public virtual decimal Open
53  {
54  get { return _open; }
55  set
56  {
57  Initialize(value);
58  _open = value;
59  }
60  }
61 
62  /// <summary>
63  /// High price of the TradeBar during the time period.
64  /// </summary>
65  [ProtoMember(103)]
66  public virtual decimal High
67  {
68  get { return _high; }
69  set
70  {
71  Initialize(value);
72  _high = value;
73  }
74  }
75 
76  /// <summary>
77  /// Low price of the TradeBar during the time period.
78  /// </summary>
79  [ProtoMember(104)]
80  public virtual decimal Low
81  {
82  get { return _low; }
83  set
84  {
85  Initialize(value);
86  _low = value;
87  }
88  }
89 
90  /// <summary>
91  /// Closing price of the TradeBar. Defined as the price at Start Time + TimeSpan.
92  /// </summary>
93  [ProtoMember(105)]
94  public virtual decimal Close
95  {
96  get { return Value; }
97  set
98  {
99  Initialize(value);
100  Value = value;
101  }
102  }
103 
104  /// <summary>
105  /// The closing time of this bar, computed via the Time and Period
106  /// </summary>
107  public override DateTime EndTime
108  {
109  get { return Time + Period; }
110  set { Period = value - Time; }
111  }
112 
113  /// <summary>
114  /// The period of this trade bar, (second, minute, daily, ect...)
115  /// </summary>
116  [ProtoMember(106)]
117  public virtual TimeSpan Period { get; set; }
118 
119  //In Base Class: Alias of Closing:
120  //public decimal Price;
121 
122  //Symbol of Asset.
123  //In Base Class: public Symbol Symbol;
124 
125  //In Base Class: DateTime Of this TradeBar
126  //public DateTime Time;
127 
128  /// <summary>
129  /// Default initializer to setup an empty tradebar.
130  /// </summary>
131  public TradeBar()
132  {
133  Symbol = Symbol.Empty;
134  DataType = MarketDataType.TradeBar;
136  }
137 
138  /// <summary>
139  /// Cloner constructor for implementing fill forward.
140  /// Return a new instance with the same values as this original.
141  /// </summary>
142  /// <param name="original">Original tradebar object we seek to clone</param>
143  public TradeBar(TradeBar original)
144  {
145  DataType = MarketDataType.TradeBar;
146  Time = new DateTime(original.Time.Ticks);
147  Symbol = original.Symbol;
148  Value = original.Close;
149  Open = original.Open;
150  High = original.High;
151  Low = original.Low;
152  Close = original.Close;
153  Volume = original.Volume;
154  Period = original.Period;
155  _initialized = 1;
156  }
157 
158  /// <summary>
159  /// Initialize Trade Bar with OHLC Values:
160  /// </summary>
161  /// <param name="time">DateTime Timestamp of the bar</param>
162  /// <param name="symbol">Market MarketType Symbol</param>
163  /// <param name="open">Decimal Opening Price</param>
164  /// <param name="high">Decimal High Price of this bar</param>
165  /// <param name="low">Decimal Low Price of this bar</param>
166  /// <param name="close">Decimal Close price of this bar</param>
167  /// <param name="volume">Volume sum over day</param>
168  /// <param name="period">The period of this bar, specify null for default of 1 minute</param>
169  public TradeBar(DateTime time, Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume, TimeSpan? period = null)
170  {
171  Time = time;
172  Symbol = symbol;
173  Value = close;
174  Open = open;
175  High = high;
176  Low = low;
177  Close = close;
178  Volume = volume;
179  Period = period ?? QuantConnect.Time.OneMinute;
180  DataType = MarketDataType.TradeBar;
181  _initialized = 1;
182  }
183 
184  /// <summary>
185  /// TradeBar Reader: Fetch the data from the QC storage and feed it line by line into the engine.
186  /// </summary>
187  /// <param name="config">Symbols, Resolution, DataType, </param>
188  /// <param name="line">Line from the data file requested</param>
189  /// <param name="date">Date of this reader request</param>
190  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
191  /// <returns>Enumerable iterator for returning each line of the required data.</returns>
192  public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
193  {
194  //Handle end of file:
195  if (line == null)
196  {
197  return null;
198  }
199 
200  if (isLiveMode)
201  {
202  return new TradeBar();
203  }
204 
205  try
206  {
207  switch (config.SecurityType)
208  {
209  //Equity File Data Format:
210  case SecurityType.Equity:
211  return ParseEquity(config, line, date);
212 
213  //FOREX has a different data file format:
214  case SecurityType.Forex:
215  return ParseForex(config, line, date);
216 
217  case SecurityType.Crypto:
218  case SecurityType.CryptoFuture:
219  return ParseCrypto(config, line, date);
220 
221  case SecurityType.Cfd:
222  return ParseCfd(config, line, date);
223 
224  case SecurityType.Index:
225  return ParseIndex(config, line, date);
226 
227  case SecurityType.Option:
228  case SecurityType.FutureOption:
229  case SecurityType.IndexOption:
230  return ParseOption(config, line, date);
231 
232  case SecurityType.Future:
233  return ParseFuture(config, line, date);
234 
235  }
236  }
237  catch (Exception err)
238  {
239  Log.Error(Invariant($"TradeBar.Reader(): Error parsing line: '{line}', Symbol: {config.Symbol.Value}, SecurityType: ") +
240  Invariant($"{config.SecurityType}, Resolution: {config.Resolution}, Date: {date:yyyy-MM-dd}, Message: {err}")
241  );
242  }
243 
244  // if we couldn't parse it above return a default instance
245  return new TradeBar { Symbol = config.Symbol, Period = config.Increment };
246  }
247 
248  /// <summary>
249  /// TradeBar Reader: Fetch the data from the QC storage and feed it directly from the stream into the engine.
250  /// </summary>
251  /// <param name="config">Symbols, Resolution, DataType, </param>
252  /// <param name="stream">The file data stream</param>
253  /// <param name="date">Date of this reader request</param>
254  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
255  /// <returns>Enumerable iterator for returning each line of the required data.</returns>
256  public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
257  {
258  //Handle end of file:
259  if (stream == null || stream.EndOfStream)
260  {
261  return null;
262  }
263  if (isLiveMode)
264  {
265  return new TradeBar();
266  }
267 
268  try
269  {
270  switch (config.SecurityType)
271  {
272  //Equity File Data Format:
273  case SecurityType.Equity:
274  return ParseEquity(config, stream, date);
275 
276  //FOREX has a different data file format:
277  case SecurityType.Forex:
278  return ParseForex(config, stream, date);
279 
280  case SecurityType.Crypto:
281  case SecurityType.CryptoFuture:
282  return ParseCrypto(config, stream, date);
283 
284  case SecurityType.Index:
285  return ParseIndex(config, stream, date);
286 
287  case SecurityType.Cfd:
288  return ParseCfd(config, stream, date);
289 
290  case SecurityType.Option:
291  case SecurityType.FutureOption:
292  case SecurityType.IndexOption:
293  return ParseOption(config, stream, date);
294 
295  case SecurityType.Future:
296  return ParseFuture(config, stream, date);
297 
298  }
299  }
300  catch (Exception err)
301  {
302  Log.Error(Invariant($"TradeBar.Reader(): Error parsing stream, Symbol: {config.Symbol.Value}, SecurityType: ") +
303  Invariant($"{config.SecurityType}, Resolution: {config.Resolution}, Date: {date:yyyy-MM-dd}, Message: {err}")
304  );
305  }
306 
307  // we need to consume a line anyway, to advance the stream
308  stream.ReadLine();
309 
310  // if we couldn't parse it above return a default instance
311  return new TradeBar { Symbol = config.Symbol, Period = config.Increment };
312  }
313 
314  /// <summary>
315  /// Parses the trade bar data line assuming QC data formats
316  /// </summary>
317  public static TradeBar Parse(SubscriptionDataConfig config, string line, DateTime baseDate)
318  {
319  switch (config.SecurityType)
320  {
321  case SecurityType.Equity:
322  return ParseEquity(config, line, baseDate);
323 
324  case SecurityType.Forex:
325  case SecurityType.Crypto:
326  case SecurityType.CryptoFuture:
327  return ParseForex(config, line, baseDate);
328 
329  case SecurityType.Cfd:
330  return ParseCfd(config, line, baseDate);
331  }
332 
333  return null;
334  }
335 
336  /// <summary>
337  /// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
338  /// </summary>
339  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
340  /// <param name="config">Symbols, Resolution, DataType, </param>
341  /// <param name="line">Line from the data file requested</param>
342  /// <param name="date">Date of this reader request</param>
343  /// <returns></returns>
344  public static T ParseEquity<T>(SubscriptionDataConfig config, string line, DateTime date)
345  where T : TradeBar, new()
346  {
347  var tradeBar = new T
348  {
349  Symbol = config.Symbol,
350  Period = config.Increment
351  };
352 
353  ParseEquity(tradeBar, config, line, date);
354 
355  return tradeBar;
356  }
357 
358  /// <summary>
359  /// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
360  /// </summary>
361  /// <param name="config">Symbols, Resolution, DataType, </param>
362  /// <param name="streamReader">The data stream of the requested file</param>
363  /// <param name="date">Date of this reader request</param>
364  /// <returns></returns>
365  public static TradeBar ParseEquity(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
366  {
367  var tradeBar = new TradeBar
368  {
369  Symbol = config.Symbol,
370  Period = config.Increment
371  };
372  ParseEquity(tradeBar, config, streamReader, date);
373  return tradeBar;
374  }
375 
376  private static void ParseEquity(TradeBar tradeBar, SubscriptionDataConfig config, StreamReader stream, DateTime date)
377  {
378  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
379  {
380  // hourly and daily have different time format, and can use slow, robust c# parser.
381  tradeBar.Time = stream.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
382  }
383  else
384  {
385  // Using custom "ToDecimal" conversion for speed on high resolution data.
386  tradeBar.Time = date.Date.AddMilliseconds(stream.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
387  }
388 
389  tradeBar.Open = stream.GetDecimal() * _scaleFactor;
390  tradeBar.High = stream.GetDecimal() * _scaleFactor;
391  tradeBar.Low = stream.GetDecimal() * _scaleFactor;
392  tradeBar.Close = stream.GetDecimal() * _scaleFactor;
393  tradeBar.Volume = stream.GetDecimal();
394  }
395 
396  private static void ParseEquity(TradeBar tradeBar, SubscriptionDataConfig config, string line, DateTime date)
397  {
398  var csv = line.ToCsv(6);
399  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
400  {
401  // hourly and daily have different time format, and can use slow, robust c# parser.
402  tradeBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
403  }
404  else
405  {
406  // Using custom "ToDecimal" conversion for speed on high resolution data.
407  tradeBar.Time = date.Date.AddMilliseconds(csv[0].ToInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
408  }
409 
410  tradeBar.Open = csv[1].ToDecimal() * _scaleFactor;
411  tradeBar.High = csv[2].ToDecimal() * _scaleFactor;
412  tradeBar.Low = csv[3].ToDecimal() * _scaleFactor;
413  tradeBar.Close = csv[4].ToDecimal() * _scaleFactor;
414  tradeBar.Volume = csv[5].ToDecimal();
415  }
416 
417  /// <summary>
418  /// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
419  /// </summary>
420  /// <param name="config">Symbols, Resolution, DataType, </param>
421  /// <param name="line">Line from the data file requested</param>
422  /// <param name="date">Date of this reader request</param>
423  /// <returns></returns>
424  public static TradeBar ParseEquity(SubscriptionDataConfig config, string line, DateTime date)
425  {
426  var tradeBar = new TradeBar
427  {
428  Symbol = config.Symbol,
429  Period = config.Increment
430  };
431  ParseEquity(tradeBar, config, line, date);
432  return tradeBar;
433  }
434 
435  /// <summary>
436  /// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
437  /// </summary>
438  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
439  /// <param name="config">Symbols, Resolution, DataType, </param>
440  /// <param name="line">Line from the data file requested</param>
441  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
442  /// <returns></returns>
443  public static T ParseForex<T>(SubscriptionDataConfig config, string line, DateTime date)
444  where T : TradeBar, new()
445  {
446  var tradeBar = new T
447  {
448  Symbol = config.Symbol,
449  Period = config.Increment
450  };
451  LineParseNoScale(config, line, date, tradeBar, hasVolume: false);
452 
453  return tradeBar;
454  }
455 
456  /// <summary>
457  /// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
458  /// </summary>
459  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
460  /// <param name="config">Symbols, Resolution, DataType, </param>
461  /// <param name="line">Line from the data file requested</param>
462  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
463  public static T ParseCrypto<T>(SubscriptionDataConfig config, string line, DateTime date)
464  where T : TradeBar, new()
465  {
466  var tradeBar = new T
467  {
468  Symbol = config.Symbol,
469  Period = config.Increment
470  };
471  LineParseNoScale(config, line, date, tradeBar);
472 
473  return tradeBar;
474  }
475 
476  /// <summary>
477  /// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
478  /// </summary>
479  /// <param name="config">Symbols, Resolution, DataType, </param>
480  /// <param name="line">Line from the data file requested</param>
481  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
482  public static TradeBar ParseCrypto(SubscriptionDataConfig config, string line, DateTime date)
483  {
484  return LineParseNoScale(config, line, date);
485  }
486 
487  /// <summary>
488  /// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
489  /// </summary>
490  /// <param name="config">Symbols, Resolution, DataType, </param>
491  /// <param name="streamReader">The data stream of the requested file</param>
492  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
493  public static TradeBar ParseCrypto(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
494  {
495  return StreamParseNoScale(config, streamReader, date);
496  }
497 
498  /// <summary>
499  /// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
500  /// </summary>
501  /// <param name="config">Symbols, Resolution, DataType, </param>
502  /// <param name="line">Line from the data file requested</param>
503  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
504  /// <returns></returns>
505  public static TradeBar ParseForex(SubscriptionDataConfig config, string line, DateTime date)
506  {
507  return LineParseNoScale(config, line, date, hasVolume: false);
508  }
509 
510  /// <summary>
511  /// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
512  /// </summary>
513  /// <param name="config">Symbols, Resolution, DataType, </param>
514  /// <param name="streamReader">The data stream of the requested file</param>
515  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
516  /// <returns></returns>
517  public static TradeBar ParseForex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
518  {
519  return StreamParseNoScale(config, streamReader, date, hasVolume: false);
520  }
521 
522  /// <summary>
523  /// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
524  /// </summary>
525  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
526  /// <param name="config">Symbols, Resolution, DataType, </param>
527  /// <param name="line">Line from the data file requested</param>
528  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
529  /// <returns></returns>
530  public static T ParseCfd<T>(SubscriptionDataConfig config, string line, DateTime date)
531  where T : TradeBar, new()
532  {
533  // CFD has the same data format as Forex
534  return ParseForex<T>(config, line, date);
535  }
536 
537  /// <summary>
538  /// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
539  /// </summary>
540  /// <param name="config">Symbols, Resolution, DataType, </param>
541  /// <param name="line">Line from the data file requested</param>
542  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
543  /// <returns></returns>
544  public static TradeBar ParseCfd(SubscriptionDataConfig config, string line, DateTime date)
545  {
546  // CFD has the same data format as Forex
547  return ParseForex(config, line, date);
548  }
549 
550  /// <summary>
551  /// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
552  /// </summary>
553  /// <param name="config">Symbols, Resolution, DataType, </param>
554  /// <param name="streamReader">The data stream of the requested file</param>
555  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
556  /// <returns></returns>
557  public static TradeBar ParseCfd(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
558  {
559  // CFD has the same data format as Forex
560  return ParseForex(config, streamReader, date);
561  }
562 
563  /// <summary>
564  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
565  /// </summary>
566  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
567  /// <param name="config">Symbols, Resolution, DataType, </param>
568  /// <param name="line">Line from the data file requested</param>
569  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
570  /// <returns></returns>
571  public static T ParseOption<T>(SubscriptionDataConfig config, string line, DateTime date)
572  where T : TradeBar, new()
573  {
574  var tradeBar = new T
575  {
576  Period = config.Increment,
577  Symbol = config.Symbol
578  };
579 
580  var csv = line.ToCsv(6);
581  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
582  {
583  // hourly and daily have different time format, and can use slow, robust c# parser.
584  tradeBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
585  }
586  else
587  {
588  // Using custom "ToDecimal" conversion for speed on high resolution data.
589  tradeBar.Time = date.Date.AddMilliseconds(csv[0].ToInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
590  }
591 
592  var scalingFactor = GetScaleFactor(config.Symbol);
593  tradeBar.Open = csv[1].ToDecimal() * scalingFactor;
594  tradeBar.High = csv[2].ToDecimal() * scalingFactor;
595  tradeBar.Low = csv[3].ToDecimal() * scalingFactor;
596  tradeBar.Close = csv[4].ToDecimal() * scalingFactor;
597  tradeBar.Volume = csv[5].ToDecimal();
598 
599  return tradeBar;
600  }
601 
602  /// <summary>
603  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
604  /// </summary>
605  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
606  /// <param name="config">Symbols, Resolution, DataType, </param>
607  /// <param name="streamReader">The data stream of the requested file</param>
608  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
609  /// <returns></returns>
610  public static T ParseOption<T>(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
611  where T : TradeBar, new()
612  {
613  var tradeBar = new T
614  {
615  Period = config.Increment,
616  Symbol = config.Symbol
617  };
618 
619  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
620  {
621  // hourly and daily have different time format, and can use slow, robust c# parser.
622  tradeBar.Time = streamReader.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
623  }
624  else
625  {
626  // Using custom "ToDecimal" conversion for speed on high resolution data.
627  tradeBar.Time = date.Date.AddMilliseconds(streamReader.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
628  }
629 
630  var scalingFactor = GetScaleFactor(config.Symbol);
631  tradeBar.Open = streamReader.GetDecimal() * scalingFactor;
632  tradeBar.High = streamReader.GetDecimal() * scalingFactor;
633  tradeBar.Low = streamReader.GetDecimal() * scalingFactor;
634  tradeBar.Close = streamReader.GetDecimal() * scalingFactor;
635  tradeBar.Volume = streamReader.GetDecimal();
636 
637  return tradeBar;
638  }
639 
640  /// <summary>
641  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
642  /// </summary>
643  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
644  /// <param name="config">Symbols, Resolution, DataType, </param>
645  /// <param name="streamReader">The data stream of the requested file</param>
646  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
647  /// <returns></returns>
648  public static T ParseFuture<T>(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
649  where T : TradeBar, new()
650  {
651  var tradeBar = new T
652  {
653  Period = config.Increment,
654  Symbol = config.Symbol
655  };
656  StreamParseNoScale(config, streamReader, date, tradeBar);
657 
658  return tradeBar;
659  }
660 
661  /// <summary>
662  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
663  /// </summary>
664  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
665  /// <param name="config">Symbols, Resolution, DataType, </param>
666  /// <param name="line">Line from the data file requested</param>
667  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
668  /// <returns></returns>
669  public static T ParseFuture<T>(SubscriptionDataConfig config, string line, DateTime date)
670  where T : TradeBar, new()
671  {
672  var tradeBar = new T
673  {
674  Period = config.Increment,
675  Symbol = config.Symbol
676  };
677  LineParseNoScale(config, line, date, tradeBar);
678 
679  return tradeBar;
680  }
681 
682  /// <summary>
683  /// Parse an index bar from the LEAN disk format
684  /// </summary>
685  public static TradeBar ParseIndex(SubscriptionDataConfig config, string line, DateTime date)
686  {
687  return LineParseNoScale(config, line, date);
688  }
689 
690  /// <summary>
691  /// Parse an index bar from the LEAN disk format
692  /// </summary>
693  private static TradeBar LineParseNoScale(SubscriptionDataConfig config, string line, DateTime date, TradeBar bar = null, bool hasVolume = true)
694  {
695  var tradeBar = bar ?? new TradeBar
696  {
697  Period = config.Increment,
698  Symbol = config.Symbol
699  };
700 
701  var csv = line.ToCsv(hasVolume ? 6 : 5);
702  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
703  {
704  // hourly and daily have different time format, and can use slow, robust c# parser.
705  tradeBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
706  }
707  else
708  {
709  // Using custom "ToDecimal" conversion for speed on high resolution data.
710  tradeBar.Time = date.Date.AddMilliseconds(csv[0].ToInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
711  }
712  tradeBar.Open = csv[1].ToDecimal();
713  tradeBar.High = csv[2].ToDecimal();
714  tradeBar.Low = csv[3].ToDecimal();
715  tradeBar.Close = csv[4].ToDecimal();
716  if (hasVolume)
717  {
718  tradeBar.Volume = csv[5].ToDecimal();
719  }
720 
721  return tradeBar;
722  }
723 
724  /// <summary>
725  /// Parse an index bar from the LEAN disk format
726  /// </summary>
727  private static TradeBar StreamParseNoScale(SubscriptionDataConfig config, StreamReader streamReader, DateTime date, TradeBar bar = null, bool hasVolume = true)
728  {
729  var tradeBar = bar ?? new TradeBar
730  {
731  Period = config.Increment,
732  Symbol = config.Symbol
733  };
734 
735  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
736  {
737  // hourly and daily have different time format, and can use slow, robust c# parser.
738  tradeBar.Time = streamReader.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
739  }
740  else
741  {
742  // Using custom "ToDecimal" conversion for speed on high resolution data.
743  tradeBar.Time = date.Date.AddMilliseconds(streamReader.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
744  }
745  tradeBar.Open = streamReader.GetDecimal();
746  tradeBar.High = streamReader.GetDecimal();
747  tradeBar.Low = streamReader.GetDecimal();
748  tradeBar.Close = streamReader.GetDecimal();
749  if (hasVolume)
750  {
751  tradeBar.Volume = streamReader.GetDecimal();
752  }
753 
754  return tradeBar;
755  }
756 
757  /// <summary>
758  /// Parse an index bar from the LEAN disk format
759  /// </summary>
760  public static TradeBar ParseIndex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
761  {
762  return StreamParseNoScale(config, streamReader, date);
763  }
764 
765  /// <summary>
766  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
767  /// </summary>
768  /// <param name="config">Symbols, Resolution, DataType, </param>
769  /// <param name="line">Line from the data file requested</param>
770  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
771  /// <returns></returns>
772  public static TradeBar ParseOption(SubscriptionDataConfig config, string line, DateTime date)
773  {
774  return ParseOption<TradeBar>(config, line, date);
775  }
776 
777  /// <summary>
778  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
779  /// </summary>
780  /// <param name="config">Symbols, Resolution, DataType, </param>
781  /// <param name="streamReader">The data stream of the requested file</param>
782  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
783  /// <returns></returns>
784  public static TradeBar ParseOption(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
785  {
786  return ParseOption<TradeBar>(config, streamReader, date);
787  }
788 
789  /// <summary>
790  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
791  /// </summary>
792  /// <param name="config">Symbols, Resolution, DataType, </param>
793  /// <param name="line">Line from the data file requested</param>
794  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
795  /// <returns></returns>
796  public static TradeBar ParseFuture(SubscriptionDataConfig config, string line, DateTime date)
797  {
798  return ParseFuture<TradeBar>(config, line, date);
799  }
800 
801  /// <summary>
802  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
803  /// </summary>
804  /// <param name="config">Symbols, Resolution, DataType, </param>
805  /// <param name="streamReader">The data stream of the requested file</param>
806  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
807  /// <returns></returns>
808  public static TradeBar ParseFuture(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
809  {
810  return ParseFuture<TradeBar>(config, streamReader, date);
811  }
812 
813  /// <summary>
814  /// Update the tradebar - build the bar from this pricing information:
815  /// </summary>
816  /// <param name="lastTrade">This trade price</param>
817  /// <param name="bidPrice">Current bid price (not used) </param>
818  /// <param name="askPrice">Current asking price (not used) </param>
819  /// <param name="volume">Volume of this trade</param>
820  /// <param name="bidSize">The size of the current bid, if available</param>
821  /// <param name="askSize">The size of the current ask, if available</param>
822  public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
823  {
824  Initialize(lastTrade);
825  if (lastTrade > High) High = lastTrade;
826  if (lastTrade < Low) Low = lastTrade;
827  //Volume is the total summed volume of trades in this bar:
828  Volume += volume;
829  //Always set the closing price;
830  Close = lastTrade;
831  }
832 
833  /// <summary>
834  /// Get Source for Custom Data File
835  /// >> What source file location would you prefer for each type of usage:
836  /// </summary>
837  /// <param name="config">Configuration object</param>
838  /// <param name="date">Date of this source request if source spread across multiple files</param>
839  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
840  /// <returns>String source location of the file</returns>
841  public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
842  {
843  if (isLiveMode)
844  {
845  // this data type is streamed in live mode
846  return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
847  }
848 
849  var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
850  if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
851  {
852  source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
853  }
854  return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
855  }
856 
857  /// <summary>
858  /// Return a new instance clone of this object, used in fill forward
859  /// </summary>
860  /// <param name="fillForward">True if this is a fill forward clone</param>
861  /// <returns>A clone of the current object</returns>
862  public override BaseData Clone(bool fillForward)
863  {
864  var clone = base.Clone(fillForward);
865 
866  if (fillForward)
867  {
868  // zero volume out, since it would skew calculations in volume-based indicators
869  ((TradeBar)clone).Volume = 0;
870  }
871 
872  return clone;
873  }
874 
875  /// <summary>
876  /// Return a new instance clone of this object
877  /// </summary>
878  public override BaseData Clone()
879  {
880  return (BaseData)MemberwiseClone();
881  }
882 
883  /// <summary>
884  /// Formats a string with the symbol and value.
885  /// </summary>
886  /// <returns>string - a string formatted as SPY: 167.753</returns>
887  public override string ToString()
888  {
889  return $"{Symbol}: " +
890  $"O: {Open.SmartRounding()} " +
891  $"H: {High.SmartRounding()} " +
892  $"L: {Low.SmartRounding()} " +
893  $"C: {Close.SmartRounding()} " +
894  $"V: {Volume.SmartRounding()}";
895  }
896 
897  /// <summary>
898  /// Gets the scaling factor according to the <see cref="SecurityType"/> of the <see cref="Symbol"/> provided.
899  /// Non-equity data will not be scaled, including options with an underlying non-equity asset class.
900  /// </summary>
901  /// <param name="symbol">Symbol to get scaling factor for</param>
902  /// <returns>Scaling factor</returns>
903  private static decimal GetScaleFactor(Symbol symbol)
904  {
905  return UseScaleFactor(symbol)
906  ? _scaleFactor
907  : 1;
908  }
909 
910  private static bool UseScaleFactor(Symbol symbol)
911  {
912  return symbol.SecurityType == SecurityType.Equity ||
913  symbol.SecurityType == SecurityType.Option ||
914  symbol.SecurityType == SecurityType.IndexOption;
915  }
916 
917  /// <summary>
918  /// Initializes this bar with a first data point
919  /// </summary>
920  /// <param name="value">The seed value for this bar</param>
921  private void Initialize(decimal value)
922  {
923  if (Interlocked.CompareExchange(ref _initialized, 1, 0) == 0)
924  {
925  _open = value;
926  _low = value;
927  _high = value;
928  }
929  }
930  }
931 }