Lean  $LEAN_TAG$
SecurityDefinitionSymbolResolver.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.IO;
19 using System.Linq;
20 using QuantConnect.Util;
21 using QuantConnect.Logging;
24 using System.Collections.Generic;
26 
28 {
29  /// <summary>
30  /// Resolves standardized security definitions such as FIGI, CUSIP, ISIN, SEDOL into
31  /// a properly mapped Lean <see cref="Symbol"/>, and vice-versa.
32  /// </summary>
34  {
35  private static SecurityDefinitionSymbolResolver _securityDefinitionSymbolResolver;
36  private static readonly object _lock = new object();
37 
38  private List<SecurityDefinition> _securityDefinitions;
39  private readonly IMapFileProvider _mapFileProvider;
40  private readonly string _securitiesDefinitionKey;
41  private readonly IDataProvider _dataProvider;
42 
43  /// <summary>
44  /// Creates an instance of the symbol resolver
45  /// </summary>
46  /// <param name="dataProvider">Data provider used to obtain symbol mappings data</param>
47  /// <param name="securitiesDefinitionKey">Location to read the securities definition data from</param>
48  private SecurityDefinitionSymbolResolver(IDataProvider dataProvider = null, string securitiesDefinitionKey = null)
49  {
50  _securitiesDefinitionKey = securitiesDefinitionKey ?? Path.Combine(Globals.GetDataFolderPath("symbol-properties"), "security-database.csv");
51 
52  _dataProvider = dataProvider ?? Composer.Instance.GetPart<IDataProvider>();
53 
54  _mapFileProvider = Composer.Instance.GetPart<IMapFileProvider>();
55  _mapFileProvider.Initialize(_dataProvider);
56  }
57 
58  /// <summary>
59  /// Converts CUSIP into a Lean <see cref="Symbol"/>
60  /// </summary>
61  /// <param name="cusip">
62  /// The Committee on Uniform Securities Identification Procedures (CUSIP) number of a security
63  /// </param>
64  /// <param name="tradingDate">
65  /// The date that the stock was trading at with the CUSIP provided. This is used
66  /// to get the ticker of the symbol on this date.
67  /// </param>
68  /// <returns>The Lean Symbol corresponding to the CUSIP number on the trading date provided</returns>
69  public Symbol CUSIP(string cusip, DateTime tradingDate)
70  {
71  if (string.IsNullOrWhiteSpace(cusip))
72  {
73  return null;
74  }
75 
76  return SecurityDefinitionToSymbol(
77  GetSecurityDefinitions().FirstOrDefault(x => x.CUSIP != null && x.CUSIP.Equals(cusip, StringComparison.InvariantCultureIgnoreCase)),
78  tradingDate);
79  }
80 
81  /// <summary>
82  /// Converts a Lean <see cref="Symbol"/> to its CUSIP number
83  /// </summary>
84  /// <param name="symbol">The Lean <see cref="Symbol"/></param>
85  /// <returns>The Committee on Uniform Securities Identification Procedures (CUSIP) number corresponding to the given Lean <see cref="Symbol"/></returns>
86  public string CUSIP(Symbol symbol)
87  {
88  return SymbolToSecurityDefinition(symbol)?.CUSIP;
89  }
90 
91  /// <summary>
92  /// Converts an asset's composite FIGI into a Lean <see cref="Symbol"/>
93  /// </summary>
94  /// <param name="compositeFigi">
95  /// The composite Financial Instrument Global Identifier (FIGI) of a security
96  /// </param>
97  /// <param name="tradingDate">
98  /// The date that the stock was trading at with the composite FIGI provided. This is used
99  /// to get the ticker of the symbol on this date.
100  /// </param>
101  /// <returns>The Lean Symbol corresponding to the composite FIGI on the trading date provided</returns>
102  public Symbol CompositeFIGI(string compositeFigi, DateTime tradingDate)
103  {
104  if (string.IsNullOrWhiteSpace(compositeFigi))
105  {
106  return null;
107  }
108 
109  return SecurityDefinitionToSymbol(
110  GetSecurityDefinitions().FirstOrDefault(x => x.CompositeFIGI != null && x.CompositeFIGI.Equals(compositeFigi, StringComparison.InvariantCultureIgnoreCase)),
111  tradingDate);
112  }
113 
114  /// <summary>
115  /// Converts a Lean <see cref="Symbol"/> to its composite FIGI representation
116  /// </summary>
117  /// <param name="symbol">The Lean <see cref="Symbol"/></param>
118  /// <returns>The composite Financial Instrument Global Identifier (FIGI) corresponding to the given Lean <see cref="Symbol"/></returns>
119  public string CompositeFIGI(Symbol symbol)
120  {
121  return SymbolToSecurityDefinition(symbol)?.CompositeFIGI;
122  }
123 
124  /// <summary>
125  /// Converts SEDOL into a Lean <see cref="Symbol"/>
126  /// </summary>
127  /// <param name="sedol">
128  /// The Stock Exchange Daily Official List (SEDOL) security identifier of a security
129  /// </param>
130  /// <param name="tradingDate">
131  /// The date that the stock was trading at with the SEDOL provided. This is used
132  /// to get the ticker of the symbol on this date.
133  /// </param>
134  /// <returns>The Lean Symbol corresponding to the SEDOL on the trading date provided</returns>
135  public Symbol SEDOL(string sedol, DateTime tradingDate)
136  {
137  if (string.IsNullOrWhiteSpace(sedol))
138  {
139  return null;
140  }
141 
142  return SecurityDefinitionToSymbol(
143  GetSecurityDefinitions().FirstOrDefault(x => x.SEDOL != null && x.SEDOL.Equals(sedol, StringComparison.InvariantCultureIgnoreCase)),
144  tradingDate);
145  }
146 
147  /// <summary>
148  /// Converts a Lean <see cref="Symbol"/> to its SEDOL representation
149  /// </summary>
150  /// <param name="symbol">The Lean <see cref="Symbol"/></param>
151  /// <returns>The Stock Exchange Daily Official List (SEDOL) security identifier corresponding to the given Lean <see cref="Symbol"/></returns>
152  public string SEDOL(Symbol symbol)
153  {
154  return SymbolToSecurityDefinition(symbol)?.SEDOL;
155  }
156 
157  /// <summary>
158  /// Converts ISIN into a Lean <see cref="Symbol"/>
159  /// </summary>
160  /// <param name="isin">
161  /// The International Securities Identification Number (ISIN) of a security
162  /// </param>
163  /// <param name="tradingDate">
164  /// The date that the stock was trading at with the ISIN provided. This is used
165  /// to get the ticker of the symbol on this date.
166  /// </param>
167  /// <returns>The Lean Symbol corresponding to the ISIN on the trading date provided</returns>
168  public Symbol ISIN(string isin, DateTime tradingDate)
169  {
170  if (string.IsNullOrWhiteSpace(isin))
171  {
172  return null;
173  }
174 
175  return SecurityDefinitionToSymbol(
176  GetSecurityDefinitions().FirstOrDefault(x => x.ISIN != null && x.ISIN.Equals(isin, StringComparison.InvariantCultureIgnoreCase)),
177  tradingDate);
178  }
179 
180  /// <summary>
181  /// Converts a Lean <see cref="Symbol"/> to its ISIN representation
182  /// </summary>
183  /// <param name="symbol">The Lean <see cref="Symbol"/></param>
184  /// <returns>The International Securities Identification Number (ISIN) corresponding to the given Lean <see cref="Symbol"/></returns>
185  public string ISIN(Symbol symbol)
186  {
187  return SymbolToSecurityDefinition(symbol)?.ISIN;
188  }
189 
190  /// <summary>
191  /// Get's the CIK value associated with the given <see cref="Symbol"/>
192  /// </summary>
193  /// <param name="symbol">The Lean <see cref="Symbol"/></param>
194  /// <returns>The Central Index Key number (CIK) corresponding to the given Lean <see cref="Symbol"/> if any, else null</returns>
195  public int? CIK(Symbol symbol)
196  {
197  return SymbolToSecurityDefinition(symbol)?.CIK;
198  }
199 
200  /// <summary>
201  /// Converts CIK into a Lean <see cref="Symbol"/> array
202  /// </summary>
203  /// <param name="cik">
204  /// The Central Index Key (CIK) of a company
205  /// </param>
206  /// <param name="tradingDate">
207  /// The date that the stock was trading at with the CIK provided. This is used
208  /// to get the ticker of the symbol on this date.
209  /// </param>
210  /// <returns>The Lean Symbols corresponding to the CIK on the trading date provided</returns>
211  public Symbol[] CIK(int cik, DateTime tradingDate)
212  {
213  if (cik == 0)
214  {
215  return Array.Empty<Symbol>();
216  }
217 
218  return GetSecurityDefinitions()
219  .Where(x => x.CIK != null && x.CIK == cik)
220  .Select(securityDefinition => SecurityDefinitionToSymbol(securityDefinition, tradingDate))
221  .Where(x => x != null)
222  .ToArray();
223  }
224 
225  /// <summary>
226  /// Converts a SecurityDefinition to a <see cref="Symbol" />
227  /// </summary>
228  /// <param name="securityDefinition">Security definition</param>
229  /// <param name="tradingDate">
230  /// The date that the stock was being traded. This is used to resolve
231  /// the ticker that the stock was trading under on this date.
232  /// </param>
233  /// <returns>Symbol if matching Lean Symbol was found on the trading date, null otherwise</returns>
234  private Symbol SecurityDefinitionToSymbol(SecurityDefinition securityDefinition, DateTime tradingDate)
235  {
236  if (securityDefinition == null)
237  {
238  return null;
239  }
240 
241  var mapFileResolver = _mapFileProvider.Get(AuxiliaryDataKey.Create(securityDefinition.SecurityIdentifier));
242 
243  // Get the first ticker the symbol traded under, and then lookup the
244  // trading date to get the ticker on the trading date.
245  var mapFile = mapFileResolver
246  .ResolveMapFile(securityDefinition.SecurityIdentifier.Symbol, securityDefinition.SecurityIdentifier.Date);
247 
248  // The mapped ticker will be null if the map file is null or there's
249  // no entry found for the given trading date.
250  var mappedTicker = mapFile?.GetMappedSymbol(tradingDate, null);
251 
252  // If we're null, then try again; get the last entry of the map file and use
253  // it as the Symbol we return to the caller.
254  mappedTicker ??= mapFile?
255  .LastOrDefault()?
256  .MappedSymbol;
257 
258  return string.IsNullOrWhiteSpace(mappedTicker)
259  ? null
260  : new Symbol(securityDefinition.SecurityIdentifier, mappedTicker);
261  }
262 
263  /// <summary>
264  /// Gets the SecurityDefinition corresponding to the given Lean <see cref="Symbol"/>
265  /// </summary>
266  private SecurityDefinition SymbolToSecurityDefinition(Symbol symbol)
267  {
268  if (symbol == null)
269  {
270  return null;
271  }
272 
273  return GetSecurityDefinitions().FirstOrDefault(x => x.SecurityIdentifier.Equals(symbol.ID));
274  }
275 
276  /// <summary>
277  /// Get's the security definitions using a lazy initialization
278  /// </summary>
279  private IEnumerable<SecurityDefinition> GetSecurityDefinitions()
280  {
281  lock (_lock)
282  {
283  if (_securityDefinitions == null && !SecurityDefinition.TryRead(_dataProvider, _securitiesDefinitionKey, out _securityDefinitions))
284  {
285  _securityDefinitions = new List<SecurityDefinition>();
286  Log.Error($"SecurityDefinitionSymbolResolver(): No security definitions data loaded from file: {_securitiesDefinitionKey}");
287  }
288  }
289 
290  return _securityDefinitions;
291  }
292 
293  /// <summary>
294  /// Gets the single instance of the symbol resolver
295  /// </summary>
296  /// <param name="dataProvider">Data provider used to obtain symbol mappings data</param>
297  /// <param name="securitiesDefinitionKey">Location to read the securities definition data from</param>
298  /// <returns>The single instance of the symbol resolver</returns>
299  public static SecurityDefinitionSymbolResolver GetInstance(IDataProvider dataProvider = null, string securitiesDefinitionKey = null)
300  {
301  lock (_lock)
302  {
303  if (_securityDefinitionSymbolResolver == null)
304  {
305  _securityDefinitionSymbolResolver = new SecurityDefinitionSymbolResolver(dataProvider, securitiesDefinitionKey);
306  }
307  }
308 
309  return _securityDefinitionSymbolResolver;
310  }
311 
312  /// <summary>
313  /// Resets the security definition symbol resolver, forcing a reload when reused.
314  /// Called in tests where multiple algorithms are run sequentially,
315  /// and we need to guarantee that every test starts with the same environment.
316  /// </summary>
317  public static void Reset()
318  {
319  lock (_lock)
320  {
321  _securityDefinitionSymbolResolver = null;
322  }
323  }
324  }
325 }