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  StreamParseScale(config, streamReader, date, useScaleFactor: true, tradeBar, true);
373 
374  return tradeBar;
375  }
376 
377  private static void ParseEquity(TradeBar tradeBar, SubscriptionDataConfig config, string line, DateTime date)
378  {
379  LineParseScale(config, line, date, useScaleFactor: true, tradeBar, hasVolume: true);
380  }
381 
382  /// <summary>
383  /// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
384  /// </summary>
385  /// <param name="config">Symbols, Resolution, DataType, </param>
386  /// <param name="line">Line from the data file requested</param>
387  /// <param name="date">Date of this reader request</param>
388  /// <returns></returns>
389  public static TradeBar ParseEquity(SubscriptionDataConfig config, string line, DateTime date)
390  {
391  var tradeBar = new TradeBar
392  {
393  Symbol = config.Symbol,
394  Period = config.Increment
395  };
396  ParseEquity(tradeBar, config, line, date);
397  return tradeBar;
398  }
399 
400  /// <summary>
401  /// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
402  /// </summary>
403  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
404  /// <param name="config">Symbols, Resolution, DataType, </param>
405  /// <param name="line">Line from the data file requested</param>
406  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
407  /// <returns></returns>
408  public static T ParseForex<T>(SubscriptionDataConfig config, string line, DateTime date)
409  where T : TradeBar, new()
410  {
411  var tradeBar = new T
412  {
413  Symbol = config.Symbol,
414  Period = config.Increment
415  };
416  LineParseNoScale(config, line, date, tradeBar, hasVolume: false);
417 
418  return tradeBar;
419  }
420 
421  /// <summary>
422  /// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
423  /// </summary>
424  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
425  /// <param name="config">Symbols, Resolution, DataType, </param>
426  /// <param name="line">Line from the data file requested</param>
427  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
428  public static T ParseCrypto<T>(SubscriptionDataConfig config, string line, DateTime date)
429  where T : TradeBar, new()
430  {
431  var tradeBar = new T
432  {
433  Symbol = config.Symbol,
434  Period = config.Increment
435  };
436  LineParseNoScale(config, line, date, tradeBar);
437 
438  return tradeBar;
439  }
440 
441  /// <summary>
442  /// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
443  /// </summary>
444  /// <param name="config">Symbols, Resolution, DataType, </param>
445  /// <param name="line">Line from the data file requested</param>
446  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
447  public static TradeBar ParseCrypto(SubscriptionDataConfig config, string line, DateTime date)
448  {
449  return LineParseNoScale(config, line, date);
450  }
451 
452  /// <summary>
453  /// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
454  /// </summary>
455  /// <param name="config">Symbols, Resolution, DataType, </param>
456  /// <param name="streamReader">The data stream of the requested file</param>
457  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
458  public static TradeBar ParseCrypto(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
459  {
460  return StreamParseNoScale(config, streamReader, date);
461  }
462 
463  /// <summary>
464  /// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
465  /// </summary>
466  /// <param name="config">Symbols, Resolution, DataType, </param>
467  /// <param name="line">Line from the data file requested</param>
468  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
469  /// <returns></returns>
470  public static TradeBar ParseForex(SubscriptionDataConfig config, string line, DateTime date)
471  {
472  return LineParseNoScale(config, line, date, hasVolume: false);
473  }
474 
475  /// <summary>
476  /// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
477  /// </summary>
478  /// <param name="config">Symbols, Resolution, DataType, </param>
479  /// <param name="streamReader">The data stream of the requested file</param>
480  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
481  /// <returns></returns>
482  public static TradeBar ParseForex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
483  {
484  return StreamParseNoScale(config, streamReader, date, hasVolume: false);
485  }
486 
487  /// <summary>
488  /// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
489  /// </summary>
490  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
491  /// <param name="config">Symbols, Resolution, DataType, </param>
492  /// <param name="line">Line from the data file requested</param>
493  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
494  /// <returns></returns>
495  public static T ParseCfd<T>(SubscriptionDataConfig config, string line, DateTime date)
496  where T : TradeBar, new()
497  {
498  // CFD has the same data format as Forex
499  return ParseForex<T>(config, line, date);
500  }
501 
502  /// <summary>
503  /// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
504  /// </summary>
505  /// <param name="config">Symbols, Resolution, DataType, </param>
506  /// <param name="line">Line from the data file requested</param>
507  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
508  /// <returns></returns>
509  public static TradeBar ParseCfd(SubscriptionDataConfig config, string line, DateTime date)
510  {
511  // CFD has the same data format as Forex
512  return ParseForex(config, line, date);
513  }
514 
515  /// <summary>
516  /// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
517  /// </summary>
518  /// <param name="config">Symbols, Resolution, DataType, </param>
519  /// <param name="streamReader">The data stream of the requested file</param>
520  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
521  /// <returns></returns>
522  public static TradeBar ParseCfd(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
523  {
524  // CFD has the same data format as Forex
525  return ParseForex(config, streamReader, date);
526  }
527 
528  /// <summary>
529  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
530  /// </summary>
531  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
532  /// <param name="config">Symbols, Resolution, DataType, </param>
533  /// <param name="line">Line from the data file requested</param>
534  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
535  /// <returns></returns>
536  public static T ParseOption<T>(SubscriptionDataConfig config, string line, DateTime date)
537  where T : TradeBar, new()
538  {
539  var tradeBar = new T
540  {
541  Period = config.Increment,
542  Symbol = config.Symbol
543  };
544  LineParseScale(config, line, date, useScaleFactor: LeanData.OptionUseScaleFactor(config.Symbol), tradeBar, hasVolume: true);
545 
546  return tradeBar;
547  }
548 
549  /// <summary>
550  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
551  /// </summary>
552  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
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 T ParseOption<T>(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
558  where T : TradeBar, new()
559  {
560  var tradeBar = new T
561  {
562  Period = config.Increment,
563  Symbol = config.Symbol
564  };
565  StreamParseScale(config, streamReader, date, useScaleFactor: LeanData.OptionUseScaleFactor(config.Symbol), tradeBar, true);
566 
567  return tradeBar;
568  }
569 
570  /// <summary>
571  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
572  /// </summary>
573  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
574  /// <param name="config">Symbols, Resolution, DataType, </param>
575  /// <param name="streamReader">The data stream of the requested file</param>
576  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
577  /// <returns></returns>
578  public static T ParseFuture<T>(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
579  where T : TradeBar, new()
580  {
581  var tradeBar = new T
582  {
583  Period = config.Increment,
584  Symbol = config.Symbol
585  };
586  StreamParseNoScale(config, streamReader, date, tradeBar);
587 
588  return tradeBar;
589  }
590 
591  /// <summary>
592  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
593  /// </summary>
594  /// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
595  /// <param name="config">Symbols, Resolution, DataType, </param>
596  /// <param name="line">Line from the data file requested</param>
597  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
598  /// <returns></returns>
599  public static T ParseFuture<T>(SubscriptionDataConfig config, string line, DateTime date)
600  where T : TradeBar, new()
601  {
602  var tradeBar = new T
603  {
604  Period = config.Increment,
605  Symbol = config.Symbol
606  };
607  LineParseNoScale(config, line, date, tradeBar);
608 
609  return tradeBar;
610  }
611 
612  /// <summary>
613  /// Parse an index bar from the LEAN disk format
614  /// </summary>
615  public static TradeBar ParseIndex(SubscriptionDataConfig config, string line, DateTime date)
616  {
617  return LineParseNoScale(config, line, date);
618  }
619 
620  /// <summary>
621  /// Parse an index bar from the LEAN disk format
622  /// </summary>
623  private static TradeBar LineParseNoScale(SubscriptionDataConfig config, string line, DateTime date, TradeBar bar = null, bool hasVolume = true)
624  {
625  var tradeBar = bar ?? new TradeBar
626  {
627  Period = config.Increment,
628  Symbol = config.Symbol
629  };
630 
631  var csv = line.ToCsv(hasVolume ? 6 : 5);
632  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
633  {
634  // hourly and daily have different time format, and can use slow, robust c# parser.
635  tradeBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
636  }
637  else
638  {
639  // Using custom "ToDecimal" conversion for speed on high resolution data.
640  tradeBar.Time = date.Date.AddMilliseconds(csv[0].ToInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
641  }
642  tradeBar.Open = csv[1].ToDecimal();
643  tradeBar.High = csv[2].ToDecimal();
644  tradeBar.Low = csv[3].ToDecimal();
645  tradeBar.Close = csv[4].ToDecimal();
646  if (hasVolume)
647  {
648  tradeBar.Volume = csv[5].ToDecimal();
649  }
650  return tradeBar;
651  }
652 
653  /// <summary>
654  /// Parse an index bar from the LEAN disk format
655  /// </summary>
656  private static TradeBar StreamParseNoScale(SubscriptionDataConfig config, StreamReader streamReader, DateTime date, TradeBar bar = null, bool hasVolume = true)
657  {
658  var tradeBar = bar ?? new TradeBar
659  {
660  Period = config.Increment,
661  Symbol = config.Symbol
662  };
663 
664  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
665  {
666  // hourly and daily have different time format, and can use slow, robust c# parser.
667  tradeBar.Time = streamReader.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
668  }
669  else
670  {
671  // Using custom "ToDecimal" conversion for speed on high resolution data.
672  tradeBar.Time = date.Date.AddMilliseconds(streamReader.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
673  }
674  tradeBar.Open = streamReader.GetDecimal();
675  tradeBar.High = streamReader.GetDecimal();
676  tradeBar.Low = streamReader.GetDecimal();
677  tradeBar.Close = streamReader.GetDecimal();
678  if (hasVolume)
679  {
680  tradeBar.Volume = streamReader.GetDecimal();
681  }
682  return tradeBar;
683  }
684 
685  private static TradeBar LineParseScale(SubscriptionDataConfig config, string line, DateTime date, bool useScaleFactor, TradeBar bar = null, bool hasVolume = true)
686  {
687  var tradeBar = bar ?? new TradeBar
688  {
689  Period = config.Increment,
690  Symbol = config.Symbol
691  };
692 
693  LineParseNoScale(config, line, date, tradeBar, hasVolume);
694  if (useScaleFactor)
695  {
696  tradeBar.Open *= _scaleFactor;
697  tradeBar.High *= _scaleFactor;
698  tradeBar.Low *= _scaleFactor;
699  tradeBar.Close *= _scaleFactor;
700  }
701 
702  return tradeBar;
703  }
704 
705  private static TradeBar StreamParseScale(SubscriptionDataConfig config, StreamReader streamReader, DateTime date, bool useScaleFactor, TradeBar bar = null, bool hasVolume = true)
706  {
707  var tradeBar = bar ?? new TradeBar
708  {
709  Period = config.Increment,
710  Symbol = config.Symbol
711  };
712 
713  StreamParseNoScale(config, streamReader, date, tradeBar, hasVolume);
714  if (useScaleFactor)
715  {
716  tradeBar.Open *= _scaleFactor;
717  tradeBar.High *= _scaleFactor;
718  tradeBar.Low *= _scaleFactor;
719  tradeBar.Close *= _scaleFactor;
720  }
721 
722  return tradeBar;
723  }
724 
725  /// <summary>
726  /// Parse an index bar from the LEAN disk format
727  /// </summary>
728  public static TradeBar ParseIndex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
729  {
730  return StreamParseNoScale(config, streamReader, date);
731  }
732 
733  /// <summary>
734  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
735  /// </summary>
736  /// <param name="config">Symbols, Resolution, DataType, </param>
737  /// <param name="line">Line from the data file requested</param>
738  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
739  /// <returns></returns>
740  public static TradeBar ParseOption(SubscriptionDataConfig config, string line, DateTime date)
741  {
742  return ParseOption<TradeBar>(config, line, date);
743  }
744 
745  /// <summary>
746  /// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
747  /// </summary>
748  /// <param name="config">Symbols, Resolution, DataType, </param>
749  /// <param name="streamReader">The data stream of the requested file</param>
750  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
751  /// <returns></returns>
752  public static TradeBar ParseOption(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
753  {
754  return ParseOption<TradeBar>(config, streamReader, date);
755  }
756 
757  /// <summary>
758  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
759  /// </summary>
760  /// <param name="config">Symbols, Resolution, DataType, </param>
761  /// <param name="line">Line from the data file requested</param>
762  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
763  /// <returns></returns>
764  public static TradeBar ParseFuture(SubscriptionDataConfig config, string line, DateTime date)
765  {
766  return ParseFuture<TradeBar>(config, line, date);
767  }
768 
769  /// <summary>
770  /// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
771  /// </summary>
772  /// <param name="config">Symbols, Resolution, DataType, </param>
773  /// <param name="streamReader">The data stream of the requested file</param>
774  /// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
775  /// <returns></returns>
776  public static TradeBar ParseFuture(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
777  {
778  return ParseFuture<TradeBar>(config, streamReader, date);
779  }
780 
781  /// <summary>
782  /// Update the tradebar - build the bar from this pricing information:
783  /// </summary>
784  /// <param name="lastTrade">This trade price</param>
785  /// <param name="bidPrice">Current bid price (not used) </param>
786  /// <param name="askPrice">Current asking price (not used) </param>
787  /// <param name="volume">Volume of this trade</param>
788  /// <param name="bidSize">The size of the current bid, if available</param>
789  /// <param name="askSize">The size of the current ask, if available</param>
790  public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
791  {
792  Initialize(lastTrade);
793  if (lastTrade > High) High = lastTrade;
794  if (lastTrade < Low) Low = lastTrade;
795  //Volume is the total summed volume of trades in this bar:
796  Volume += volume;
797  //Always set the closing price;
798  Close = lastTrade;
799  }
800 
801  /// <summary>
802  /// Get Source for Custom Data File
803  /// >> What source file location would you prefer for each type of usage:
804  /// </summary>
805  /// <param name="config">Configuration object</param>
806  /// <param name="date">Date of this source request if source spread across multiple files</param>
807  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
808  /// <returns>String source location of the file</returns>
809  public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
810  {
811  if (isLiveMode)
812  {
813  // this data type is streamed in live mode
814  return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
815  }
816 
817  var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
818  if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
819  {
820  source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
821  }
822  return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
823  }
824 
825  /// <summary>
826  /// Return a new instance clone of this object, used in fill forward
827  /// </summary>
828  /// <param name="fillForward">True if this is a fill forward clone</param>
829  /// <returns>A clone of the current object</returns>
830  public override BaseData Clone(bool fillForward)
831  {
832  var clone = base.Clone(fillForward);
833 
834  if (fillForward)
835  {
836  // zero volume out, since it would skew calculations in volume-based indicators
837  ((TradeBar)clone).Volume = 0;
838  }
839 
840  return clone;
841  }
842 
843  /// <summary>
844  /// Return a new instance clone of this object
845  /// </summary>
846  public override BaseData Clone()
847  {
848  return (BaseData)MemberwiseClone();
849  }
850 
851  /// <summary>
852  /// Formats a string with the symbol and value.
853  /// </summary>
854  /// <returns>string - a string formatted as SPY: 167.753</returns>
855  public override string ToString()
856  {
857  return $"{Symbol}: " +
858  $"O: {Open.SmartRounding()} " +
859  $"H: {High.SmartRounding()} " +
860  $"L: {Low.SmartRounding()} " +
861  $"C: {Close.SmartRounding()} " +
862  $"V: {Volume.SmartRounding()}";
863  }
864 
865  /// <summary>
866  /// Initializes this bar with a first data point
867  /// </summary>
868  /// <param name="value">The seed value for this bar</param>
869  private void Initialize(decimal value)
870  {
871  if (Interlocked.CompareExchange(ref _initialized, 1, 0) == 0)
872  {
873  _open = value;
874  _low = value;
875  _high = value;
876  }
877  }
878  }
879 }