Lean  $LEAN_TAG$
QuoteBar.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 System.Globalization;
18 using System.IO;
19 using System.Runtime.CompilerServices;
20 using ProtoBuf;
21 using QuantConnect.Logging;
22 using QuantConnect.Python;
23 using QuantConnect.Util;
24 using static QuantConnect.StringExtensions;
25 
27 {
28  /// <summary>
29  /// QuoteBar 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 QuoteBar : BaseData, IBaseDataBar
34  {
35  // scale factor used in QC equity/option/indexOption data files
36  private const decimal _scaleFactor = 1 / 10000m;
37 
38  /// <summary>
39  /// Average bid size
40  /// </summary>
41  [ProtoMember(201)]
42  [PandasColumn("bidsize")]
43  public decimal LastBidSize { get; set; }
44 
45  /// <summary>
46  /// Average ask size
47  /// </summary>
48  [ProtoMember(202)]
49  [PandasColumn("asksize")]
50  public decimal LastAskSize { get; set; }
51 
52  /// <summary>
53  /// Bid OHLC
54  /// </summary>
55  [ProtoMember(203)]
56  public Bar Bid { get; set; }
57 
58  /// <summary>
59  /// Ask OHLC
60  /// </summary>
61  [ProtoMember(204)]
62  public Bar Ask { get; set; }
63 
64  /// <summary>
65  /// Opening price of the bar: Defined as the price at the start of the time period.
66  /// </summary>
67  public decimal Open
68  {
69  get
70  {
71  if (Bid != null && Ask != null)
72  {
73  if (Bid.Open != 0m && Ask.Open != 0m)
74  return (Bid.Open + Ask.Open) / 2m;
75 
76  if (Bid.Open != 0)
77  return Bid.Open;
78 
79  if (Ask.Open != 0)
80  return Ask.Open;
81 
82  return 0m;
83  }
84  if (Bid != null)
85  {
86  return Bid.Open;
87  }
88  if (Ask != null)
89  {
90  return Ask.Open;
91  }
92  return 0m;
93  }
94  }
95 
96  /// <summary>
97  /// High price of the QuoteBar during the time period.
98  /// </summary>
99  public decimal High
100  {
101  get
102  {
103  if (Bid != null && Ask != null)
104  {
105  if (Bid.High != 0m && Ask.High != 0m)
106  return (Bid.High + Ask.High) / 2m;
107 
108  if (Bid.High != 0)
109  return Bid.High;
110 
111  if (Ask.High != 0)
112  return Ask.High;
113 
114  return 0m;
115  }
116  if (Bid != null)
117  {
118  return Bid.High;
119  }
120  if (Ask != null)
121  {
122  return Ask.High;
123  }
124  return 0m;
125  }
126  }
127 
128  /// <summary>
129  /// Low price of the QuoteBar during the time period.
130  /// </summary>
131  public decimal Low
132  {
133  get
134  {
135  if (Bid != null && Ask != null)
136  {
137  if (Bid.Low != 0m && Ask.Low != 0m)
138  return (Bid.Low + Ask.Low) / 2m;
139 
140  if (Bid.Low != 0)
141  return Bid.Low;
142 
143  if (Ask.Low != 0)
144  return Ask.Low;
145 
146  return 0m;
147  }
148  if (Bid != null)
149  {
150  return Bid.Low;
151  }
152  if (Ask != null)
153  {
154  return Ask.Low;
155  }
156  return 0m;
157  }
158  }
159 
160  /// <summary>
161  /// Closing price of the QuoteBar. Defined as the price at Start Time + TimeSpan.
162  /// </summary>
163  public decimal Close
164  {
165  get
166  {
167  if (Bid != null && Ask != null)
168  {
169  if (Bid.Close != 0m && Ask.Close != 0m)
170  return (Bid.Close + Ask.Close) / 2m;
171 
172  if (Bid.Close != 0)
173  return Bid.Close;
174 
175  if (Ask.Close != 0)
176  return Ask.Close;
177 
178  return 0m;
179  }
180  if (Bid != null)
181  {
182  return Bid.Close;
183  }
184  if (Ask != null)
185  {
186  return Ask.Close;
187  }
188  return Value;
189  }
190  }
191 
192  /// <summary>
193  /// The closing time of this bar, computed via the Time and Period
194  /// </summary>
195  [PandasIgnore]
196  public override DateTime EndTime
197  {
198  get { return Time + Period; }
199  set { Period = value - Time; }
200  }
201 
202  /// <summary>
203  /// The period of this quote bar, (second, minute, daily, ect...)
204  /// </summary>
205  [ProtoMember(205)]
206  [PandasIgnore]
207  public TimeSpan Period { get; set; }
208 
209  /// <summary>
210  /// Default initializer to setup an empty quotebar.
211  /// </summary>
212  public QuoteBar()
213  {
214  Symbol = Symbol.Empty;
215  Time = new DateTime();
216  Bid = new Bar();
217  Ask = new Bar();
218  Value = 0;
220  DataType = MarketDataType.QuoteBar;
221  }
222 
223  /// <summary>
224  /// Initialize Quote Bar with Bid(OHLC) and Ask(OHLC) Values:
225  /// </summary>
226  /// <param name="time">DateTime Timestamp of the bar</param>
227  /// <param name="symbol">Market MarketType Symbol</param>
228  /// <param name="bid">Bid OLHC bar</param>
229  /// <param name="lastBidSize">Average bid size over period</param>
230  /// <param name="ask">Ask OLHC bar</param>
231  /// <param name="lastAskSize">Average ask size over period</param>
232  /// <param name="period">The period of this bar, specify null for default of 1 minute</param>
233  public QuoteBar(DateTime time, Symbol symbol, IBar bid, decimal lastBidSize, IBar ask, decimal lastAskSize, TimeSpan? period = null)
234  {
235  Symbol = symbol;
236  Time = time;
237  Bid = bid == null ? null : new Bar(bid.Open, bid.High, bid.Low, bid.Close);
238  Ask = ask == null ? null : new Bar(ask.Open, ask.High, ask.Low, ask.Close);
239  if (Bid != null) LastBidSize = lastBidSize;
240  if (Ask != null) LastAskSize = lastAskSize;
241  Value = Close;
242  Period = period ?? QuantConnect.Time.OneMinute;
243  DataType = MarketDataType.QuoteBar;
244  }
245 
246  /// <summary>
247  /// Update the quotebar - build the bar from this pricing information:
248  /// </summary>
249  /// <param name="lastTrade">The last trade price</param>
250  /// <param name="bidPrice">Current bid price</param>
251  /// <param name="askPrice">Current asking price</param>
252  /// <param name="volume">Volume of this trade</param>
253  /// <param name="bidSize">The size of the current bid, if available, if not, pass 0</param>
254  /// <param name="askSize">The size of the current ask, if available, if not, pass 0</param>
255  [MethodImpl(MethodImplOptions.AggressiveInlining)]
256  public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
257  {
258  // update our bid and ask bars - handle null values, this is to give good values for midpoint OHLC
259  if (Bid == null && bidPrice != 0) Bid = new Bar(bidPrice, bidPrice, bidPrice, bidPrice);
260  else if (Bid != null) Bid.Update(ref bidPrice);
261 
262  if (Ask == null && askPrice != 0) Ask = new Bar(askPrice, askPrice, askPrice, askPrice);
263  else if (Ask != null) Ask.Update(ref askPrice);
264 
265  if (bidSize > 0)
266  {
267  LastBidSize = bidSize;
268  }
269 
270  if (askSize > 0)
271  {
272  LastAskSize = askSize;
273  }
274 
275  // be prepared for updates without trades
276  if (lastTrade != 0) Value = lastTrade;
277  else if (askPrice != 0) Value = askPrice;
278  else if (bidPrice != 0) Value = bidPrice;
279  }
280 
281  /// <summary>
282  /// QuoteBar Reader: Fetch the data from the QC storage and feed it line by line into the engine.
283  /// </summary>
284  /// <param name="config">Symbols, Resolution, DataType, </param>
285  /// <param name="stream">The file data stream</param>
286  /// <param name="date">Date of this reader request</param>
287  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
288  /// <returns>Enumerable iterator for returning each line of the required data.</returns>
289  public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
290  {
291  try
292  {
293  switch (config.SecurityType)
294  {
295  case SecurityType.Equity:
296  return ParseEquity(config, stream, date);
297 
298  case SecurityType.Forex:
299  case SecurityType.Crypto:
300  case SecurityType.CryptoFuture:
301  return ParseForex(config, stream, date);
302 
303  case SecurityType.Cfd:
304  return ParseCfd(config, stream, date);
305 
306  case SecurityType.Option:
307  case SecurityType.FutureOption:
308  case SecurityType.IndexOption:
309  return ParseOption(config, stream, date);
310 
311  case SecurityType.Future:
312  return ParseFuture(config, stream, date);
313 
314  }
315  }
316  catch (Exception err)
317  {
318  Log.Error(Invariant($"QuoteBar.Reader(): Error parsing stream, Symbol: {config.Symbol.Value}, SecurityType: {config.SecurityType}, ") +
319  Invariant($"Resolution: {config.Resolution}, Date: {date.ToStringInvariant("yyyy-MM-dd")}, Message: {err}")
320  );
321  }
322 
323  // we need to consume a line anyway, to advance the stream
324  stream.ReadLine();
325 
326  // if we couldn't parse it above return a default instance
327  return new QuoteBar { Symbol = config.Symbol, Period = config.Increment };
328  }
329 
330  /// <summary>
331  /// QuoteBar Reader: Fetch the data from the QC storage and feed it line by line into the engine.
332  /// </summary>
333  /// <param name="config">Symbols, Resolution, DataType, </param>
334  /// <param name="line">Line from the data file requested</param>
335  /// <param name="date">Date of this reader request</param>
336  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
337  /// <returns>Enumerable iterator for returning each line of the required data.</returns>
338  public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
339  {
340  try
341  {
342  switch (config.SecurityType)
343  {
344  case SecurityType.Equity:
345  return ParseEquity(config, line, date);
346 
347  case SecurityType.Forex:
348  case SecurityType.Crypto:
349  case SecurityType.CryptoFuture:
350  return ParseForex(config, line, date);
351 
352  case SecurityType.Cfd:
353  return ParseCfd(config, line, date);
354 
355  case SecurityType.Option:
356  case SecurityType.FutureOption:
357  case SecurityType.IndexOption:
358  return ParseOption(config, line, date);
359 
360  case SecurityType.Future:
361  return ParseFuture(config, line, date);
362 
363  }
364  }
365  catch (Exception err)
366  {
367  Log.Error(Invariant($"QuoteBar.Reader(): Error parsing line: '{line}', Symbol: {config.Symbol.Value}, SecurityType: {config.SecurityType}, ") +
368  Invariant($"Resolution: {config.Resolution}, Date: {date.ToStringInvariant("yyyy-MM-dd")}, Message: {err}")
369  );
370  }
371 
372  // if we couldn't parse it above return a default instance
373  return new QuoteBar { Symbol = config.Symbol, Period = config.Increment };
374  }
375 
376  /// <summary>
377  /// Parse a quotebar representing a future with a scaling factor
378  /// </summary>
379  /// <param name="config">Symbols, Resolution, DataType</param>
380  /// <param name="line">Line from the data file requested</param>
381  /// <param name="date">Date of this reader request</param>
382  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
383  public QuoteBar ParseFuture(SubscriptionDataConfig config, string line, DateTime date)
384  {
385  return ParseQuote(config, date, line, false);
386  }
387 
388  /// <summary>
389  /// Parse a quotebar representing a future with a scaling factor
390  /// </summary>
391  /// <param name="config">Symbols, Resolution, DataType</param>
392  /// <param name="streamReader">The data stream of the requested file</param>
393  /// <param name="date">Date of this reader request</param>
394  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
395  public QuoteBar ParseFuture(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
396  {
397  return ParseQuote(config, date, streamReader, false);
398  }
399 
400  /// <summary>
401  /// Parse a quotebar representing an option with a scaling factor
402  /// </summary>
403  /// <param name="config">Symbols, Resolution, DataType</param>
404  /// <param name="line">Line from the data file requested</param>
405  /// <param name="date">Date of this reader request</param>
406  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
407  public QuoteBar ParseOption(SubscriptionDataConfig config, string line, DateTime date)
408  {
409  return ParseQuote(config, date, line, LeanData.OptionUseScaleFactor(config.Symbol));
410  }
411 
412  /// <summary>
413  /// Parse a quotebar representing an option with a scaling factor
414  /// </summary>
415  /// <param name="config">Symbols, Resolution, DataType</param>
416  /// <param name="streamReader">The data stream of the requested file</param>
417  /// <param name="date">Date of this reader request</param>
418  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
419  public QuoteBar ParseOption(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
420  {
421  // scale factor only applies for equity and index options
422  return ParseQuote(config, date, streamReader, useScaleFactor: LeanData.OptionUseScaleFactor(config.Symbol));
423  }
424 
425  /// <summary>
426  /// Parse a quotebar representing a cfd without a scaling factor
427  /// </summary>
428  /// <param name="config">Symbols, Resolution, DataType</param>
429  /// <param name="line">Line from the data file requested</param>
430  /// <param name="date">Date of this reader request</param>
431  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
432  public QuoteBar ParseCfd(SubscriptionDataConfig config, string line, DateTime date)
433  {
434  return ParseQuote(config, date, line, false);
435  }
436 
437  /// <summary>
438  /// Parse a quotebar representing a cfd without a scaling factor
439  /// </summary>
440  /// <param name="config">Symbols, Resolution, DataType</param>
441  /// <param name="streamReader">The data stream of the requested file</param>
442  /// <param name="date">Date of this reader request</param>
443  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
444  public QuoteBar ParseCfd(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
445  {
446  return ParseQuote(config, date, streamReader, false);
447  }
448 
449  /// <summary>
450  /// Parse a quotebar representing a forex without a scaling factor
451  /// </summary>
452  /// <param name="config">Symbols, Resolution, DataType</param>
453  /// <param name="line">Line from the data file requested</param>
454  /// <param name="date">Date of this reader request</param>
455  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
456  public QuoteBar ParseForex(SubscriptionDataConfig config, string line, DateTime date)
457  {
458  return ParseQuote(config, date, line, false);
459  }
460 
461  /// <summary>
462  /// Parse a quotebar representing a forex without a scaling factor
463  /// </summary>
464  /// <param name="config">Symbols, Resolution, DataType</param>
465  /// <param name="streamReader">The data stream of the requested file</param>
466  /// <param name="date">Date of this reader request</param>
467  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
468  public QuoteBar ParseForex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
469  {
470  return ParseQuote(config, date, streamReader, false);
471  }
472 
473  /// <summary>
474  /// Parse a quotebar representing an equity with a scaling factor
475  /// </summary>
476  /// <param name="config">Symbols, Resolution, DataType</param>
477  /// <param name="line">Line from the data file requested</param>
478  /// <param name="date">Date of this reader request</param>
479  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
480  public QuoteBar ParseEquity(SubscriptionDataConfig config, string line, DateTime date)
481  {
482  return ParseQuote(config, date, line, true);
483  }
484 
485  /// <summary>
486  /// Parse a quotebar representing an equity with a scaling factor
487  /// </summary>
488  /// <param name="config">Symbols, Resolution, DataType</param>
489  /// <param name="streamReader">The data stream of the requested file</param>
490  /// <param name="date">Date of this reader request</param>
491  /// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
492  public QuoteBar ParseEquity(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
493  {
494  return ParseQuote(config, date, streamReader, true);
495  }
496 
497  /// <summary>
498  /// "Scaffold" code - If the data being read is formatted as a QuoteBar, use this method to deserialize it
499  /// </summary>
500  /// <param name="config">Symbols, Resolution, DataType, </param>
501  /// <param name="streamReader">The data stream of the requested file</param>
502  /// <param name="date">Date of this reader request</param>
503  /// <param name="useScaleFactor">Whether the data has a scaling factor applied</param>
504  /// <returns><see cref="QuoteBar"/> with the bid/ask prices set appropriately</returns>
505  private QuoteBar ParseQuote(SubscriptionDataConfig config, DateTime date, StreamReader streamReader, bool useScaleFactor)
506  {
507  // Non-equity asset classes will not use scaling, including options that have a non-equity underlying asset class.
508  var scaleFactor = useScaleFactor
509  ? _scaleFactor
510  : 1;
511 
512  var quoteBar = new QuoteBar
513  {
514  Period = config.Increment,
515  Symbol = config.Symbol
516  };
517 
518  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
519  {
520  // hourly and daily have different time format, and can use slow, robust c# parser.
521  quoteBar.Time = streamReader.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
522  }
523  else
524  {
525  // Using custom int conversion for speed on high resolution data.
526  quoteBar.Time = date.Date.AddMilliseconds(streamReader.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
527  }
528 
529  var open = streamReader.GetDecimal();
530  var high = streamReader.GetDecimal();
531  var low = streamReader.GetDecimal();
532  var close = streamReader.GetDecimal();
533  var lastSize = streamReader.GetDecimal();
534  // only create the bid if it exists in the file
535  if (open != 0 || high != 0 || low != 0 || close != 0)
536  {
537  // the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
538  quoteBar.Bid.Open = open * scaleFactor;
539  quoteBar.Bid.High = high * scaleFactor;
540  quoteBar.Bid.Low = low * scaleFactor;
541  quoteBar.Bid.Close = close * scaleFactor;
542  quoteBar.LastBidSize = lastSize;
543  }
544  else
545  {
546  quoteBar.Bid = null;
547  }
548 
549  open = streamReader.GetDecimal();
550  high = streamReader.GetDecimal();
551  low = streamReader.GetDecimal();
552  close = streamReader.GetDecimal();
553  lastSize = streamReader.GetDecimal();
554  // only create the ask if it exists in the file
555  if (open != 0 || high != 0 || low != 0 || close != 0)
556  {
557  // the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
558  quoteBar.Ask.Open = open * scaleFactor;
559  quoteBar.Ask.High = high * scaleFactor;
560  quoteBar.Ask.Low = low * scaleFactor;
561  quoteBar.Ask.Close = close * scaleFactor;
562  quoteBar.LastAskSize = lastSize;
563  }
564  else
565  {
566  quoteBar.Ask = null;
567  }
568 
569  quoteBar.Value = quoteBar.Close;
570  return quoteBar;
571  }
572 
573  /// <summary>
574  /// "Scaffold" code - If the data being read is formatted as a QuoteBar, use this method to deserialize it
575  /// TODO: Once all Forex data refactored to use QuoteBar formatted data, use only this method
576  /// </summary>
577  /// <param name="config">Symbols, Resolution, DataType, </param>
578  /// <param name="line">Line from the data file requested</param>
579  /// <param name="date">Date of this reader request</param>
580  /// <param name="useScaleFactor">Whether the data has a scaling factor applied</param>
581  /// <returns><see cref="QuoteBar"/> with the bid/ask prices set appropriately</returns>
582  private QuoteBar ParseQuote(SubscriptionDataConfig config, DateTime date, string line, bool useScaleFactor)
583  {
584  var scaleFactor = useScaleFactor
585  ? _scaleFactor
586  : 1;
587 
588  var quoteBar = new QuoteBar
589  {
590  Period = config.Increment,
591  Symbol = config.Symbol
592  };
593 
594  var csv = line.ToCsv(11);
595  if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
596  {
597  // hourly and daily have different time format, and can use slow, robust c# parser.
598  quoteBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
599  }
600  else
601  {
602  // Using custom "ToDecimal" conversion for speed on high resolution data.
603  quoteBar.Time = date.Date.AddMilliseconds((double)csv[0].ToDecimal()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
604  }
605 
606  // only create the bid if it exists in the file
607  if (csv[1].Length != 0 || csv[2].Length != 0 || csv[3].Length != 0 || csv[4].Length != 0)
608  {
609  // the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
610  quoteBar.Bid.Open = csv[1].ToDecimal() * scaleFactor;
611  quoteBar.Bid.High = csv[2].ToDecimal() * scaleFactor;
612  quoteBar.Bid.Low = csv[3].ToDecimal() * scaleFactor;
613  quoteBar.Bid.Close = csv[4].ToDecimal() * scaleFactor;
614  quoteBar.LastBidSize = csv[5].ToDecimal();
615  }
616  else
617  {
618  quoteBar.Bid = null;
619  }
620 
621  // only create the ask if it exists in the file
622  if (csv[6].Length != 0 || csv[7].Length != 0 || csv[8].Length != 0 || csv[9].Length != 0)
623  {
624  // the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
625  quoteBar.Ask.Open = csv[6].ToDecimal() * scaleFactor;
626  quoteBar.Ask.High = csv[7].ToDecimal() * scaleFactor;
627  quoteBar.Ask.Low = csv[8].ToDecimal() * scaleFactor;
628  quoteBar.Ask.Close = csv[9].ToDecimal() * scaleFactor;
629  quoteBar.LastAskSize = csv[10].ToDecimal();
630  }
631  else
632  {
633  quoteBar.Ask = null;
634  }
635 
636  quoteBar.Value = quoteBar.Close;
637  return quoteBar;
638  }
639 
640  /// <summary>
641  /// Get Source for Custom Data File
642  /// >> What source file location would you prefer for each type of usage:
643  /// </summary>
644  /// <param name="config">Configuration object</param>
645  /// <param name="date">Date of this source request if source spread across multiple files</param>
646  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
647  /// <returns>String source location of the file</returns>
648  public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
649  {
650  if (isLiveMode)
651  {
652  // this data type is streamed in live mode
653  return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
654  }
655 
656  var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
657  if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
658  {
659  source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
660  }
661  return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
662  }
663 
664  /// <summary>
665  /// Return a new instance clone of this quote bar, used in fill forward
666  /// </summary>
667  /// <returns>A clone of the current quote bar</returns>
668  public override BaseData Clone()
669  {
670  return new QuoteBar
671  {
672  Ask = Ask == null ? null : Ask.Clone(),
673  Bid = Bid == null ? null : Bid.Clone(),
676  Symbol = Symbol,
677  Time = Time,
678  Period = Period,
679  Value = Value,
681  };
682  }
683 
684  /// <summary>
685  /// Collapses QuoteBars into TradeBars object when
686  /// algorithm requires FX data, but calls OnData(<see cref="TradeBars"/>)
687  /// TODO: (2017) Remove this method in favor of using OnData(<see cref="Slice"/>)
688  /// </summary>
689  /// <returns><see cref="TradeBars"/></returns>
691  {
692  return new TradeBar(Time, Symbol, Open, High, Low, Close, 0, Period);
693  }
694 
695  /// <summary>
696  /// Convert this <see cref="QuoteBar"/> to string form.
697  /// </summary>
698  /// <returns>String representation of the <see cref="QuoteBar"/></returns>
699  public override string ToString()
700  {
701  return $"{Symbol}: " +
702  $"Bid: O: {Bid?.Open.SmartRounding()} " +
703  $"Bid: H: {Bid?.High.SmartRounding()} " +
704  $"Bid: L: {Bid?.Low.SmartRounding()} " +
705  $"Bid: C: {Bid?.Close.SmartRounding()} " +
706  $"Ask: O: {Ask?.Open.SmartRounding()} " +
707  $"Ask: H: {Ask?.High.SmartRounding()} " +
708  $"Ask: L: {Ask?.Low.SmartRounding()} " +
709  $"Ask: C: {Ask?.Close.SmartRounding()} ";
710  }
711  }
712 }