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