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