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