Lean  $LEAN_TAG$
SymbolCache.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 System.Collections.Concurrent;
19 using System.Linq;
20 
21 namespace QuantConnect
22 {
23  /// <summary>
24  /// Provides a string->Symbol mapping to allow for user defined strings to be lifted into a Symbol
25  /// This is mainly used via the Symbol implicit operator, but also functions that create securities
26  /// should also call Set to add new mappings
27  /// </summary>
28  public static class SymbolCache
29  {
30  // we aggregate the two maps into a class so we can assign a new one as an atomic operation
31  private static Cache _cache = new Cache();
32 
33  /// <summary>
34  /// Adds a mapping for the specified ticker
35  /// </summary>
36  /// <param name="ticker">The string ticker symbol</param>
37  /// <param name="symbol">The symbol object that maps to the string ticker symbol</param>
38  public static void Set(string ticker, Symbol symbol)
39  {
40  _cache.Symbols[ticker] = symbol;
41  _cache.Tickers[symbol] = ticker;
42  }
43 
44  /// <summary>
45  /// Gets the Symbol object that is mapped to the specified string ticker symbol
46  /// </summary>
47  /// <param name="ticker">The string ticker symbol</param>
48  /// <returns>The symbol object that maps to the specified string ticker symbol</returns>
49  public static Symbol GetSymbol(string ticker)
50  {
51  var result = TryGetSymbol(ticker);
52  if (result.Item3 != null)
53  {
54  throw result.Item3;
55  }
56 
57  return result.Item2;
58  }
59 
60  /// <summary>
61  /// Gets the Symbol object that is mapped to the specified string ticker symbol
62  /// </summary>
63  /// <param name="ticker">The string ticker symbol</param>
64  /// <param name="symbol">The output symbol object</param>
65  /// <returns>The symbol object that maps to the specified string ticker symbol</returns>
66  public static bool TryGetSymbol(string ticker, out Symbol symbol)
67  {
68  var result = TryGetSymbol(ticker);
69  // ignore errors
70  if (result.Item1)
71  {
72  symbol = result.Item2;
73  return true;
74  }
75 
76  symbol = null;
77  return result.Item1;
78  }
79 
80  /// <summary>
81  /// Gets the string ticker symbol that is mapped to the specified Symbol
82  /// </summary>
83  /// <param name="symbol">The symbol object</param>
84  /// <returns>The string ticker symbol that maps to the specified symbol object</returns>
85  public static string GetTicker(Symbol symbol)
86  {
87  string ticker;
88  return _cache.Tickers.TryGetValue(symbol, out ticker) ? ticker : symbol.ID.ToString();
89  }
90 
91  /// <summary>
92  /// Gets the string ticker symbol that is mapped to the specified Symbol
93  /// </summary>
94  /// <param name="symbol">The symbol object</param>
95  /// <param name="ticker">The output string ticker symbol</param>
96  /// <returns>The string ticker symbol that maps to the specified symbol object</returns>
97  public static bool TryGetTicker(Symbol symbol, out string ticker)
98  {
99  return _cache.Tickers.TryGetValue(symbol, out ticker);
100  }
101 
102  /// <summary>
103  /// Removes the mapping for the specified symbol from the cache
104  /// </summary>
105  /// <param name="symbol">The symbol whose mappings are to be removed</param>
106  /// <returns>True if the symbol mapping were removed from the cache</returns>
107  public static bool TryRemove(Symbol symbol)
108  {
109  string ticker;
110  return _cache.Tickers.TryRemove(symbol, out ticker) && _cache.Symbols.TryRemove(ticker, out symbol);
111  }
112 
113  /// <summary>
114  /// Removes the mapping for the specified symbol from the cache
115  /// </summary>
116  /// <param name="ticker">The ticker whose mappings are to be removed</param>
117  /// <returns>True if the symbol mapping were removed from the cache</returns>
118  public static bool TryRemove(string ticker)
119  {
120  Symbol symbol;
121  return _cache.Symbols.TryRemove(ticker, out symbol) && _cache.Tickers.TryRemove(symbol, out ticker);
122  }
123 
124  /// <summary>
125  /// Clears the current caches
126  /// </summary>
127  public static void Clear()
128  {
129  _cache = new Cache();
130  }
131 
132  private static Tuple<bool, Symbol, InvalidOperationException> TryGetSymbol(string ticker)
133  {
134  Symbol symbol;
135  InvalidOperationException error = null;
136  if (!_cache.TryGetSymbol(ticker, out symbol))
137  {
138  // fall-back full-text search as a back-shim for custom data symbols.
139  // permitting a user to use BTC to resolve to BTC.Bitcoin
140  var search = $"{ticker.ToUpperInvariant()}.";
141  var match = _cache.Symbols.Where(kvp => kvp.Key.StartsWith(search)).ToList();
142 
143  if (match.Count == 0)
144  {
145  // no matches
146  error = new InvalidOperationException(Messages.SymbolCache.UnableToLocateTicker(ticker));
147  }
148  else if (match.Count == 1)
149  {
150  // exactly one match
151  symbol = match.Single().Value;
152  }
153  else if (match.Count > 1)
154  {
155  // too many matches
156  error = new InvalidOperationException(
157  Messages.SymbolCache.MultipleMatchingTickersLocated(match.Select(kvp => kvp.Key)));
158  }
159  }
160 
161  return Tuple.Create(symbol != null, symbol, error);
162  }
163 
164  class Cache
165  {
166  public readonly ConcurrentDictionary<string, Symbol> Symbols = new ConcurrentDictionary<string, Symbol>(StringComparer.OrdinalIgnoreCase);
167  public readonly ConcurrentDictionary<Symbol, string> Tickers = new ConcurrentDictionary<Symbol, string>();
168 
169  /// <summary>
170  /// Attempts to resolve the ticker to a Symbol via the cache. If not found in the
171  /// cache then
172  /// </summary>
173  /// <param name="ticker">The ticker to resolver to a symbol</param>
174  /// <param name="symbol">The resolves symbol</param>
175  /// <returns>True if we successfully resolved a symbol, false otherwise</returns>
176  public bool TryGetSymbol(string ticker, out Symbol symbol)
177  {
178  if (Symbols.TryGetValue(ticker, out symbol))
179  {
180  return true;
181  }
182  SecurityIdentifier sid;
183  if (SecurityIdentifier.TryParse(ticker, out sid))
184  {
185  symbol = new Symbol(sid, sid.Symbol);
186  return true;
187  }
188  return false;
189  }
190  }
191  }
192 }