Lean  $LEAN_TAG$
Program.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 NodaTime;
18 using QuantConnect.Util;
19 using QuantConnect.Data;
20 using QuantConnect.Logging;
26 using DataFeeds = QuantConnect.Lean.Engine.DataFeeds;
30 
32 public static class Program
33 {
34  /// <summary>
35  /// Synchronizer in charge of guaranteeing a single operation per file path
36  /// </summary>
37  private readonly static KeyStringSynchronizer DiskSynchronizer = new();
38 
39  /// <summary>
40  /// The provider used to cache history data files
41  /// </summary>
42  private static readonly IDataCacheProvider _dataCacheProvider = new DiskDataCacheProvider(DiskSynchronizer);
43 
44  /// <summary>
45  /// Represents the time interval of 5 seconds.
46  /// </summary>
47  private static TimeSpan _logDisplayInterval = TimeSpan.FromSeconds(5);
48 
49  /// <summary>
50  /// Provides access to exchange hours and raw data times zones in various markets
51  /// </summary>
52  private static readonly MarketHoursDatabase _marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
53 
54  /// <summary>
55  /// The main entry point for the application.
56  /// </summary>
57  /// <param name="args">Command-line arguments passed to the application.</param>
58  public static void Main(string[] args)
59  {
60  // Parse report arguments and merge with config to use in the optimizer
61  if (args.Length > 0)
62  {
64  }
65 
67 
68  var dataDownloader = Composer.Instance.GetExportedValueByTypeName<IDataDownloader>(Config.Get(DownloaderCommandArguments.CommandDownloaderDataDownloader));
69  var commandDataType = Config.Get(DownloaderCommandArguments.CommandDataType).ToUpperInvariant();
70 
71  switch (commandDataType)
72  {
73  case "UNIVERSE":
74  RunUniverseDownloader(dataDownloader, new DataUniverseDownloadConfig());
75  break;
76  case "TRADE":
77  case "QUOTE":
78  case "OPENINTEREST":
79  RunDownload(dataDownloader, new DataDownloadConfig(), Globals.DataFolder, _dataCacheProvider);
80  break;
81  default:
82  Log.Error($"QuantConnect.DownloaderDataProvider.Launcher: Unsupported command data type '{commandDataType}'. Valid options: UNIVERSE, TRADE, QUOTE, OPENINTEREST.");
83  break;
84  }
85  }
86 
87  /// <summary>
88  /// Executes a data download operation using the specified data downloader.
89  /// </summary>
90  /// <param name="dataDownloader">An instance of an object implementing the <see cref="IDataDownloader"/> interface, responsible for downloading data.</param>
91  /// <param name="dataDownloadConfig">Configuration settings for the data download operation.</param>
92  /// <param name="dataDirectory">The directory where the downloaded data will be stored.</param>
93  /// <param name="dataCacheProvider">The provider used to cache history data files</param>
94  /// <param name="mapSymbol">True if the symbol should be mapped while writing the data</param>
95  /// <exception cref="ArgumentNullException">Thrown when <paramref name="dataDownloader"/> is null.</exception>
96  public static void RunDownload(IDataDownloader dataDownloader, DataDownloadConfig dataDownloadConfig, string dataDirectory, IDataCacheProvider dataCacheProvider, bool mapSymbol = true)
97  {
98  if (dataDownloader == null)
99  {
100  throw new ArgumentNullException(nameof(dataDownloader), "The data downloader instance cannot be null. Please ensure that a valid instance of data downloader is provided.");
101  }
102 
103  var totalDownloadSymbols = dataDownloadConfig.Symbols.Count;
104  var completeSymbolCount = 0;
105  var startDownloadUtcTime = DateTime.UtcNow;
106 
107  foreach (var symbol in dataDownloadConfig.Symbols)
108  {
109  var downloadParameters = new DataDownloaderGetParameters(symbol, dataDownloadConfig.Resolution, dataDownloadConfig.StartDate, dataDownloadConfig.EndDate, dataDownloadConfig.TickType);
110 
111  Log.Trace($"DownloaderDataProvider.Main(): Starting download {downloadParameters}");
112  var downloadedData = dataDownloader.Get(downloadParameters);
113 
114  if (downloadedData == null)
115  {
116  completeSymbolCount++;
117  Log.Trace($"DownloaderDataProvider.Main(): No data available for the following parameters: {downloadParameters}");
118  continue;
119  }
120 
121  var (dataTimeZone, exchangeTimeZone) = GetDataAndExchangeTimeZoneBySymbol(symbol);
122 
123  var writer = new LeanDataWriter(dataDownloadConfig.Resolution, symbol, dataDirectory, dataDownloadConfig.TickType, dataCacheProvider, mapSymbol: mapSymbol);
124 
125  var groupedData = DataFeeds.DownloaderDataProvider.FilterAndGroupDownloadDataBySymbol(
126  downloadedData,
127  symbol,
128  dataDownloadConfig.DataType,
129  exchangeTimeZone,
130  dataTimeZone,
131  downloadParameters.StartUtc,
132  downloadParameters.EndUtc);
133 
134  var lastLogStatusTime = DateTime.UtcNow;
135 
136  foreach (var data in groupedData)
137  {
138  writer.Write(data.Select(data =>
139  {
140  var utcNow = DateTime.UtcNow;
141  if (utcNow - lastLogStatusTime >= _logDisplayInterval)
142  {
143  lastLogStatusTime = utcNow;
144  Log.Trace($"Downloading data for {downloadParameters.Symbol}. Please hold on...");
145  }
146  return data;
147  }));
148  }
149 
150  completeSymbolCount++;
151  var symbolPercentComplete = (double)completeSymbolCount / totalDownloadSymbols * 100;
152  Log.Trace($"DownloaderDataProvider.RunDownload(): {symbolPercentComplete:F2}% complete ({completeSymbolCount} out of {totalDownloadSymbols} symbols)");
153 
154  Log.Trace($"DownloaderDataProvider.RunDownload(): Download completed for {downloadParameters.Symbol} at {downloadParameters.Resolution} resolution, " +
155  $"covering the period from {dataDownloadConfig.StartDate} to {dataDownloadConfig.EndDate}.");
156  }
157  Log.Trace($"All downloads completed in {(DateTime.UtcNow - startDownloadUtcTime).TotalSeconds:F2} seconds.");
158  }
159 
160  /// <summary>
161  /// Initiates the universe downloader using the provided configuration.
162  /// </summary>
163  /// <param name="dataDownloader">The data downloader instance.</param>
164  /// <param name="dataUniverseDownloadConfig">The universe download configuration.</param>
165  private static void RunUniverseDownloader(IDataDownloader dataDownloader, DataUniverseDownloadConfig dataUniverseDownloadConfig)
166  {
167  foreach (var symbol in dataUniverseDownloadConfig.Symbols)
168  {
169  var universeDownloadParameters = new DataUniverseDownloaderGetParameters(symbol, dataUniverseDownloadConfig.StartDate, dataUniverseDownloadConfig.EndDate);
170  UniverseExtensions.RunUniverseDownloader(dataDownloader, universeDownloadParameters);
171  }
172  }
173 
174  /// <summary>
175  /// Retrieves the data time zone and exchange time zone associated with the specified symbol.
176  /// </summary>
177  /// <param name="symbol">The symbol for which to retrieve time zones.</param>
178  /// <returns>
179  /// A tuple containing the data time zone and exchange time zone.
180  /// The data time zone represents the time zone for data related to the symbol.
181  /// The exchange time zone represents the time zone for trading activities related to the symbol.
182  /// </returns>
183  private static (DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) GetDataAndExchangeTimeZoneBySymbol(Symbol symbol)
184  {
185  var entry = _marketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.SecurityType);
186  return (entry.DataTimeZone, entry.ExchangeHours.TimeZone);
187  }
188 
189  /// <summary>
190  /// Initializes various configurations for the application.
191  /// This method sets up logging, data providers, map file providers, and factor file providers.
192  /// </summary>
193  /// <remarks>
194  /// The method reads configuration values to determine whether debugging is enabled,
195  /// which log handler to use, and which data, map file, and factor file providers to initialize.
196  /// </remarks>
197  /// <seealso cref="Log"/>
198  /// <seealso cref="Config"/>
199  /// <seealso cref="Composer"/>
200  /// <seealso cref="ILogHandler"/>
201  /// <seealso cref="IDataProvider"/>
202  /// <seealso cref="IMapFileProvider"/>
203  /// <seealso cref="IFactorFileProvider"/>
204  public static void InitializeConfigurations()
205  {
206  Log.DebuggingEnabled = Config.GetBool("debug-mode", false);
207  Log.LogHandler = Composer.Instance.GetExportedValueByTypeName<ILogHandler>(Config.Get("log-handler", "CompositeLogHandler"));
208 
209  var dataProvider = Composer.Instance.GetExportedValueByTypeName<IDataProvider>("DefaultDataProvider");
210  var mapFileProvider = Composer.Instance.GetExportedValueByTypeName<IMapFileProvider>(Config.Get("map-file-provider", "LocalDiskMapFileProvider"));
211  var factorFileProvider = Composer.Instance.GetExportedValueByTypeName<IFactorFileProvider>(Config.Get("factor-file-provider", "LocalDiskFactorFileProvider"));
212 
213  var optionChainProvider = Composer.Instance.GetPart<IOptionChainProvider>();
214  if (optionChainProvider == null)
215  {
216  var historyManager = Composer.Instance.GetExportedValueByTypeName<HistoryProviderManager>(nameof(HistoryProviderManager));
217  historyManager.Initialize(new HistoryProviderInitializeParameters(null, null, dataProvider, _dataCacheProvider,
218  mapFileProvider, factorFileProvider, _ => { }, false, new DataPermissionManager(), null, new AlgorithmSettings()));
219  var baseOptionChainProvider = new LiveOptionChainProvider();
220  baseOptionChainProvider.Initialize(new(mapFileProvider, historyManager));
221  optionChainProvider = new CachingOptionChainProvider(baseOptionChainProvider);
222  Composer.Instance.AddPart(optionChainProvider);
223  }
224 
225  mapFileProvider.Initialize(dataProvider);
226  factorFileProvider.Initialize(mapFileProvider, dataProvider);
227  }
228 }