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