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
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  /// Simple method to create the canonical option symbol for any given underlying symbol
229  /// </summary>
230  /// <param name="underlyingSymbol">Underlying of this option</param>
231  /// <param name="market">Market for this option</param>
232  /// <param name="alias">An alias to be used for the symbol cache. Required when
233  /// adding the same security from different markets</param>
234  /// <returns>New Canonical Option</returns>
235  public static Symbol CreateCanonicalOption(Symbol underlyingSymbol, string market = null, string alias = null)
236  {
237  return CreateCanonicalOption(underlyingSymbol, null, market, alias);
238  }
239 
240  /// <summary>
241  /// Simple method to create the canonical option symbol for any given underlying symbol
242  /// </summary>
243  /// <param name="underlyingSymbol">Underlying of this option</param>
244  /// <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>
245  /// <param name="market">Market for this option</param>
246  /// <param name="alias">An alias to be used for the symbol cache. Required when
247  /// adding the same security from different markets</param>
248  /// <returns>New Canonical Option</returns>
249  public static Symbol CreateCanonicalOption(Symbol underlyingSymbol, string targetOption, string market = null, string alias = null)
250  {
251  var optionType = GetOptionTypeFromUnderlying(underlyingSymbol);
252  market ??= underlyingSymbol.ID.Market;
253 
254  return CreateOption(underlyingSymbol,
255  targetOption,
256  market,
257  optionType.DefaultOptionStyle(),
258  default,
259  default,
261  alias);
262  }
263 
264 
265  /// <summary>
266  /// Provides a convenience method for creating a future Symbol.
267  /// </summary>
268  /// <param name="ticker">The ticker</param>
269  /// <param name="market">The market the future resides in</param>
270  /// <param name="expiry">The future expiry date</param>
271  /// <param name="alias">An alias to be used for the symbol cache. Required when
272  /// adding the same security from different markets</param>
273  /// <returns>A new Symbol object for the specified future contract</returns>
274  public static Symbol CreateFuture(string ticker, string market, DateTime expiry, string alias = null)
275  {
276  var sid = SecurityIdentifier.GenerateFuture(expiry, ticker, market);
277 
278  return new Symbol(sid, alias ?? GetAlias(sid));
279  }
280 
281  /// <summary>
282  /// Method returns true, if symbol is a derivative canonical symbol
283  /// </summary>
284  /// <returns>true, if symbol is a derivative canonical symbol</returns>
285  public bool IsCanonical()
286  {
287  return
288  (ID.SecurityType == SecurityType.Future ||
289  (ID.SecurityType.IsOption() && HasUnderlying)) &&
291  }
292 
293  /// <summary>
294  /// Get's the canonical representation of this symbol
295  /// </summary>
296  /// <remarks>This is useful for access and performance</remarks>
297  public Symbol Canonical
298  {
299  get
300  {
301  if (_canonical != null)
302  {
303  return _canonical;
304  }
305 
306  _canonical = this;
307  if (!IsCanonical())
308  {
309  if (SecurityType.IsOption())
310  {
311  _canonical = CreateCanonicalOption(Underlying, ID.Symbol, ID.Market, null);
312  }
313  else if (SecurityType == SecurityType.Future)
314  {
315  _canonical = Create(ID.Symbol, SecurityType.Future, ID.Market);
316  }
317  else
318  {
319  throw new InvalidOperationException(Messages.Symbol.CanonicalNotDefined);
320  }
321  }
322  return _canonical;
323  }
324  }
325 
326  /// <summary>
327  /// Determines whether the symbol has a canonical representation
328  /// </summary>
329  public bool HasCanonical()
330  {
331  return !IsCanonical() && (SecurityType.IsOption() || SecurityType == SecurityType.Future);
332  }
333 
334  /// <summary>
335  /// Determines if the specified <paramref name="symbol"/> is an underlying of this symbol instance
336  /// </summary>
337  /// <param name="symbol">The underlying to check for</param>
338  /// <returns>True if the specified <paramref name="symbol"/> is an underlying of this symbol instance</returns>
339  public bool HasUnderlyingSymbol(Symbol symbol)
340  {
341  var current = this;
342  while (current.HasUnderlying)
343  {
344  if (current.Underlying == symbol)
345  {
346  return true;
347  }
348 
349  current = current.Underlying;
350  }
351 
352  return false;
353  }
354 
355  #region Properties
356 
357  /// <summary>
358  /// Gets the current symbol for this ticker
359  /// </summary>
360  [ProtoMember(1)]
361  public string Value { get; private set; }
362 
363  /// <summary>
364  /// Gets the security identifier for this symbol
365  /// </summary>
366  [ProtoMember(2)]
367  public SecurityIdentifier ID { get; private set; }
368 
369  /// <summary>
370  /// Gets whether or not this <see cref="Symbol"/> is a derivative,
371  /// that is, it has a valid <see cref="Underlying"/> property
372  /// </summary>
373  public bool HasUnderlying
374  {
375  get { return !ReferenceEquals(Underlying, null); }
376  }
377 
378  /// <summary>
379  /// Gets the security underlying symbol, if any
380  /// </summary>
381  [ProtoMember(3)]
382  public Symbol Underlying { get; private set; }
383 
384 
385  /// <summary>
386  /// Gets the security type of the symbol
387  /// </summary>
389  {
390  get { return ID.SecurityType; }
391  }
392 
393  /// <summary>
394  /// The Committee on Uniform Securities Identification Procedures (CUSIP) number corresponding to this <see cref="Symbol"/>
395  /// </summary>
396  public string CUSIP { get { return _securityDefinitionSymbolResolver.Value.CUSIP(this); } }
397 
398  /// <summary>
399  /// The composite Financial Instrument Global Identifier (FIGI) corresponding to this <see cref="Symbol"/>
400  /// </summary>
401  public string CompositeFIGI { get { return _securityDefinitionSymbolResolver.Value.CompositeFIGI(this); } }
402 
403  /// <summary>
404  /// The Stock Exchange Daily Official List (SEDOL) security identifier corresponding to this <see cref="Symbol"/>
405  /// </summary>
406  public string SEDOL { get { return _securityDefinitionSymbolResolver.Value.SEDOL(this); } }
407 
408  /// <summary>
409  /// The International Securities Identification Number (ISIN) corresponding to this <see cref="Symbol"/>
410  /// </summary>
411  public string ISIN { get { return _securityDefinitionSymbolResolver.Value.ISIN(this); } }
412 
413  /// <summary>
414  /// The Central Index Key number (CIK) corresponding to this <see cref="Symbol"/>
415  /// </summary>
416  public int? CIK { get { return _securityDefinitionSymbolResolver.Value.CIK(this); } }
417 
418  #endregion
419 
420  #region Constructors
421 
422  /// <summary>
423  /// Initializes a new instance of the <see cref="Symbol"/> class
424  /// </summary>
425  /// <param name="sid">The security identifier for this symbol</param>
426  /// <param name="value">The current ticker symbol value</param>
427  public Symbol(SecurityIdentifier sid, string value)
428  {
429  if (value == null)
430  {
431  throw new ArgumentNullException(nameof(value));
432  }
433  ID = sid;
434  if (ID.HasUnderlying)
435  {
437  }
438 
439  Value = GetAlias(sid, Underlying) ?? value.LazyToUpper();
440  }
441 
442  /// <summary>
443  /// Creates new symbol with updated mapped symbol. Symbol Mapping: When symbols change over time (e.g. CHASE-> JPM) need to update the symbol requested.
444  /// Method returns newly created symbol
445  /// </summary>
446  public Symbol UpdateMappedSymbol(string mappedSymbol, uint contractDepthOffset = 0)
447  {
448  // Throw for any option SecurityType that is not for equities, we don't support mapping for them (FOPs and Index Options)
449  if (ID.SecurityType.IsOption() && SecurityType != SecurityType.Option)
450  {
451  throw new ArgumentException(Messages.Symbol.SecurityTypeCannotBeMapped(ID.SecurityType));
452  }
453 
454  if(ID.SecurityType == SecurityType.Future)
455  {
456  if (mappedSymbol == Value)
457  {
458  // futures with no real continuous mapping
459  return this;
460  }
461  var id = SecurityIdentifier.Parse(mappedSymbol);
462  var underlying = new Symbol(id, mappedSymbol);
463  underlying = underlying.AdjustSymbolByOffset(contractDepthOffset);
464 
465  // we map the underlying
466  return new Symbol(ID, underlying.Value, underlying);
467  }
468 
469  // Avoid updating the current instance's underlying Symbol.
470  var underlyingSymbol = Underlying;
471 
472  // Some universe Symbols, such as Constituent ETF universe Symbols and mapped custom data Symbols, have an
473  // underlying equity ETF Symbol as their underlying. When we're checking to see if a specific BaseData
474  // instance requires mapping, only the parent Symbol will be updated, which might not even need to be mapped
475  // (e.g. universe symbols with no equity ticker in symbol value).
476  // This will ensure that we map all of the underlying Symbol(s) that also require mapping updates.
477  if (HasUnderlying)
478  {
479  underlyingSymbol = Underlying.UpdateMappedSymbol(mappedSymbol, contractDepthOffset);
480  }
481 
482  // If this Symbol is not a custom data type, and the security type does not support mapping,
483  // then we know for a fact that this Symbol should not be mapped.
484  // Custom data types should be mapped, especially if this method is called on them because
485  // they can have an underlying that is also mapped.
486  if (SecurityType != SecurityType.Base && !this.RequiresMapping())
487  {
488  return new Symbol(ID, Value, underlyingSymbol);
489  }
490 
491  if (SecurityType == SecurityType.Option)
492  {
493  mappedSymbol = !IsCanonical()
495  : Value;
496  }
497 
498  return new Symbol(ID, mappedSymbol, underlyingSymbol);
499  }
500 
501  /// <summary>
502  /// Determines the SecurityType based on the underlying Symbol's SecurityType
503  /// </summary>
504  /// <param name="underlyingSymbol">Underlying Symbol of an option</param>
505  /// <returns>SecurityType of the option</returns>
506  /// <exception cref="ArgumentException">The provided underlying has no SecurityType able to represent it as an option</exception>
507  public static SecurityType GetOptionTypeFromUnderlying(Symbol underlyingSymbol)
508  {
509  return GetOptionTypeFromUnderlying(underlyingSymbol.SecurityType);
510  }
511 
512  /// <summary>
513  /// Determines the SecurityType based on the underlying Symbol's SecurityType <see cref="GetUnderlyingFromOptionType(SecurityType)"/>
514  /// </summary>
515  /// <param name="securityType">SecurityType of the underlying Symbol</param>
516  /// <returns>SecurityType of the option</returns>
517  /// <exception cref="ArgumentException">The provided underlying has no SecurityType able to represent it as an option</exception>
519  {
520  switch (securityType)
521  {
522  case SecurityType.Equity:
523  return SecurityType.Option;
524  case SecurityType.Future:
525  return SecurityType.FutureOption;
526  case SecurityType.Index:
527  return SecurityType.IndexOption;
528  default:
529  throw new ArgumentException(Messages.Symbol.NoOptionTypeForUnderlying(securityType));
530  }
531  }
532 
533  /// <summary>
534  /// Determines the underlying SecurityType based on the option Symbol's SecurityType <see cref="GetOptionTypeFromUnderlying(SecurityType)"/>
535  /// </summary>
536  /// <param name="securityType">SecurityType of the option Symbol</param>
537  /// <returns>SecurityType of the underlying</returns>
538  /// <exception cref="ArgumentException">The provided option has no SecurityType able to represent it as an underlying</exception>
540  {
541  switch (securityType)
542  {
543  case SecurityType.Option:
544  return SecurityType.Equity;
545  case SecurityType.FutureOption:
546  return SecurityType.Future;
547  case SecurityType.IndexOption:
548  return SecurityType.Index;
549  default:
550  throw new ArgumentException(Messages.Symbol.NoUnderlyingForOption(securityType));
551  }
552  }
553 
554  /// <summary>
555  /// Private constructor initializes a new instance of the <see cref="Symbol"/> class with underlying
556  /// </summary>
557  /// <param name="sid">The security identifier for this symbol</param>
558  /// <param name="value">The current ticker symbol value</param>
559  /// <param name="underlying">The underlying symbol</param>
560  internal Symbol(SecurityIdentifier sid, string value, Symbol underlying)
561  {
562  if (value == null)
563  {
564  throw new ArgumentNullException(nameof(value));
565  }
566  ID = sid;
567  Value = value.LazyToUpper();
568  Underlying = underlying;
569  }
570 
571  #endregion
572 
573  #region Overrides of Object
574 
575  /// <summary>
576  /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
577  /// </summary>
578  /// <returns>
579  /// true if the specified object is equal to the current object; otherwise, false.
580  /// </returns>
581  /// <param name="obj">The object to compare with the current object. </param><filterpriority>2</filterpriority>
582  public override bool Equals(object obj)
583  {
584  if (ReferenceEquals(null, obj)) return false;
585  if (ReferenceEquals(this, obj)) return true;
586 
587  // compare strings just as you would a symbol object
588  var sidString = obj as string;
589  if (sidString != null)
590  {
591  SecurityIdentifier sid;
592  if (SecurityIdentifier.TryParse(sidString, out sid))
593  {
594  return ID.Equals(sid);
595  }
596  }
597 
598  // compare a sid just as you would a symbol object
599  if (obj is SecurityIdentifier)
600  {
601  return ID.Equals((SecurityIdentifier) obj);
602  }
603 
604  if (obj.GetType() != GetType()) return false;
605  return Equals((Symbol)obj);
606  }
607 
608  /// <summary>
609  /// Serves as a hash function for a particular type.
610  /// </summary>
611  /// <returns>
612  /// A hash code for the current <see cref="T:System.Object"/>.
613  /// </returns>
614  /// <filterpriority>2</filterpriority>
615  public override int GetHashCode()
616  {
617  // only SID is used for comparisons
618  unchecked { return ID.GetHashCode(); }
619  }
620 
621  /// <summary>
622  /// 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.
623  /// </summary>
624  /// <returns>
625  /// 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.
626  /// </returns>
627  /// <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>
628  public int CompareTo(object obj)
629  {
630  var str = obj as string;
631  if (str != null)
632  {
633  return string.Compare(Value, str, StringComparison.OrdinalIgnoreCase);
634  }
635  var sym = obj as Symbol;
636  if (sym != null)
637  {
638  return string.Compare(Value, sym.Value, StringComparison.OrdinalIgnoreCase);
639  }
640 
641  throw new ArgumentException(Messages.Symbol.UnexpectedObjectTypeToCompareTo);
642  }
643 
644  /// <summary>
645  /// Returns a string that represents the current object.
646  /// </summary>
647  /// <returns>
648  /// A string that represents the current object.
649  /// </returns>
650  /// <filterpriority>2</filterpriority>
651  public override string ToString()
652  {
653  return SymbolCache.GetTicker(this);
654  }
655 
656  #endregion
657 
658  #region Equality members
659 
660  /// <summary>
661  /// Indicates whether the current object is equal to another object of the same type.
662  /// </summary>
663  /// <returns>
664  /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
665  /// </returns>
666  /// <param name="other">An object to compare with this object.</param>
667  public bool Equals(Symbol other)
668  {
669  if (ReferenceEquals(this, other)) return true;
670 
671  if (ReferenceEquals(other, null)
672  || ReferenceEquals(other, Empty))
673  {
674  // other is null or empty (equivalents)
675  // so we need to know how We compare with Empty
676  if (!_isEmpty.HasValue)
677  {
678  // for accuracy we compare IDs not references here
679  _isEmpty = ID.Equals(Empty.ID);
680  }
681  return _isEmpty.Value;
682  }
683 
684  // only SID is used for comparisons
685  return ID.Equals(other.ID);
686  }
687 
688  /// <summary>
689  /// Equals operator
690  /// </summary>
691  /// <param name="left">The left operand</param>
692  /// <param name="right">The right operand</param>
693  /// <returns>True if both symbols are equal, otherwise false</returns>
694  public static bool operator ==(Symbol left, Symbol right)
695  {
696  if (ReferenceEquals(left, right))
697  {
698  // this is a performance shortcut
699  return true;
700  }
701  if (ReferenceEquals(left, null) || left.Equals(Empty))
702  {
703  return ReferenceEquals(right, null) || right.Equals(Empty);
704  }
705  return left.Equals(right);
706  }
707 
708  /// <summary>
709  /// Not equals operator
710  /// </summary>
711  /// <param name="left">The left operand</param>
712  /// <param name="right">The right operand</param>
713  /// <returns>True if both symbols are not equal, otherwise false</returns>
714  public static bool operator !=(Symbol left, Symbol right)
715  {
716  return !(left == right);
717  }
718 
719  #endregion
720 
721  #region Implicit operators
722 
723  /// <summary>
724  /// Returns the symbol's string ticker
725  /// </summary>
726  /// <param name="symbol">The symbol</param>
727  /// <returns>The string ticker</returns>
728  [Obsolete("Symbol implicit operator to string is provided for algorithm use only.")]
729  public static implicit operator string(Symbol symbol)
730  {
731  return symbol.ToString();
732  }
733 
734  /// <summary>
735  /// Creates symbol using string as sid
736  /// </summary>
737  /// <param name="ticker">The string</param>
738  /// <returns>The symbol</returns>
739  [Obsolete("Symbol implicit operator from string is provided for algorithm use only.")]
740  public static implicit operator Symbol(string ticker)
741  {
742  Symbol symbol;
743  if (SymbolCache.TryGetSymbol(ticker, out symbol))
744  {
745  return symbol;
746  }
747 
748  SecurityIdentifier sid;
749  if (SecurityIdentifier.TryParse(ticker, out sid))
750  {
751  return new Symbol(sid, sid.Symbol);
752  }
753 
754  return new Symbol(new SecurityIdentifier(ticker, 0), ticker);
755  }
756 
757  #endregion
758 
759  #region String methods
760 
761  // in order to maintain better compile time backwards compatibility,
762  // we'll redirect a few common string methods to Value, but mark obsolete
763 #pragma warning disable 1591
764  [Obsolete("Symbol.Contains is a pass-through for Symbol.Value.Contains")]
765  public bool Contains(string value) { return Value.Contains(value); }
766  [Obsolete("Symbol.EndsWith is a pass-through for Symbol.Value.EndsWith")]
767  public bool EndsWith(string value) { return Value.EndsWithInvariant(value); }
768  [Obsolete("Symbol.StartsWith is a pass-through for Symbol.Value.StartsWith")]
769  public bool StartsWith(string value) { return Value.StartsWithInvariant(value); }
770  [Obsolete("Symbol.ToLower is a pass-through for Symbol.Value.ToLower")]
771  public string ToLower() { return Value.ToLowerInvariant(); }
772  [Obsolete("Symbol.ToUpper is a pass-through for Symbol.Value.ToUpper")]
773  public string ToUpper() { return Value.LazyToUpper(); }
774 #pragma warning restore 1591
775 
776  #endregion
777 
778  /// <summary>
779  /// Centralized helper method to resolve alias for a symbol
780  /// </summary>
781  public static string GetAlias(SecurityIdentifier securityIdentifier, Symbol underlying = null)
782  {
783  string sym;
784  switch (securityIdentifier.SecurityType)
785  {
786  case SecurityType.FutureOption:
787  case SecurityType.Option:
788  case SecurityType.IndexOption:
789  sym = underlying.Value;
790  if (securityIdentifier.Symbol != underlying.ID.Symbol)
791  {
792  // If we have changed the SID and it does not match the underlying,
793  // we've mapped a future into another Symbol. We want to have a value
794  // representing the mapped ticker, not of the underlying.
795  // e.g. we want:
796  // OG C3200...|GC18Z20
797  // NOT
798  // GC C3200...|GC18Z20
799  sym = securityIdentifier.Symbol;
800  }
801 
802  if (securityIdentifier.Date == SecurityIdentifier.DefaultDate)
803  {
804  return $"?{sym.LazyToUpper()}";
805  }
806 
807  if (sym.Length > 5) sym += " ";
808 
809  return SymbolRepresentation.GenerateOptionTickerOSI(sym, securityIdentifier.OptionRight, securityIdentifier.StrikePrice, securityIdentifier.Date);
810  case SecurityType.Future:
811  sym = securityIdentifier.Symbol;
812  if (securityIdentifier.Date == SecurityIdentifier.DefaultDate)
813  {
814  return $"/{sym}";
815  }
816  return SymbolRepresentation.GenerateFutureTicker(sym, securityIdentifier.Date);
817  default:
818  return null;
819  }
820  }
821  }
822 }