chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,182 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using QuantConnect.Util;
using System.Globalization;
using QuantConnect.Logging;
using QuantConnect.Brokerages;
using QuantConnect.Configuration;
using QuantConnect.DownloaderDataProvider.Launcher.Models.Constants;
namespace QuantConnect.DownloaderDataProvider.Launcher.Models;
/// <summary>
/// Abstract base class for configuring data download parameters, including common properties and initialization logic.
/// </summary>
public abstract class BaseDataDownloadConfig
{
/// <summary>
/// Gets the start date for the data download.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// Gets the end date for the data download.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the resolution of the downloaded data.
/// </summary>
public Resolution Resolution { get; protected set; }
/// <summary>
/// Gets or sets the market name for which the data will be downloaded.
/// </summary>
public string MarketName { get; protected set; }
/// <summary>
/// Gets the type of security for which the data is being downloaded.
/// </summary>
public SecurityType SecurityType { get; set; }
/// <summary>
/// Gets or sets the type of tick data to be downloaded.
/// </summary>
public TickType TickType { get; protected set; }
/// <summary>
/// The type of data based on <see cref="TickTypes"/>
/// </summary>
public abstract Type DataType { get; }
/// <summary>
/// Gets the list of symbols for which the data will be downloaded.
/// </summary>
public IReadOnlyCollection<Symbol> Symbols { get; protected set; } = [];
/// <summary>
/// Initializes a new instance of the <see cref="BaseDataDownloadConfig"/> class.
/// </summary>
protected BaseDataDownloadConfig()
{
StartDate = ParseDate(Config.Get(DownloaderCommandArguments.CommandStartDate).ToString());
EndDate = ParseDate(Config.Get(DownloaderCommandArguments.CommandEndDate).ToString());
SecurityType = ParseEnum<SecurityType>(Config.Get(DownloaderCommandArguments.CommandSecurityType).ToString());
MarketName = Config.Get(DownloaderCommandArguments.CommandMarketName).ToString().ToLower(CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(MarketName))
{
MarketName = DefaultBrokerageModel.DefaultMarketMap[SecurityType];
Log.Trace($"{nameof(BaseDataDownloadConfig)}: Default market '{MarketName}' applied for SecurityType '{SecurityType}'");
}
if (!Market.SupportedMarkets().Contains(MarketName))
{
throw new ArgumentException($"The specified market '{MarketName}' is not supported. Supported markets are: {string.Join(", ", Market.SupportedMarkets())}.");
}
Symbols = LoadSymbols(Config.GetValue<Dictionary<string, string>>(DownloaderCommandArguments.CommandTickers), SecurityType, MarketName);
}
/// <summary>
/// Initializes a new instance of the <see cref="DataDownloadConfig"/> class with the specified parameters.
/// </summary>
/// <param name="tickType">The type of tick data to be downloaded.</param>
/// <param name="securityType">The type of security for which data is being downloaded.</param>
/// <param name="resolution">The resolution of the data being downloaded.</param>
/// <param name="startDate">The start date for the data download range.</param>
/// <param name="endDate">The end date for the data download range.</param>
/// <param name="marketName">The name of the market from which the data is being downloaded.</param>
/// <param name="symbols">A list of symbols for which data is being downloaded.</param>
protected BaseDataDownloadConfig(TickType tickType, SecurityType securityType, Resolution resolution, DateTime startDate, DateTime endDate, string marketName, List<Symbol> symbols)
{
StartDate = startDate;
EndDate = endDate;
Resolution = resolution;
MarketName = marketName;
SecurityType = securityType;
TickType = tickType;
Symbols = symbols;
}
/// <summary>
/// Loads the symbols for which data will be downloaded.
/// </summary>
/// <param name="tickers">A dictionary of tickers to load symbols for.</param>
/// <param name="securityType">The type of security to download data for.</param>
/// <param name="market">The market for which the symbols are valid.</param>
/// <returns>A collection of symbols for the specified market and security type.</returns>
/// <summary>
private static IReadOnlyCollection<Symbol> LoadSymbols(Dictionary<string, string> tickers, SecurityType securityType, string market)
{
if (tickers == null || tickers.Count == 0)
{
throw new ArgumentException($"{nameof(BaseDataDownloadConfig)}.{nameof(LoadSymbols)}: The tickers dictionary cannot be null or empty.");
}
return tickers.Keys.ToList((ticker) => ParseTicker(ticker, securityType, market));
}
/// <summary>
/// Parse input 'ticker' to a Symbol or Canonical Symbol based on the provided security type and market.
/// </summary>
/// <param name="ticker">The ticker string input by the user.</param>
/// <param name="securityType">The security type.</param>
/// <param name="market">The market name.</param>
/// <returns>A <see cref="Symbol"/> representing the specified security.</returns>
private static Symbol ParseTicker(string ticker, SecurityType securityType, string market)
{
var symbol = default(Symbol);
try
{
symbol = SymbolRepresentation.ParseTickerFromUserInput(ticker, securityType, market);
}
catch (Exception ex)
{
Log.Debug($"{nameof(BaseDataDownloadConfig)}.{nameof(ParseTicker)}: Failed to parse symbol. Exception: {ex.Message}");
}
return symbol ?? Symbol.Create(ticker, securityType, market);
}
/// <summary>
/// Parses a string to a <see cref="DateTime"/> using a specific date format.
/// </summary>
/// <param name="date">The date string to parse.</param>
/// <returns>The parsed <see cref="DateTime"/> value.</returns>
protected static DateTime ParseDate(string date) => DateTime.ParseExact(date, DateFormat.EightCharacter, CultureInfo.InvariantCulture);
/// <summary>
/// Parses a string value into an enum of the specified type.
/// </summary>
/// <typeparam name="TEnum">The enum type to parse the value into.</typeparam>
/// <param name="value">The string value to parse.</param>
/// <returns>The parsed enum value.</returns>
/// <exception cref="ArgumentException">Thrown if the value cannot be parsed or is not a valid enum value.</exception>
protected static TEnum ParseEnum<TEnum>(string value) where TEnum : struct, Enum
{
if (!Enum.TryParse(value, true, out TEnum result) || !Enum.IsDefined(result))
{
throw new ArgumentException($"Invalid {typeof(TEnum).Name} specified: '{value}'. Please provide a valid {typeof(TEnum).Name}. " +
$"Valid values are: {string.Join(", ", Enum.GetNames<TEnum>())}.");
}
return result;
}
}
@@ -0,0 +1,149 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using QuantConnect.Util;
using QuantConnect.Data;
using QuantConnect.Packets;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Configuration;
namespace QuantConnect.DownloaderDataProvider.Launcher.Models
{
/// <summary>
/// Class for downloading data from a brokerage.
/// </summary>
public class BrokerageDataDownloader : IDataDownloader, IDisposable
{
/// <summary>
/// Represents the Brokerage implementation.
/// </summary>
private IBrokerage _brokerage;
/// <summary>
/// Provides access to exchange hours and raw data times zones in various markets
/// </summary>
private readonly MarketHoursDatabase _marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
/// <summary>
/// Initializes a new instance of the <see cref="BrokerageDataDownloader"/> class.
/// </summary>
public BrokerageDataDownloader()
{
var liveNodeConfiguration = new LiveNodePacket()
{
Brokerage = Config.Get("data-downloader-brokerage"),
UserToken = Globals.UserToken,
UserId = Globals.UserId,
ProjectId = Globals.ProjectId,
OrganizationId = Globals.OrganizationID,
Version = Globals.Version,
DeploymentTarget = DeploymentTarget.LocalPlatform
};
try
{
// import the brokerage data for the configured brokerage
var brokerageFactory = Composer.Instance.Single<IBrokerageFactory>(factory => factory.BrokerageType.MatchesTypeName(liveNodeConfiguration.Brokerage));
liveNodeConfiguration.BrokerageData = brokerageFactory.BrokerageData;
}
catch (InvalidOperationException error)
{
throw new InvalidOperationException($"{nameof(BrokerageDataDownloader)}.An error occurred while resolving brokerage data for a live job. Brokerage: {liveNodeConfiguration.Brokerage}.", error);
}
_brokerage = Composer.Instance.GetExportedValueByTypeName<IBrokerage>(liveNodeConfiguration.Brokerage);
_brokerage.Message += (object _, Brokerages.BrokerageMessageEvent e) =>
{
if (e.Type == Brokerages.BrokerageMessageType.Error)
{
Logging.Log.Error(e.Message);
}
else
{
Logging.Log.Trace(e.Message);
}
};
((IDataQueueHandler)_brokerage).SetJob(liveNodeConfiguration);
}
/// <summary>
/// Get historical data enumerable for a single symbol, type and resolution given this start and end time (in UTC).
/// </summary>
/// <param name="dataDownloaderGetParameters">model class for passing in parameters for historical data</param>
/// <returns>Enumerable of base data for this symbol</returns>
public IEnumerable<BaseData>? Get(DataDownloaderGetParameters dataDownloaderGetParameters)
{
var symbol = dataDownloaderGetParameters.Symbol;
var resolution = dataDownloaderGetParameters.Resolution;
var startUtc = dataDownloaderGetParameters.StartUtc;
var endUtc = dataDownloaderGetParameters.EndUtc;
var tickType = dataDownloaderGetParameters.TickType;
var dataType = LeanData.GetDataType(resolution, tickType);
var exchangeHours = _marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
var dataTimeZone = _marketHoursDatabase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType);
var symbols = new List<Symbol> { symbol };
if (symbol.IsCanonical())
{
symbols = GetChainSymbols(symbol, true).ToList();
}
return symbols
.Select(symbol =>
{
var request = new Data.HistoryRequest(startUtc, endUtc, dataType, symbol, resolution, exchangeHours: exchangeHours, dataTimeZone: dataTimeZone, resolution,
// let's not ask for extended market hours for hour and daily resolutions to match lean
includeExtendedMarketHours: resolution != Resolution.Hour && resolution != Resolution.Daily, false, DataNormalizationMode.Raw, tickType);
var history = _brokerage.GetHistory(request);
if (history == null)
{
Logging.Log.Trace($"{nameof(BrokerageDataDownloader)}.{nameof(Get)}: Ignoring history request for unsupported symbol {symbol}");
}
return history;
})
.Where(history => history != null)
.SelectMany(history => history);
}
/// <summary>
/// Returns an IEnumerable of Future/Option contract symbols for the given root ticker
/// </summary>
/// <param name="symbol">The Symbol to get futures/options chain for</param>
/// <param name="includeExpired">Include expired contracts</param>
private IEnumerable<Symbol> GetChainSymbols(Symbol symbol, bool includeExpired)
{
if (_brokerage is IDataQueueUniverseProvider universeProvider)
{
return universeProvider.LookupSymbols(symbol, includeExpired);
}
else
{
throw new InvalidOperationException($"{nameof(BrokerageDataDownloader)}.{nameof(GetChainSymbols)}: The current brokerage does not support fetching canonical symbols. Please ensure your brokerage instance supports this feature.");
}
}
public void Dispose()
{
_brokerage.DisposeSafely();
}
}
}
@@ -0,0 +1,37 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.DownloaderDataProvider.Launcher.Models.Constants
{
public sealed class DownloaderCommandArguments
{
public const string CommandDownloaderDataDownloader = "data-downloader";
public const string CommandDataType = "data-type";
public const string CommandTickers = "tickers";
public const string CommandSecurityType = "security-type";
public const string CommandMarketName = "market";
public const string CommandResolution = "resolution";
public const string CommandStartDate = "start-date";
public const string CommandEndDate = "end-date";
}
}
@@ -0,0 +1,55 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using QuantConnect.Util;
using QuantConnect.Configuration;
using QuantConnect.DownloaderDataProvider.Launcher.Models.Constants;
namespace QuantConnect.DownloaderDataProvider.Launcher.Models;
/// <summary>
/// Represents the configuration for downloading data.
/// </summary>
public sealed class DataDownloadConfig : BaseDataDownloadConfig
{
/// <summary>
/// Gets the type of data download.
/// </summary>
public override Type DataType { get => LeanData.GetDataType(Resolution, TickType); }
/// <summary>
/// Initializes a new instance of the <see cref="DataDownloadConfig"/> class.
/// </summary>s
public DataDownloadConfig()
{
TickType = ParseEnum<TickType>(Config.Get(DownloaderCommandArguments.CommandDataType));
Resolution = ParseEnum<Resolution>(Config.Get(DownloaderCommandArguments.CommandResolution));
}
/// <summary>
/// Initializes a new instance of the <see cref="DataDownloadConfig"/> class with the specified parameters.
/// </summary>
/// <param name="tickType">The type of tick data to be downloaded.</param>
/// <param name="securityType">The type of security for which data is being downloaded.</param>
/// <param name="resolution">The resolution of the data being downloaded.</param>
/// <param name="startDate">The start date for the data download range.</param>
/// <param name="endDate">The end date for the data download range.</param>
/// <param name="marketName">The name of the market from which the data is being downloaded.</param>
/// <param name="symbols">A list of symbols for which data is being downloaded.</param>
public DataDownloadConfig(TickType tickType, SecurityType securityType, Resolution resolution, DateTime startDate, DateTime endDate, string marketName, List<Symbol> symbols)
: base(tickType, securityType, resolution, startDate, endDate, marketName, symbols)
{ }
}
@@ -0,0 +1,60 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using QuantConnect.Data.UniverseSelection;
namespace QuantConnect.DownloaderDataProvider.Launcher.Models;
/// <summary>
/// Represents the configuration for downloading data for a universe of securities.
/// </summary>
public sealed class DataUniverseDownloadConfig : BaseDataDownloadConfig
{
/// <summary>
/// Gets the type of data universe download.
/// </summary>
public override Type DataType { get; }
/// <summary>
/// Initializes a new instance of the <see cref="DataUniverseDownloadConfig"/> class using configuration settings.
/// </summary>
/// <exception cref="ArgumentException">Thrown when an unsupported security type is specified.</exception>
public DataUniverseDownloadConfig()
{
Resolution = Resolution.Daily;
DataType = GetDataUniverseType(SecurityType);
}
/// <summary>
/// Retrieves the corresponding data universe type based on the specified security type.
/// </summary>
/// <param name="securityType">The security type for which the data universe type is determined.</param>
/// <returns>The corresponding <see cref="Type"/> of the data universe.</returns>
/// <exception cref="NotImplementedException">
/// Thrown when the specified <paramref name="securityType"/> is not supported.
/// </exception>
private static Type GetDataUniverseType(SecurityType securityType)
{
switch (securityType)
{
case SecurityType.Option:
case SecurityType.IndexOption:
return typeof(OptionUniverse);
default:
throw new NotImplementedException($"DataUniverseDownloadConfig.GetDataUniverseType(): The data universe type for SecurityType '{securityType}' is not implemented.");
}
}
}