Lean  $LEAN_TAG$
ExchangeInfoUpdater.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 using System;
17 using System.IO;
18 using System.Linq;
21 
22 namespace QuantConnect.ToolBox
23 {
24  /// <summary>
25  /// Base tool for pulling data from a remote source and updating existing csv file.
26  /// </summary>
27  public class ExchangeInfoUpdater
28  {
29  private readonly IExchangeInfoDownloader _eidl;
30 
32  {
33  _eidl = eidl;
34  }
35 
36  /// <summary>
37  /// Update existing symbol properties database
38  /// </summary>
39  public void Run()
40  {
41  var directory = Path.Combine(Globals.DataFolder, "symbol-properties");
42  var file = Path.Combine(directory, "symbol-properties-database.csv");
43  var baseOutputDirectory = Config.Get("temp-output-directory", "/temp-output-directory");
44  var tempOutputDirectory = Directory.CreateDirectory(Path.Combine(baseOutputDirectory, "symbol-properties"));
45  var tmp = Path.Combine(tempOutputDirectory.FullName, "symbol-properties-database.csv");
46  if (File.Exists(tmp))
47  {
48  file = tmp;
49  }
50  else if (!File.Exists(file))
51  {
52  throw new FileNotFoundException("Unable to locate symbol properties file: " + file);
53  }
54 
55  // Read file data before to escape from clash if file == tmp
56  // Dispose off enumerator to free up resource
57  var fileLines = File.ReadLines(file).ToList();
58 
59  using (var writer = new StreamWriter(tmp))
60  {
61  var fetch = false;
62  var filter = $"{_eidl.Market},";
63  foreach (var line in fileLines)
64  {
65  if (!line.StartsWithInvariant(filter, true))
66  {
67  writer.WriteLine(line);
68  }
69  else if (!fetch)
70  {
71  WriteData(writer);
72  fetch = true;
73  }
74  }
75 
76  if (!fetch)
77  {
78  writer.WriteLine(Environment.NewLine);
79  WriteData(writer);
80  }
81  }
82  }
83 
84  private void WriteData(StreamWriter writer)
85  {
86  var existingSymbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder();
87  var entryPerSymbol = _eidl.Get().ToDictionary(newLine => {
88  var splitted = newLine.Split(',');
89  return new SecurityDatabaseKey(splitted[0], splitted[1], (SecurityType)Enum.Parse(typeof(SecurityType), splitted[2], true));
90  });
91 
92  foreach (var existingEntry in existingSymbolPropertiesDatabase.GetSymbolPropertiesList(_eidl.Market))
93  {
94  if (!entryPerSymbol.ContainsKey(existingEntry.Key))
95  {
96  // let's keep any existing which is no longer available, to take into account for delistings/removals
97  entryPerSymbol[existingEntry.Key] = $"{existingEntry.Key.Market},{existingEntry.Key.Symbol},{existingEntry.Key.SecurityType.ToLower()},{existingEntry.Value}";
98  }
99  }
100 
101  foreach (var upd in entryPerSymbol.OrderBy(x => x.Key.SecurityType).ThenBy(x => x.Key.Symbol))
102  {
103  writer.WriteLine(upd.Value);
104  }
105  }
106  }
107 }