Lean  $LEAN_TAG$
Tick.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 Newtonsoft.Json;
20 using QuantConnect.Util;
21 using QuantConnect.Logging;
22 using System.Globalization;
23 using System.Runtime.CompilerServices;
24 
26 {
27  /// <summary>
28  /// Tick class is the base representation for tick data. It is grouped into a Ticks object
29  /// which implements IDictionary and passed into an OnData event handler.
30  /// </summary>
31  [ProtoContract(SkipConstructor = true)]
32  [ProtoInclude(1000, typeof(OpenInterest))]
33  public class Tick : BaseData
34  {
35  private Exchange _exchange = QuantConnect.Exchange.UNKNOWN;
36  private string _exchangeValue;
37  private uint? _parsedSaleCondition;
38 
39  /// <summary>
40  /// Type of the Tick: Trade or Quote.
41  /// </summary>
42  [ProtoMember(10)]
43  public TickType TickType { get; set; } = TickType.Trade;
44 
45  /// <summary>
46  /// Quantity exchanged in a trade.
47  /// </summary>
48  [ProtoMember(11)]
49  public decimal Quantity { get; set; }
50 
51  /// <summary>
52  /// Exchange code this tick came from <see cref="Exchanges"/>
53  /// </summary>
54  public string ExchangeCode
55  {
56  get
57  {
58  if (_exchange == null)
59  {
60  _exchange = Symbol != null
61  ? _exchangeValue.GetPrimaryExchange(Symbol.SecurityType, Symbol.ID.Market) : _exchangeValue.GetPrimaryExchange();
62  }
63  return _exchange.Code;
64  }
65  set
66  {
67  _exchangeValue = value;
68  _exchange = null;
69  }
70  }
71 
72  /// <summary>
73  /// Exchange name this tick came from <see cref="Exchanges"/>
74  /// </summary>
75  [ProtoMember(12)]
76  public string Exchange
77  {
78  get
79  {
80  if (_exchange == null)
81  {
82  _exchange = Symbol != null
83  ? _exchangeValue.GetPrimaryExchange(Symbol.SecurityType, Symbol.ID.Market) : _exchangeValue.GetPrimaryExchange();
84  }
85  return _exchange;
86  }
87  set
88  {
89  _exchangeValue = value;
90  _exchange = null;
91  }
92  }
93 
94  /// <summary>
95  /// Sale condition for the tick.
96  /// </summary>
97  public string SaleCondition { get; set; } = string.Empty;
98 
99  /// <summary>
100  /// For performance parsed sale condition for the tick.
101  /// </summary>
102  [JsonIgnore]
103  public uint ParsedSaleCondition
104  {
105  get
106  {
107  if (string.IsNullOrEmpty(SaleCondition))
108  {
109  return 0;
110  }
111 
112  if (!_parsedSaleCondition.HasValue)
113  {
114  _parsedSaleCondition = uint.Parse(SaleCondition, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
115  }
116  return _parsedSaleCondition.Value;
117  }
118  set
119  {
120  _parsedSaleCondition = value;
121  }
122  }
123 
124  /// <summary>
125  /// Bool whether this is a suspicious tick
126  /// </summary>
127  [ProtoMember(14)]
128  public bool Suspicious { get; set; }
129 
130  /// <summary>
131  /// Bid Price for Tick
132  /// </summary>
133  [ProtoMember(15)]
134  public decimal BidPrice { get; set; }
135 
136  /// <summary>
137  /// Asking price for the Tick quote.
138  /// </summary>
139  [ProtoMember(16)]
140  public decimal AskPrice { get; set; }
141 
142  /// <summary>
143  /// Alias for "Value" - the last sale for this asset.
144  /// </summary>
145  public decimal LastPrice
146  {
147  get
148  {
149  return Value;
150  }
151  }
152 
153  /// <summary>
154  /// Size of bid quote.
155  /// </summary>
156  [ProtoMember(17)]
157  public decimal BidSize { get; set; }
158 
159  /// <summary>
160  /// Size of ask quote.
161  /// </summary>
162  [ProtoMember(18)]
163  public decimal AskSize { get; set; }
164 
165  //In Base Class: Alias of Closing:
166  //public decimal Price;
167 
168  //Symbol of Asset.
169  //In Base Class: public Symbol Symbol;
170 
171  //In Base Class: DateTime Of this TradeBar
172  //public DateTime Time;
173 
174  /// <summary>
175  /// Initialize tick class with a default constructor.
176  /// </summary>
177  public Tick()
178  {
179  Value = 0;
180  Time = new DateTime();
181  DataType = MarketDataType.Tick;
182  Symbol = Symbol.Empty;
183  TickType = TickType.Trade;
184  Quantity = 0;
185  _exchange = QuantConnect.Exchange.UNKNOWN;
186  SaleCondition = string.Empty;
187  Suspicious = false;
188  BidSize = 0;
189  AskSize = 0;
190  }
191 
192  /// <summary>
193  /// Cloner constructor for fill forward engine implementation. Clone the original tick into this new tick:
194  /// </summary>
195  /// <param name="original">Original tick we're cloning</param>
196  public Tick(Tick original)
197  {
198  Symbol = original.Symbol;
199  Time = new DateTime(original.Time.Ticks);
200  Value = original.Value;
201  BidPrice = original.BidPrice;
202  AskPrice = original.AskPrice;
203  // directly set privates so we don't parse the exchange
204  _exchange = original._exchange;
205  _exchangeValue = original._exchangeValue;
206  SaleCondition = original.SaleCondition;
207  Quantity = original.Quantity;
208  Suspicious = original.Suspicious;
209  DataType = MarketDataType.Tick;
210  TickType = original.TickType;
211  BidSize = original.BidSize;
212  AskSize = original.AskSize;
213  }
214 
215  /// <summary>
216  /// Constructor for a FOREX tick where there is no last sale price. The volume in FX is so high its rare to find FX trade data.
217  /// To fake this the tick contains bid-ask prices and the last price is the midpoint.
218  /// </summary>
219  /// <param name="time">Full date and time</param>
220  /// <param name="symbol">Underlying currency pair we're trading</param>
221  /// <param name="bid">FX tick bid value</param>
222  /// <param name="ask">FX tick ask value</param>
223  public Tick(DateTime time, Symbol symbol, decimal bid, decimal ask)
224  {
225  DataType = MarketDataType.Tick;
226  Time = time;
227  Symbol = symbol;
228  Value = (bid + ask) / 2;
229  TickType = TickType.Quote;
230  BidPrice = bid;
231  AskPrice = ask;
232  }
233 
234  /// <summary>
235  /// Initializes a new instance of the <see cref="Tick"/> class to <see cref="TickType.OpenInterest"/>.
236  /// </summary>
237  /// <param name="time">The time at which the open interest tick occurred.</param>
238  /// <param name="symbol">The symbol associated with the open interest tick.</param>
239  /// <param name="openInterest">The value of the open interest for the specified symbol.</param>
240  public Tick(DateTime time, Symbol symbol, decimal openInterest)
241  {
242  Time = time;
243  Symbol = symbol;
244  Value = openInterest;
245  DataType = MarketDataType.Tick;
246  TickType = TickType.OpenInterest;
247  }
248 
249  /// <summary>
250  /// Initializer for a last-trade equity tick with bid or ask prices.
251  /// </summary>
252  /// <param name="time">Full date and time</param>
253  /// <param name="symbol">Underlying equity security symbol</param>
254  /// <param name="bid">Bid value</param>
255  /// <param name="ask">Ask value</param>
256  /// <param name="last">Last trade price</param>
257  public Tick(DateTime time, Symbol symbol, decimal last, decimal bid, decimal ask)
258  {
259  DataType = MarketDataType.Tick;
260  Time = time;
261  Symbol = symbol;
262  Value = last;
263  TickType = TickType.Quote;
264  BidPrice = bid;
265  AskPrice = ask;
266  }
267 
268  /// <summary>
269  /// Trade tick type constructor
270  /// </summary>
271  /// <param name="time">Full date and time</param>
272  /// <param name="symbol">Underlying equity security symbol</param>
273  /// <param name="saleCondition">The ticks sale condition</param>
274  /// <param name="exchange">The ticks exchange</param>
275  /// <param name="quantity">The quantity traded</param>
276  /// <param name="price">The price of the trade</param>
277  public Tick(DateTime time, Symbol symbol, string saleCondition, string exchange, decimal quantity, decimal price)
278  {
279  Value = price;
280  Time = time;
281  DataType = MarketDataType.Tick;
282  Symbol = symbol;
283  TickType = TickType.Trade;
284  Quantity = quantity;
285  Exchange = exchange;
286  SaleCondition = saleCondition;
287  Suspicious = false;
288  }
289 
290  /// <summary>
291  /// Trade tick type constructor
292  /// </summary>
293  /// <param name="time">Full date and time</param>
294  /// <param name="symbol">Underlying equity security symbol</param>
295  /// <param name="saleCondition">The ticks sale condition</param>
296  /// <param name="exchange">The ticks exchange</param>
297  /// <param name="quantity">The quantity traded</param>
298  /// <param name="price">The price of the trade</param>
299  public Tick(DateTime time, Symbol symbol, string saleCondition, Exchange exchange, decimal quantity, decimal price)
300  : this(time, symbol, saleCondition, string.Empty, quantity, price)
301  {
302  // we were giving the exchange, set it directly
303  _exchange = exchange;
304  }
305 
306  /// <summary>
307  /// Quote tick type constructor
308  /// </summary>
309  /// <param name="time">Full date and time</param>
310  /// <param name="symbol">Underlying equity security symbol</param>
311  /// <param name="saleCondition">The ticks sale condition</param>
312  /// <param name="exchange">The ticks exchange</param>
313  /// <param name="bidSize">The bid size</param>
314  /// <param name="bidPrice">The bid price</param>
315  /// <param name="askSize">The ask size</param>
316  /// <param name="askPrice">The ask price</param>
317  public Tick(DateTime time, Symbol symbol, string saleCondition, string exchange, decimal bidSize, decimal bidPrice, decimal askSize, decimal askPrice)
318  {
319  Time = time;
320  DataType = MarketDataType.Tick;
321  Symbol = symbol;
322  TickType = TickType.Quote;
323  Exchange = exchange;
324  SaleCondition = saleCondition;
325  Suspicious = false;
326  AskPrice = askPrice;
327  AskSize = askSize;
328  BidPrice = bidPrice;
329  BidSize = bidSize;
330  }
331 
332  /// <summary>
333  /// Quote tick type constructor
334  /// </summary>
335  /// <param name="time">Full date and time</param>
336  /// <param name="symbol">Underlying equity security symbol</param>
337  /// <param name="saleCondition">The ticks sale condition</param>
338  /// <param name="exchange">The ticks exchange</param>
339  /// <param name="bidSize">The bid size</param>
340  /// <param name="bidPrice">The bid price</param>
341  /// <param name="askSize">The ask size</param>
342  /// <param name="askPrice">The ask price</param>
343  public Tick(DateTime time, Symbol symbol, string saleCondition, Exchange exchange, decimal bidSize, decimal bidPrice, decimal askSize, decimal askPrice)
344  : this(time, symbol, saleCondition, string.Empty, bidSize, bidPrice, askSize, askPrice)
345  {
346  // we were giving the exchange, set it directly
347  _exchange = exchange;
348  }
349 
350  /// <summary>
351  /// Constructor for QuantConnect FXCM Data source:
352  /// </summary>
353  /// <param name="symbol">Symbol for underlying asset</param>
354  /// <param name="line">CSV line of data from FXCM</param>
355  public Tick(Symbol symbol, string line)
356  {
357  var csv = line.Split(',');
358  DataType = MarketDataType.Tick;
359  Symbol = symbol;
360  Time = DateTime.ParseExact(csv[0], DateFormat.Forex, CultureInfo.InvariantCulture);
361  Value = (BidPrice + AskPrice) / 2;
362  TickType = TickType.Quote;
363  BidPrice = Convert.ToDecimal(csv[1], CultureInfo.InvariantCulture);
364  AskPrice = Convert.ToDecimal(csv[2], CultureInfo.InvariantCulture);
365  }
366 
367  /// <summary>
368  /// Constructor for QuantConnect tick data
369  /// </summary>
370  /// <param name="symbol">Symbol for underlying asset</param>
371  /// <param name="line">CSV line of data from QC tick csv</param>
372  /// <param name="baseDate">The base date of the tick</param>
373  public Tick(Symbol symbol, string line, DateTime baseDate)
374  {
375  var csv = line.Split(',');
376  DataType = MarketDataType.Tick;
377  Symbol = symbol;
378  Time = baseDate.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()));
379  Value = csv[1].ToDecimal() / GetScaleFactor(symbol);
380  TickType = TickType.Trade;
381  Quantity = csv[2].ToDecimal();
382  Exchange = csv[3].Trim();
383  SaleCondition = csv[4];
384  Suspicious = csv[5].ToInt32() == 1;
385  }
386 
387  /// <summary>
388  /// Parse a tick data line from quantconnect zip source files.
389  /// </summary>
390  /// <param name="reader">The source stream reader</param>
391  /// <param name="date">Base date for the tick (ticks date is stored as int milliseconds since midnight)</param>
392  /// <param name="config">Subscription configuration object</param>
393  public Tick(SubscriptionDataConfig config, StreamReader reader, DateTime date)
394  {
395  try
396  {
397  DataType = MarketDataType.Tick;
398  Symbol = config.Symbol;
399 
400  // Which security type is this data feed:
401  var scaleFactor = GetScaleFactor(config.Symbol);
402 
403  switch (config.SecurityType)
404  {
405  case SecurityType.Equity:
406  {
407  TickType = config.TickType;
408  Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal())).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
409 
410  bool pastLineEnd;
411  if (TickType == TickType.Trade)
412  {
413  Value = reader.GetDecimal() / scaleFactor;
414  Quantity = reader.GetDecimal(out pastLineEnd);
415  if (!pastLineEnd)
416  {
417  Exchange = reader.GetString();
418  SaleCondition = reader.GetString();
419  Suspicious = reader.GetInt32() == 1;
420  }
421  }
422  else if (TickType == TickType.Quote)
423  {
424  BidPrice = reader.GetDecimal() / scaleFactor;
425  BidSize = reader.GetDecimal();
426  AskPrice = reader.GetDecimal() / scaleFactor;
427  AskSize = reader.GetDecimal(out pastLineEnd);
428 
429  SetValue();
430 
431  if (!pastLineEnd)
432  {
433  Exchange = reader.GetString();
434  SaleCondition = reader.GetString();
435  Suspicious = reader.GetInt32() == 1;
436  }
437  }
438  else
439  {
440  throw new InvalidOperationException($"Tick(): Unexpected tick type {TickType}");
441  }
442  break;
443  }
444 
445  case SecurityType.Forex:
446  case SecurityType.Cfd:
447  {
448  TickType = TickType.Quote;
449  Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal()))
450  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
451  BidPrice = reader.GetDecimal();
452  AskPrice = reader.GetDecimal();
453 
454  SetValue();
455  break;
456  }
457 
458  case SecurityType.CryptoFuture:
459  case SecurityType.Crypto:
460  {
461  TickType = config.TickType;
462  Exchange = config.Market;
463  Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal()))
464  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
465 
466  if (TickType == TickType.Trade)
467  {
468  Value = reader.GetDecimal();
469  Quantity = reader.GetDecimal(out var endOfLine);
470  Suspicious = !endOfLine && reader.GetInt32() == 1;
471  }
472  else if(TickType == TickType.Quote)
473  {
474  BidPrice = reader.GetDecimal();
475  BidSize = reader.GetDecimal();
476  AskPrice = reader.GetDecimal();
477  AskSize = reader.GetDecimal(out var endOfLine);
478  Suspicious = !endOfLine && reader.GetInt32() == 1;
479 
480  SetValue();
481  }
482  break;
483  }
484  case SecurityType.Future:
485  case SecurityType.Option:
486  case SecurityType.FutureOption:
487  case SecurityType.IndexOption:
488  {
489  TickType = config.TickType;
490  Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal()))
491  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
492 
493  if (TickType == TickType.Trade)
494  {
495  Value = reader.GetDecimal() / scaleFactor;
496  Quantity = reader.GetDecimal();
497  Exchange = reader.GetString();
498  SaleCondition = reader.GetString();
499  Suspicious = reader.GetInt32() == 1;
500  }
501  else if (TickType == TickType.OpenInterest)
502  {
503  Value = reader.GetDecimal();
504  }
505  else
506  {
507  BidPrice = reader.GetDecimal() / scaleFactor;
508  BidSize = reader.GetDecimal();
509  AskPrice = reader.GetDecimal() / scaleFactor;
510  AskSize = reader.GetDecimal();
511  Exchange = reader.GetString();
512  Suspicious = reader.GetInt32() == 1;
513 
514  SetValue();
515  }
516 
517  break;
518  }
519  }
520  }
521  catch (Exception err)
522  {
523  Log.Error(err);
524  }
525  }
526 
527  /// <summary>
528  /// Parse a tick data line from quantconnect zip source files.
529  /// </summary>
530  /// <param name="line">CSV source line of the compressed source</param>
531  /// <param name="date">Base date for the tick (ticks date is stored as int milliseconds since midnight)</param>
532  /// <param name="config">Subscription configuration object</param>
533  public Tick(SubscriptionDataConfig config, string line, DateTime date)
534  {
535  try
536  {
537  DataType = MarketDataType.Tick;
538  Symbol = config.Symbol;
539 
540  // Which security type is this data feed:
541  var scaleFactor = GetScaleFactor(config.Symbol);
542 
543  switch (config.SecurityType)
544  {
545  case SecurityType.Equity:
546  {
547  var index = 0;
548  TickType = config.TickType;
549  var csv = line.ToCsv(TickType == TickType.Trade ? 6 : 8);
550  Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[index++].ToDecimal())).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
551 
552  if (TickType == TickType.Trade)
553  {
554  Value = csv[index++].ToDecimal() / scaleFactor;
555  Quantity = csv[index++].ToDecimal();
556  if (csv.Count > index)
557  {
558  Exchange = csv[index++];
559  SaleCondition = csv[index++];
560  Suspicious = (csv[index++] == "1");
561  }
562  }
563  else if (TickType == TickType.Quote)
564  {
565  BidPrice = csv[index++].ToDecimal() / scaleFactor;
566  BidSize = csv[index++].ToDecimal();
567  AskPrice = csv[index++].ToDecimal() / scaleFactor;
568  AskSize = csv[index++].ToDecimal();
569 
570  SetValue();
571 
572  if (csv.Count > index)
573  {
574  Exchange = csv[index++];
575  SaleCondition = csv[index++];
576  Suspicious = (csv[index++] == "1");
577  }
578  }
579  else
580  {
581  throw new InvalidOperationException($"Tick(): Unexpected tick type {TickType}");
582  }
583  break;
584  }
585 
586  case SecurityType.Forex:
587  case SecurityType.Cfd:
588  {
589  var csv = line.ToCsv(3);
590  TickType = TickType.Quote;
591  var ticks = (long)(csv[0].ToDecimal() * TimeSpan.TicksPerMillisecond);
592  Time = date.Date.AddTicks(ticks)
593  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
594  BidPrice = csv[1].ToDecimal();
595  AskPrice = csv[2].ToDecimal();
596 
597  SetValue();
598  break;
599  }
600 
601  case SecurityType.Crypto:
602  case SecurityType.CryptoFuture:
603  {
604  TickType = config.TickType;
605  Exchange = config.Market;
606 
607  if (TickType == TickType.Trade)
608  {
609  var csv = line.ToCsv(3);
610  Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()))
611  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
612  Value = csv[1].ToDecimal();
613  Quantity = csv[2].ToDecimal();
614  Suspicious = csv.Count >= 4 && csv[3] == "1";
615  }
616 
617  if (TickType == TickType.Quote)
618  {
619  var csv = line.ToCsv(6);
620  Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()))
621  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
622  BidPrice = csv[1].ToDecimal();
623  BidSize = csv[2].ToDecimal();
624  AskPrice = csv[3].ToDecimal();
625  AskSize = csv[4].ToDecimal();
626  Suspicious = csv.Count >= 6 && csv[5] == "1";
627 
628  SetValue();
629  }
630  break;
631  }
632  case SecurityType.Future:
633  case SecurityType.Option:
634  case SecurityType.FutureOption:
635  case SecurityType.IndexOption:
636  {
637  var csv = line.ToCsv(7);
638  TickType = config.TickType;
639  Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()))
640  .ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
641 
642  if (TickType == TickType.Trade)
643  {
644  Value = csv[1].ToDecimal()/scaleFactor;
645  Quantity = csv[2].ToDecimal();
646  Exchange = csv[3];
647  SaleCondition = csv[4];
648  Suspicious = csv[5] == "1";
649  }
650  else if (TickType == TickType.OpenInterest)
651  {
652  Value = csv[1].ToDecimal();
653  }
654  else
655  {
656  if (csv[1].Length != 0)
657  {
658  BidPrice = csv[1].ToDecimal()/scaleFactor;
659  BidSize = csv[2].ToDecimal();
660  }
661  if (csv[3].Length != 0)
662  {
663  AskPrice = csv[3].ToDecimal()/scaleFactor;
664  AskSize = csv[4].ToDecimal();
665  }
666  Exchange = csv[5];
667  Suspicious = csv[6] == "1";
668 
669  SetValue();
670  }
671 
672  break;
673  }
674  }
675  }
676  catch (Exception err)
677  {
678  Log.Error(err);
679  }
680  }
681 
682  /// <summary>
683  /// Tick implementation of reader method: read a line of data from the source and convert it to a tick object.
684  /// </summary>
685  /// <param name="config">Subscription configuration object for algorithm</param>
686  /// <param name="line">Line from the datafeed source</param>
687  /// <param name="date">Date of this reader request</param>
688  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
689  /// <returns>New Initialized tick</returns>
690  public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
691  {
692  if (isLiveMode)
693  {
694  // currently ticks don't come through the reader function
695  return new Tick();
696  }
697 
698  return new Tick(config, line, date);
699  }
700 
701  /// <summary>
702  /// Tick implementation of reader method: read a line of data from the source and convert it to a tick object.
703  /// </summary>
704  /// <param name="config">Subscription configuration object for algorithm</param>
705  /// <param name="stream">The source stream reader</param>
706  /// <param name="date">Date of this reader request</param>
707  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
708  /// <returns>New Initialized tick</returns>
709  public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
710  {
711  if (isLiveMode)
712  {
713  // currently ticks don't come through the reader function
714  return new Tick();
715  }
716 
717  return new Tick(config, stream, date);
718  }
719 
720 
721  /// <summary>
722  /// Get source for tick data feed - not used with QuantConnect data sources implementation.
723  /// </summary>
724  /// <param name="config">Configuration object</param>
725  /// <param name="date">Date of this source request if source spread across multiple files</param>
726  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
727  /// <returns>String source location of the file to be opened with a stream</returns>
728  public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
729  {
730  if (isLiveMode)
731  {
732  // this data type is streamed in live mode
733  return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
734  }
735 
736  var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
737  if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
738  {
739  source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
740  }
741  return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
742  }
743 
744  /// <summary>
745  /// Update the tick price information - not used.
746  /// </summary>
747  /// <param name="lastTrade">This trade price</param>
748  /// <param name="bidPrice">Current bid price</param>
749  /// <param name="askPrice">Current asking price</param>
750  /// <param name="volume">Volume of this trade</param>
751  /// <param name="bidSize">The size of the current bid, if available</param>
752  /// <param name="askSize">The size of the current ask, if available</param>
753  [MethodImpl(MethodImplOptions.AggressiveInlining)]
754  public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
755  {
756  Value = lastTrade;
757  BidPrice = bidPrice;
758  AskPrice = askPrice;
759  BidSize = bidSize;
760  AskSize = askSize;
761  Quantity = Convert.ToDecimal(volume);
762  }
763 
764  /// <summary>
765  /// Check if tick contains valid data (either a trade, or a bid or ask)
766  /// </summary>
767  [MethodImpl(MethodImplOptions.AggressiveInlining)]
768  public bool IsValid()
769  {
770  // Indexes have zero volume in live trading, but is still a valid tick.
771  return (TickType == TickType.Trade && (LastPrice > 0.0m && (Quantity > 0 || Symbol.SecurityType == SecurityType.Index))) ||
772  (TickType == TickType.Quote && AskPrice > 0.0m && AskSize > 0) ||
773  (TickType == TickType.Quote && BidPrice > 0.0m && BidSize > 0) ||
774  (TickType == TickType.OpenInterest && Value > 0);
775  }
776 
777  /// <summary>
778  /// Clone implementation for tick class:
779  /// </summary>
780  /// <returns>New tick object clone of the current class values.</returns>
781  public override BaseData Clone()
782  {
783  return new Tick(this);
784  }
785 
786  /// <summary>
787  /// Formats a string with the symbol and value.
788  /// </summary>
789  /// <returns>string - a string formatted as SPY: 167.753</returns>
790  public override string ToString()
791  {
792  switch (TickType)
793  {
794  case TickType.Trade:
795  return $"{Symbol}: Price: {Price} Quantity: {Quantity}";
796 
797  case TickType.Quote:
798  return $"{Symbol}: Bid: {BidSize}@{BidPrice} Ask: {AskSize}@{AskPrice}";
799 
800  case TickType.OpenInterest:
801  return $"{Symbol}: OpenInterest: {Value}";
802 
803  default:
804  throw new ArgumentOutOfRangeException();
805  }
806  }
807 
808  /// <summary>
809  /// Sets the tick Value based on ask and bid price
810  /// </summary>
811  public void SetValue()
812  {
813  Value = BidPrice + AskPrice;
814  if (BidPrice * AskPrice != 0)
815  {
816  Value /= 2m;
817  }
818  }
819 
820  /// <summary>
821  /// Gets the scaling factor according to the <see cref="SecurityType"/> of the <see cref="Symbol"/> provided.
822  /// Non-equity data will not be scaled, including options with an underlying non-equity asset class.
823  /// </summary>
824  /// <param name="symbol">Symbol to get scaling factor for</param>
825  /// <returns>Scaling factor</returns>
826  private static decimal GetScaleFactor(Symbol symbol)
827  {
828  return symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Option ? 10000m : 1;
829  }
830 
831  }
832 }