Lean  $LEAN_TAG$
Symbol.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 
17 using System;
18 using ProtoBuf;
19 using Python.Runtime;
20 using Newtonsoft.Json;
22 using QuantConnect.Python;
23 
24 namespace QuantConnect
25 {
26  /// <summary>
27  /// Represents a unique security identifier. This is made of two components,
28  /// the unique SID and the Value. The value is the current ticker symbol while
29  /// the SID is constant over the life of a security
30  /// </summary>
31  [JsonConverter(typeof(SymbolJsonConverter))]
32  [ProtoContract(SkipConstructor = true)]
33  [PandasNonExpandable]
34  public sealed class Symbol : IEquatable<Symbol>, IComparable, ISymbol
35  {
36  private static readonly Lazy<SecurityDefinitionSymbolResolver> _securityDefinitionSymbolResolver = new (() => SecurityDefinitionSymbolResolver.GetInstance());
37 
38  private Symbol _canonical;
39  // for performance we register how we compare with empty
40  private bool? _isEmpty;
41 
42  /// <summary>
43  /// Represents an unassigned symbol. This is intended to be used as an
44  /// uninitialized, default value
45  /// </summary>
46  public static readonly Symbol Empty = new Symbol(SecurityIdentifier.Empty, string.Empty);
47 
48  /// <summary>
49  /// Represents no symbol. This is intended to be used when no symbol is explicitly intended
50  /// </summary>
51  public static readonly Symbol None = new Symbol(SecurityIdentifier.None, "NONE");
52 
53  /// <summary>
54  /// Provides a convenience method for creating a Symbol for most security types.
55  /// This method currently does not support Commodities
56  /// </summary>
57  /// <param name="ticker">The string ticker symbol</param>
58  /// <param name="securityType">The security type of the ticker. If securityType == Option, then a canonical symbol is created</param>
59  /// <param name="market">The market the ticker resides in</param>
60  /// <param name="alias">An alias to be used for the symbol cache. Required when
61  /// adding the same security from different markets</param>
62  /// <param name="baseDataType">Optional for <see cref="SecurityType.Base"/> and used for generating the base data SID</param>
63  /// <returns>A new Symbol object for the specified ticker</returns>
64  public static Symbol Create(string ticker, SecurityType securityType, string market, string alias = null, Type baseDataType = null)
65  {
67 
68  switch (securityType)
69  {
70  case SecurityType.Base:
71  sid = SecurityIdentifier.GenerateBase(baseDataType, ticker, market);
72  break;
73 
74  case SecurityType.Equity:
75  sid = SecurityIdentifier.GenerateEquity(ticker, market);
76  break;
77 
78  case SecurityType.Forex:
79  sid = SecurityIdentifier.GenerateForex(ticker, market);
80  break;
81 
82  case SecurityType.Cfd:
83  sid = SecurityIdentifier.GenerateCfd(ticker, market);
84  break;
85 
86  case SecurityType.Index:
87  sid = SecurityIdentifier.GenerateIndex(ticker, market);
88  break;
89 
90  case SecurityType.Option:
91  return CreateOption(ticker, market, default, default, default, SecurityIdentifier.DefaultDate);
92 
93  case SecurityType.Future:
95  break;
96 
97  case SecurityType.Crypto:
98  sid = SecurityIdentifier.GenerateCrypto(ticker, market);
99  break;
100 
101  case SecurityType.CryptoFuture:
103  break;
104 
105  case SecurityType.IndexOption:
106  return CreateOption(
107  Create(ticker, SecurityType.Index, market),
108  market,
109  OptionStyle.European,
110  default,
111  default,
113 
114  case SecurityType.FutureOption:
115  throw new NotImplementedException(Messages.Symbol.InsufficientInformationToCreateFutureOptionSymbol);
116 
117  case SecurityType.Commodity:
118  default:
119  throw new NotImplementedException(Messages.Symbol.SecurityTypeNotImplementedYet(securityType));
120  }
121 
122  return new Symbol(sid, alias ?? ticker);
123  }
124 
125  /// <summary>
126  /// Creates a new Symbol for custom data. This method allows for the creation of a new Base Symbol
127  /// using the first ticker and the first traded date from the provided underlying Symbol. This avoids
128  /// the issue for mappable types, where the ticker is remapped supposing the provided ticker value is from today.
129  /// See <see cref="SecurityIdentifier"/>'s private method GetFirstTickerAndDate.
130  /// The provided symbol is also set to <see cref="Symbol.Underlying"/> so that it can be accessed using the custom data Symbol.
131  /// This is useful for associating custom data Symbols to other asset classes so that it is possible to filter using custom data
132  /// and place trades on the underlying asset based on the filtered custom data.
133  /// </summary>
134  /// <param name="baseType">Type of BaseData instance</param>
135  /// <param name="underlying">Underlying symbol to set for the Base Symbol</param>
136  /// <param name="market">Market</param>
137  /// <returns>New non-mapped Base Symbol that contains an Underlying Symbol</returns>
138  public static Symbol CreateBase(PyObject baseType, Symbol underlying, string market = null)
139  {
140  return CreateBase(baseType.CreateType(), underlying, market);
141  }
142 
143  /// <summary>
144  /// Creates a new Symbol for custom data. This method allows for the creation of a new Base Symbol
145  /// using the first ticker and the first traded date from the provided underlying Symbol. This avoids
146  /// the issue for mappable types, where the ticker is remapped supposing the provided ticker value is from today.
147  /// See <see cref="SecurityIdentifier"/>'s private method GetFirstTickerAndDate.
148  /// The provided symbol is also set to <see cref="Symbol.Underlying"/> so that it can be accessed using the custom data Symbol.
149  /// This is useful for associating custom data Symbols to other asset classes so that it is possible to filter using custom data
150  /// and place trades on the underlying asset based on the filtered custom data.
151  /// </summary>
152  /// <param name="baseType">Type of BaseData instance</param>
153  /// <param name="underlying">Underlying symbol to set for the Base Symbol</param>
154  /// <param name="market">Market</param>
155  /// <returns>New non-mapped Base Symbol that contains an Underlying Symbol</returns>
156  public static Symbol CreateBase(Type baseType, Symbol underlying, string market = null)
157  {
158  // The SID Date is only defined for the following security types: base, equity, future, option.
159  // Default to SecurityIdentifier.DefaultDate if there's no matching SecurityType
160  var firstDate = underlying.SecurityType == SecurityType.Equity ||
161  underlying.SecurityType.IsOption() ||
162  underlying.SecurityType == SecurityType.Future ||
163  underlying.SecurityType == SecurityType.Base
164  ? underlying.ID.Date
165  : (DateTime?)null;
166 
167  var sid = SecurityIdentifier.GenerateBase(baseType, underlying.ID.Symbol, market ?? Market.USA, mapSymbol: false, date: firstDate);
168  return new Symbol(sid, underlying.Value, underlying);
169  }
170 
171  /// <summary>
172  /// Provides a convenience method for creating an option Symbol.
173  /// </summary>
174  /// <param name="underlying">The underlying ticker</param>
175  /// <param name="market">The market the underlying resides in</param>
176  /// <param name="style">The option style (American, European, ect..)</param>
177  /// <param name="right">The option right (Put/Call)</param>
178  /// <param name="strike">The option strike price</param>
179  /// <param name="expiry">The option expiry date</param>
180  /// <param name="alias">An alias to be used for the symbol cache. Required when
181  /// adding the same security from different markets</param>
182  /// <param name="mapSymbol">Specifies if symbol should be mapped using map file provider</param>
183  /// <returns>A new Symbol object for the specified option contract</returns>
184  public static Symbol CreateOption(string underlying, string market, OptionStyle style, OptionRight right, decimal strike, DateTime expiry, string alias = null, bool mapSymbol = true)
185  {
186  var underlyingSid = SecurityIdentifier.GenerateEquity(underlying, market, mapSymbol);
187  var underlyingSymbol = new Symbol(underlyingSid, underlying);
188 
189  return CreateOption(underlyingSymbol, market, style, right, strike, expiry, alias);
190  }
191 
192  /// <summary>
193  /// Provides a convenience method for creating an option Symbol using SecurityIdentifier.
194  /// </summary>
195  /// <param name="underlyingSymbol">The underlying security symbol</param>
196  /// <param name="market">The market the underlying resides in</param>
197  /// <param name="style">The option style (American, European, ect..)</param>
198  /// <param name="right">The option right (Put/Call)</param>
199  /// <param name="strike">The option strike price</param>
200  /// <param name="expiry">The option expiry date</param>
201  /// <param name="alias">An alias to be used for the symbol cache. Required when
202  /// adding the same security from diferent markets</param>
203  /// <returns>A new Symbol object for the specified option contract</returns>
204  public static Symbol CreateOption(Symbol underlyingSymbol, string market, OptionStyle style, OptionRight right, decimal strike, DateTime expiry, string alias = null)
205  {
206  return CreateOption(underlyingSymbol, null, market, style, right, strike, expiry, alias);
207  }
208 
209  /// <summary>
210  /// Provides a convenience method for creating an option Symbol using SecurityIdentifier.
211  /// </summary>
212  /// <param name="underlyingSymbol">The underlying security symbol</param>
213  /// <param name="targetOption">The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying</param>
214  /// <param name="market">The market the underlying resides in</param>
215  /// <param name="style">The option style (American, European, ect..)</param>
216  /// <param name="right">The option right (Put/Call)</param>
217  /// <param name="strike">The option strike price</param>
218  /// <param name="expiry">The option expiry date</param>
219  /// <param name="alias">An alias to be used for the symbol cache. Required when
220  /// adding the same security from diferent markets</param>
221  /// <returns>A new Symbol object for the specified option contract</returns>
222  public static Symbol CreateOption(Symbol underlyingSymbol, string targetOption, string market, OptionStyle style, OptionRight right, decimal strike, DateTime expiry, string alias = null)
223  {
224  var sid = SecurityIdentifier.GenerateOption(expiry, underlyingSymbol.ID, targetOption, market, strike, right, style);
225 
226  return new Symbol(sid, alias ?? GetAlias(sid, underlyingSymbol), underlyingSymbol);
227  }
228 
229  /// <summary>
230  /// Provides a convenience method for creating an option Symbol from its SecurityIdentifier and alias.
231  /// </summary>
232  /// <param name="sid">The option SID</param>
233  /// <param name="value">The alias</param>
234  /// <param name="underlying">Optional underlying symbol to use. If null, it will we created from the given option SID and value</param>
235  /// <returns>A new Symbol object for the specified option</returns>
236  public static Symbol CreateOption(SecurityIdentifier sid, string value, Symbol underlying = null)
237  {
238  if (value == null)
239  {
240  throw new ArgumentNullException(nameof(value));
241  }
242 
243  if (!sid.SecurityType.IsOption())
244  {
245  throw new ArgumentException(Messages.Symbol.SidNotForOption(sid), nameof(value));
246  }
247 
248  if (IsCanonical(sid))
249  {
250  return new Symbol(sid, value);
251  }
252 
253  if (underlying == null)
254  {
256  out var _, out var underlyingValue, out var _, out var _, out var _);
257  underlying = new Symbol(sid.Underlying, underlyingValue);
258  }
259  else if (underlying.ID != sid.Underlying)
260  {
261  throw new ArgumentException(Messages.Symbol.UnderlyingSidDoesNotMatch(sid, underlying), nameof(underlying));
262  }
263 
264  return new Symbol(sid, value, underlying);
265  }
266 
267  /// <summary>
268  /// Simple method to create the canonical option symbol for any given underlying symbol
269  /// </summary>
270  /// <param name="underlyingSymbol">Underlying of this option</param>
271  /// <param name="market">Market for this option</param>
272  /// <param name="alias">An alias to be used for the symbol cache. Required when
273  /// adding the same security from different markets</param>
274  /// <returns>New Canonical Option</returns>
275  public static Symbol CreateCanonicalOption(Symbol underlyingSymbol, string market = null, string alias = null)
276  {
277  return CreateCanonicalOption(underlyingSymbol, null, market, alias);
278  }
279 
280  /// <summary>
281  /// Simple method to create the canonical option symbol for any given underlying symbol
282  /// </summary>
283  /// <param name="underlyingSymbol">Underlying of this option</param>
284  /// <param name="targetOption">The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying</param>
285  /// <param name="market">Market for this option</param>
286  /// <param name="alias">An alias to be used for the symbol cache. Required when
287  /// adding the same security from different markets</param>
288  /// <returns>New Canonical Option</returns>
289  public static Symbol CreateCanonicalOption(Symbol underlyingSymbol, string targetOption, string market = null, string alias = null)
290  {
291  var optionType = GetOptionTypeFromUnderlying(underlyingSymbol);
292  market ??= underlyingSymbol.ID.Market;
293 
294  return CreateOption(underlyingSymbol,
295  targetOption,
296  market,
297  optionType.DefaultOptionStyle(),
298  default,
299  default,
301  alias);
302  }
303 
304 
305  /// <summary>
306  /// Provides a convenience method for creating a future Symbol.
307  /// </summary>
308  /// <param name="ticker">The ticker</param>
309  /// <param name="market">The market the future resides in</param>
310  /// <param name="expiry">The future expiry date</param>
311  /// <param name="alias">An alias to be used for the symbol cache. Required when
312  /// adding the same security from different markets</param>
313  /// <returns>A new Symbol object for the specified future contract</returns>
314  public static Symbol CreateFuture(string ticker, string market, DateTime expiry, string alias = null)
315  {
316  var sid = SecurityIdentifier.GenerateFuture(expiry, ticker, market);
317 
318  return new Symbol(sid, alias ?? GetAlias(sid));
319  }
320 
321  /// <summary>
322  /// Method returns true, if symbol is a derivative canonical symbol
323  /// </summary>
324  /// <returns>true, if symbol is a derivative canonical symbol</returns>
325  public bool IsCanonical()
326  {
327  return IsCanonical(ID);
328  }
329 
330  /// <summary>
331  /// Get's the canonical representation of this symbol
332  /// </summary>
333  /// <remarks>This is useful for access and performance</remarks>
334  public Symbol Canonical
335  {
336  get
337  {
338  if (_canonical != null)
339  {
340  return _canonical;
341  }
342 
343  _canonical = this;
344  if (!IsCanonical())
345  {
346  if (SecurityType.IsOption())
347  {
348  _canonical = CreateCanonicalOption(Underlying, ID.Symbol, ID.Market, null);
349  }
350  else if (SecurityType == SecurityType.Future)
351  {
352  _canonical = Create(ID.Symbol, SecurityType.Future, ID.Market);
353  }
354  else
355  {
356  throw new InvalidOperationException(Messages.Symbol.CanonicalNotDefined);
357  }
358  }
359  return _canonical;
360  }
361  }
362 
363  /// <summary>
364  /// Determines whether the symbol has a canonical representation
365  /// </summary>
366  public bool HasCanonical()
367  {
368  return !IsCanonical() && (SecurityType.IsOption() || SecurityType == SecurityType.Future);
369  }
370 
371  /// <summary>
372  /// Determines if the specified <paramref name="symbol"/> is an underlying of this symbol instance
373  /// </summary>
374  /// <param name="symbol">The underlying to check for</param>
375  /// <returns>True if the specified <paramref name="symbol"/> is an underlying of this symbol instance</returns>
376  public bool HasUnderlyingSymbol(Symbol symbol)
377  {
378  var current = this;
379  while (current.HasUnderlying)
380  {
381  if (current.Underlying == symbol)
382  {
383  return true;
384  }
385 
386  current = current.Underlying;
387  }
388 
389  return false;
390  }
391 
392  #region Properties
393 
394  /// <summary>
395  /// Gets the current symbol for this ticker
396  /// </summary>
397  [ProtoMember(1)]
398  public string Value { get; private set; }
399 
400  /// <summary>
401  /// Gets the security identifier for this symbol
402  /// </summary>
403  [ProtoMember(2)]
404  public SecurityIdentifier ID { get; private set; }
405 
406  /// <summary>
407  /// Gets whether or not this <see cref="Symbol"/> is a derivative,
408  /// that is, it has a valid <see cref="Underlying"/> property
409  /// </summary>
410  public bool HasUnderlying
411  {
412  get { return !ReferenceEquals(Underlying, null); }
413  }
414 
415  /// <summary>
416  /// Gets the security underlying symbol, if any
417  /// </summary>
418  [ProtoMember(3)]
419  public Symbol Underlying { get; private set; }
420 
421 
422  /// <summary>
423  /// Gets the security type of the symbol
424  /// </summary>
426  {
427  get { return ID.SecurityType; }
428  }
429 
430  /// <summary>
431  /// The Committee on Uniform Securities Identification Procedures (CUSIP) number corresponding to this <see cref="Symbol"/>
432  /// </summary>
433  public string CUSIP { get { return _securityDefinitionSymbolResolver.Value.CUSIP(this); } }
434 
435  /// <summary>
436  /// The composite Financial Instrument Global Identifier (FIGI) corresponding to this <see cref="Symbol"/>
437  /// </summary>
438  public string CompositeFIGI { get { return _securityDefinitionSymbolResolver.Value.CompositeFIGI(this); } }
439 
440  /// <summary>
441  /// The Stock Exchange Daily Official List (SEDOL) security identifier corresponding to this <see cref="Symbol"/>
442  /// </summary>
443  public string SEDOL { get { return _securityDefinitionSymbolResolver.Value.SEDOL(this); } }
444 
445  /// <summary>
446  /// The International Securities Identification Number (ISIN) corresponding to this <see cref="Symbol"/>
447  /// </summary>
448  public string ISIN { get { return _securityDefinitionSymbolResolver.Value.ISIN(this); } }
449 
450  /// <summary>
451  /// The Central Index Key number (CIK) corresponding to this <see cref="Symbol"/>
452  /// </summary>
453  public int? CIK { get { return _securityDefinitionSymbolResolver.Value.CIK(this); } }
454 
455  #endregion
456 
457  #region Constructors
458 
459  /// <summary>
460  /// Initializes a new instance of the <see cref="Symbol"/> class
461  /// </summary>
462  /// <param name="sid">The security identifier for this symbol</param>
463  /// <param name="value">The current ticker symbol value</param>
464  public Symbol(SecurityIdentifier sid, string value)
465  {
466  if (value == null)
467  {
468  throw new ArgumentNullException(nameof(value));
469  }
470  ID = sid;
471  if (ID.HasUnderlying)
472  {
474  }
475 
476  Value = GetAlias(sid, Underlying) ?? value.LazyToUpper();
477  }
478 
479  /// <summary>
480  /// Creates new symbol with updated mapped symbol. Symbol Mapping: When symbols change over time (e.g. CHASE-> JPM) need to update the symbol requested.
481  /// Method returns newly created symbol
482  /// </summary>
483  public Symbol UpdateMappedSymbol(string mappedSymbol, uint contractDepthOffset = 0)
484  {
485  // Throw for any option SecurityType that is not for equities, we don't support mapping for them (FOPs and Index Options)
486  if (ID.SecurityType.IsOption() && SecurityType != SecurityType.Option)
487  {
488  throw new ArgumentException(Messages.Symbol.SecurityTypeCannotBeMapped(ID.SecurityType));
489  }
490 
491  if(ID.SecurityType == SecurityType.Future)
492  {
493  if (mappedSymbol == Value)
494  {
495  // futures with no real continuous mapping
496  return this;
497  }
498  var id = SecurityIdentifier.Parse(mappedSymbol);
499  var underlying = new Symbol(id, mappedSymbol);
500  underlying = underlying.AdjustSymbolByOffset(contractDepthOffset);
501 
502  // we map the underlying
503  return new Symbol(ID, underlying.Value, underlying);
504  }
505 
506  // Avoid updating the current instance's underlying Symbol.
507  var underlyingSymbol = Underlying;
508 
509  // Some universe Symbols, such as Constituent ETF universe Symbols and mapped custom data Symbols, have an
510  // underlying equity ETF Symbol as their underlying. When we're checking to see if a specific BaseData
511  // instance requires mapping, only the parent Symbol will be updated, which might not even need to be mapped
512  // (e.g. universe symbols with no equity ticker in symbol value).
513  // This will ensure that we map all of the underlying Symbol(s) that also require mapping updates.
514  if (HasUnderlying)
515  {
516  underlyingSymbol = Underlying.UpdateMappedSymbol(mappedSymbol, contractDepthOffset);
517  }
518 
519  // If this Symbol is not a custom data type, and the security type does not support mapping,
520  // then we know for a fact that this Symbol should not be mapped.
521  // Custom data types should be mapped, especially if this method is called on them because
522  // they can have an underlying that is also mapped.
523  if (SecurityType != SecurityType.Base && !this.RequiresMapping())
524  {
525  return new Symbol(ID, Value, underlyingSymbol);
526  }
527 
528  if (SecurityType == SecurityType.Option)
529  {
530  mappedSymbol = !IsCanonical()
532  : Value;
533  }
534 
535  return new Symbol(ID, mappedSymbol, underlyingSymbol);
536  }
537 
538  /// <summary>
539  /// Determines the SecurityType based on the underlying Symbol's SecurityType
540  /// </summary>
541  /// <param name="underlyingSymbol">Underlying Symbol of an option</param>
542  /// <returns>SecurityType of the option</returns>
543  /// <exception cref="ArgumentException">The provided underlying has no SecurityType able to represent it as an option</exception>
544  public static SecurityType GetOptionTypeFromUnderlying(Symbol underlyingSymbol)
545  {
546  return GetOptionTypeFromUnderlying(underlyingSymbol.SecurityType);
547  }
548 
549  /// <summary>
550  /// Determines the SecurityType based on the underlying Symbol's SecurityType <see cref="GetUnderlyingFromOptionType(SecurityType)"/>
551  /// </summary>
552  /// <param name="securityType">SecurityType of the underlying Symbol</param>
553  /// <returns>SecurityType of the option</returns>
554  /// <exception cref="ArgumentException">The provided underlying has no SecurityType able to represent it as an option</exception>
556  {
557  switch (securityType)
558  {
559  case SecurityType.Equity:
560  return SecurityType.Option;
561  case SecurityType.Future:
562  return SecurityType.FutureOption;
563  case SecurityType.Index:
564  return SecurityType.IndexOption;
565  default:
566  throw new ArgumentException(Messages.Symbol.NoOptionTypeForUnderlying(securityType));
567  }
568  }
569 
570  /// <summary>
571  /// Determines the underlying SecurityType based on the option Symbol's SecurityType <see cref="GetOptionTypeFromUnderlying(SecurityType)"/>
572  /// </summary>
573  /// <param name="securityType">SecurityType of the option Symbol</param>
574  /// <returns>SecurityType of the underlying</returns>
575  /// <exception cref="ArgumentException">The provided option has no SecurityType able to represent it as an underlying</exception>
577  {
578  switch (securityType)
579  {
580  case SecurityType.Option:
581  return SecurityType.Equity;
582  case SecurityType.FutureOption:
583  return SecurityType.Future;
584  case SecurityType.IndexOption:
585  return SecurityType.Index;
586  default:
587  throw new ArgumentException(Messages.Symbol.NoUnderlyingForOption(securityType));
588  }
589  }
590 
591  /// <summary>
592  /// Private constructor initializes a new instance of the <see cref="Symbol"/> class with underlying
593  /// </summary>
594  /// <param name="sid">The security identifier for this symbol</param>
595  /// <param name="value">The current ticker symbol value</param>
596  /// <param name="underlying">The underlying symbol</param>
597  internal Symbol(SecurityIdentifier sid, string value, Symbol underlying)
598  {
599  if (value == null)
600  {
601  throw new ArgumentNullException(nameof(value));
602  }
603  ID = sid;
604  Value = value.LazyToUpper();
605  Underlying = underlying;
606  }
607 
608  #endregion
609 
610  #region Overrides of Object
611 
612  /// <summary>
613  /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
614  /// </summary>
615  /// <returns>
616  /// true if the specified object is equal to the current object; otherwise, false.
617  /// </returns>
618  /// <param name="obj">The object to compare with the current object. </param><filterpriority>2</filterpriority>
619  public override bool Equals(object obj)
620  {
621  if (ReferenceEquals(null, obj)) return false;
622  if (ReferenceEquals(this, obj)) return true;
623 
624  // compare strings just as you would a symbol object
625  if (obj is string stringSymbol)
626  {
627  return Equals((Symbol)stringSymbol);
628  }
629 
630  // compare a sid just as you would a symbol object
631  if (obj is SecurityIdentifier sid)
632  {
633  return ID.Equals(sid);
634  }
635 
636  if (obj.GetType() != GetType()) return false;
637  return Equals((Symbol)obj);
638  }
639 
640  /// <summary>
641  /// Serves as a hash function for a particular type.
642  /// </summary>
643  /// <returns>
644  /// A hash code for the current <see cref="T:System.Object"/>.
645  /// </returns>
646  /// <filterpriority>2</filterpriority>
647  public override int GetHashCode()
648  {
649  // only SID is used for comparisons
650  unchecked { return ID.GetHashCode(); }
651  }
652 
653  /// <summary>
654  /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
655  /// </summary>
656  /// <returns>
657  /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj"/> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj"/>. Greater than zero This instance follows <paramref name="obj"/> in the sort order.
658  /// </returns>
659  /// <param name="obj">An object to compare with this instance. </param><exception cref="T:System.ArgumentException"><paramref name="obj"/> is not the same type as this instance. </exception><filterpriority>2</filterpriority>
660  public int CompareTo(object obj)
661  {
662  var str = obj as string;
663  if (str != null)
664  {
665  return string.Compare(Value, str, StringComparison.OrdinalIgnoreCase);
666  }
667  var sym = obj as Symbol;
668  if (sym != null)
669  {
670  return string.Compare(Value, sym.Value, StringComparison.OrdinalIgnoreCase);
671  }
672 
673  throw new ArgumentException(Messages.Symbol.UnexpectedObjectTypeToCompareTo);
674  }
675 
676  /// <summary>
677  /// Returns a string that represents the current object.
678  /// </summary>
679  /// <returns>
680  /// A string that represents the current object.
681  /// </returns>
682  /// <filterpriority>2</filterpriority>
683  public override string ToString()
684  {
685  return SymbolCache.GetTicker(this);
686  }
687 
688  #endregion
689 
690  #region Equality members
691 
692  /// <summary>
693  /// Indicates whether the current object is equal to another object of the same type.
694  /// </summary>
695  /// <returns>
696  /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
697  /// </returns>
698  /// <param name="other">An object to compare with this object.</param>
699  public bool Equals(Symbol other)
700  {
701  if (ReferenceEquals(this, other)) return true;
702 
703  if (ReferenceEquals(other, null)
704  || ReferenceEquals(other, Empty))
705  {
706  // other is null or empty (equivalents)
707  // so we need to know how We compare with Empty
708  if (!_isEmpty.HasValue)
709  {
710  // for accuracy we compare IDs not references here
711  _isEmpty = ID.Equals(Empty.ID);
712  }
713  return _isEmpty.Value;
714  }
715 
716  // only SID is used for comparisons
717  return ID.Equals(other.ID);
718  }
719 
720  /// <summary>
721  /// Equals operator
722  /// </summary>
723  /// <param name="left">The left operand</param>
724  /// <param name="right">The right operand</param>
725  /// <returns>True if both symbols are equal, otherwise false</returns>
726  public static bool operator ==(Symbol left, Symbol right)
727  {
728  if (ReferenceEquals(left, right))
729  {
730  // this is a performance shortcut
731  return true;
732  }
733 
734  if (left is null)
735  {
736  // Rely on the Equals method if possible
737  return right is null || right.Equals(left);
738  }
739 
740  return left.Equals(right);
741  }
742 
743  /// <summary>
744  /// Equals operator
745  /// </summary>
746  /// <param name="left">The left operand</param>
747  /// <param name="right">The right operand</param>
748  /// <returns>True if both symbols are equal, otherwise false</returns>
749  /// <remarks>This is necessary in cases like Pythonnet passing a string
750  /// as an object instead of using the implicit conversion</remarks>
751  public static bool operator ==(Symbol left, object right)
752  {
753  if (ReferenceEquals(left, right))
754  {
755  // this is a performance shortcut
756  return true;
757  }
758 
759  if (left is null)
760  {
761  // Rely on the Equals method if possible
762  return right is null || right.Equals(left);
763  }
764 
765  return left.Equals(right);
766  }
767 
768  /// <summary>
769  /// Equals operator
770  /// </summary>
771  /// <param name="left">The left operand</param>
772  /// <param name="right">The right operand</param>
773  /// <returns>True if both symbols are equal, otherwise false</returns>
774  /// <remarks>This is necessary in cases like Pythonnet passing a string
775  /// as an object instead of using the implicit conversion</remarks>
776  public static bool operator ==(object left, Symbol right)
777  {
778  if (ReferenceEquals(left, right))
779  {
780  // this is a performance shortcut
781  return true;
782  }
783 
784  if (left is null)
785  {
786  return right is null;
787  }
788 
789  if (left is Symbol leftSymbol)
790  {
791  return leftSymbol.Equals(right);
792  }
793 
794  if (left is string leftStr)
795  {
796  return leftStr.Equals(right?.ToString(), StringComparison.InvariantCulture);
797  }
798 
799  return false;
800  }
801 
802  /// <summary>
803  /// Not equals operator
804  /// </summary>
805  /// <param name="left">The left operand</param>
806  /// <param name="right">The right operand</param>
807  /// <returns>True if both symbols are not equal, otherwise false</returns>
808  public static bool operator !=(Symbol left, Symbol right)
809  {
810  return !(left == right);
811  }
812 
813  /// <summary>
814  /// Not equals operator
815  /// </summary>
816  /// <param name="left">The left operand</param>
817  /// <param name="right">The right operand</param>
818  /// <returns>True if both symbols are not equal, otherwise false</returns>
819  public static bool operator !=(Symbol left, object right)
820  {
821  return !(left == right);
822  }
823 
824  /// <summary>
825  /// Not equals operator
826  /// </summary>
827  /// <param name="left">The left operand</param>
828  /// <param name="right">The right operand</param>
829  /// <returns>True if both symbols are not equal, otherwise false</returns>
830  public static bool operator !=(object left, Symbol right)
831  {
832  return !(left == right);
833  }
834 
835  #endregion
836 
837  #region Implicit operators
838 
839  /// <summary>
840  /// Returns the symbol's string ticker
841  /// </summary>
842  /// <param name="symbol">The symbol</param>
843  /// <returns>The string ticker</returns>
844  [Obsolete("Symbol implicit operator to string is provided for algorithm use only.")]
845  public static implicit operator string(Symbol symbol)
846  {
847  return symbol.ToString();
848  }
849 
850  /// <summary>
851  /// Creates symbol using string as sid
852  /// </summary>
853  /// <param name="ticker">The string</param>
854  /// <returns>The symbol</returns>
855  [Obsolete("Symbol implicit operator from string is provided for algorithm use only.")]
856  public static implicit operator Symbol(string ticker)
857  {
858  Symbol symbol;
859  if (SymbolCache.TryGetSymbol(ticker, out symbol))
860  {
861  return symbol;
862  }
863 
864  return new Symbol(new SecurityIdentifier(ticker, 0), ticker);
865  }
866 
867  #endregion
868 
869  #region String methods
870 
871  // in order to maintain better compile time backwards compatibility,
872  // we'll redirect a few common string methods to Value, but mark obsolete
873 #pragma warning disable 1591
874  [Obsolete("Symbol.Contains is a pass-through for Symbol.Value.Contains")]
875  public bool Contains(string value) { return Value.Contains(value); }
876  [Obsolete("Symbol.EndsWith is a pass-through for Symbol.Value.EndsWith")]
877  public bool EndsWith(string value) { return Value.EndsWithInvariant(value); }
878  [Obsolete("Symbol.StartsWith is a pass-through for Symbol.Value.StartsWith")]
879  public bool StartsWith(string value) { return Value.StartsWithInvariant(value); }
880  [Obsolete("Symbol.ToLower is a pass-through for Symbol.Value.ToLower")]
881  public string ToLower() { return Value.ToLowerInvariant(); }
882  [Obsolete("Symbol.ToUpper is a pass-through for Symbol.Value.ToUpper")]
883  public string ToUpper() { return Value.LazyToUpper(); }
884 #pragma warning restore 1591
885 
886  #endregion
887 
888  /// <summary>
889  /// Centralized helper method to resolve alias for a symbol
890  /// </summary>
891  public static string GetAlias(SecurityIdentifier securityIdentifier, Symbol underlying = null)
892  {
893  string sym;
894  switch (securityIdentifier.SecurityType)
895  {
896  case SecurityType.FutureOption:
897  case SecurityType.Option:
898  case SecurityType.IndexOption:
899  sym = underlying.Value;
900  if (securityIdentifier.Symbol != underlying.ID.Symbol)
901  {
902  // If we have changed the SID and it does not match the underlying,
903  // we've mapped a future into another Symbol. We want to have a value
904  // representing the mapped ticker, not of the underlying.
905  // e.g. we want:
906  // OG C3200...|GC18Z20
907  // NOT
908  // GC C3200...|GC18Z20
909  sym = securityIdentifier.Symbol;
910  }
911 
912  if (securityIdentifier.Date == SecurityIdentifier.DefaultDate)
913  {
914  return $"?{sym.LazyToUpper()}";
915  }
916 
917  if (sym.Length > 5) sym += " ";
918 
919  return SymbolRepresentation.GenerateOptionTickerOSI(sym, securityIdentifier.OptionRight, securityIdentifier.StrikePrice, securityIdentifier.Date);
920  case SecurityType.Future:
921  sym = securityIdentifier.Symbol;
922  if (securityIdentifier.Date == SecurityIdentifier.DefaultDate)
923  {
924  return $"/{sym}";
925  }
926  return SymbolRepresentation.GenerateFutureTicker(sym, securityIdentifier.Date);
927  default:
928  return null;
929  }
930  }
931 
932  private static bool IsCanonical(SecurityIdentifier sid)
933  {
934  return
935  (sid.SecurityType == SecurityType.Future ||
936  (sid.SecurityType.IsOption() && sid.HasUnderlying)) &&
938  }
939  }
940 }