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