From fcb36f7ee0ed64b79f753ecb39d1456d21f63bc8 Mon Sep 17 00:00:00 2001 From: Jan Korf Date: Mon, 20 Jul 2026 13:40:58 +0200 Subject: [PATCH] Shared asset and symbol types Added SpotSymbolCatalog to Shared ISpotSymbolRestClient interface Added FuturesSymbolCatalog to Shared IFuturesSymbolRestClient interface Added BaseAssetType, BaseAssetSubType, QuoteAssetType and QuoteAssetSubType to GetSymbolsRequest model Added DisplayName to SharedSpotSymbol and SharedFuturesSymbol models Added BaseAssetType, BaseAssetSubType, QuoteAssetType and QuoteAssetSubType to SharedSpotSymbol and SharedFuturesSymbol models Added IsStableCoin, IsCommodity and IsEquity helper methods to LibraryHelpers --- CryptoExchange.Net/ExchangeSymbolCache.cs | 81 ++++++++++-- CryptoExchange.Net/LibraryHelpers.cs | 118 ++++++++++++++++++ .../SharedApis/Enums/SharedAssetType.cs | 51 ++++++++ .../Rest/Futures/IFuturesSymbolRestClient.cs | 5 + .../Rest/Spot/ISpotSymbolRestClient.cs | 5 + .../Endpoints/GetFuturesSymbolsOptions.cs | 41 +++++- .../Endpoints/GetSpotSymbolsOptions.cs | 42 ++++++- .../Models/Rest/GetSymbolsRequest.cs | 33 ++++- .../SharedApis/Models/SharedSymbolCatalog.cs | 58 +++++++++ .../ResponseModels/SharedFuturesSymbol.cs | 2 +- .../ResponseModels/SharedSpotSymbol.cs | 28 ++++- CryptoExchange.Net/SharedApis/SharedUtils.cs | 17 +++ 12 files changed, 465 insertions(+), 16 deletions(-) create mode 100644 CryptoExchange.Net/SharedApis/Enums/SharedAssetType.cs create mode 100644 CryptoExchange.Net/SharedApis/Models/SharedSymbolCatalog.cs diff --git a/CryptoExchange.Net/ExchangeSymbolCache.cs b/CryptoExchange.Net/ExchangeSymbolCache.cs index e3b0fc8b..59c75251 100644 --- a/CryptoExchange.Net/ExchangeSymbolCache.cs +++ b/CryptoExchange.Net/ExchangeSymbolCache.cs @@ -33,7 +33,7 @@ namespace CryptoExchange.Net if (keyedCache != null && DateTime.UtcNow - keyedCache.UpdateTime < TimeSpan.FromMinutes(60)) return; - exchangeInfo.Set(key, new ExchangeInfo(DateTime.UtcNow, updateData.ToDictionary(x => x.Name, x => x.SharedSymbol))); + exchangeInfo.Set(key, new ExchangeInfo(DateTime.UtcNow, updateData.ToDictionary(x => x.Name, x => x))); } /// @@ -118,6 +118,22 @@ namespace CryptoExchange.Net return exchangeInfo.ParseSymbol(key, symbolName); } + /// + /// Get a symbol catalog for a specific exchange(topic) and environment. Only available if has been called previously. + /// + /// Exchange name + /// Id for the provided data + /// Trade environment + /// Additional data set identification key + public static SharedSymbolCatalog? GetSymbolCatalog(string exchange, string topicId, string environmentName, string? key) + { + var id = topicId + environmentName; + if (!_symbolInfos.TryGetValue(id, out var exchangeInfo)) + return null; + + return exchangeInfo.GetSymbolCatalog(exchange, key); + } + class ExchangeKeyedCache { private ExchangeInfo? _noKeyCache; @@ -163,7 +179,7 @@ namespace CryptoExchange.Net public SharedSymbol? ParseSymbol(string? key, string symbolName) { - SharedSymbol? symbolInfo = null; + SharedSpotSymbol? symbolInfo = null; if (key == null) { if (_noKeyCache != null) @@ -173,7 +189,7 @@ namespace CryptoExchange.Net return new SharedSymbol(symbolInfo.TradingMode, symbolInfo.BaseAsset, symbolInfo.QuoteAsset, symbolName) { - DeliverTime = symbolInfo.DeliverTime + DeliverTime = (symbolInfo as SharedFuturesSymbol)?.DeliveryTime }; } @@ -183,7 +199,7 @@ namespace CryptoExchange.Net { return new SharedSymbol(symbolInfo.TradingMode, symbolInfo.BaseAsset, symbolInfo.QuoteAsset, symbolName) { - DeliverTime = symbolInfo.DeliverTime + DeliverTime = (symbolInfo as SharedFuturesSymbol)?.DeliveryTime }; } } @@ -199,7 +215,7 @@ namespace CryptoExchange.Net { return new SharedSymbol(symbolInfo.TradingMode, symbolInfo.BaseAsset, symbolInfo.QuoteAsset, symbolName) { - DeliverTime = symbolInfo.DeliverTime + DeliverTime = (symbolInfo as SharedFuturesSymbol)?.DeliveryTime }; } @@ -265,7 +281,7 @@ namespace CryptoExchange.Net { return _noKeyCache.Symbols .Where(x => x.Value.BaseAsset.Equals(baseAsset, StringComparison.InvariantCultureIgnoreCase)) - .Select(x => x.Value) + .Select(x => x.Value.SharedSymbol) .ToArray(); } @@ -274,7 +290,7 @@ namespace CryptoExchange.Net { result.AddRange(cache.Symbols .Where(x => x.Value.BaseAsset.Equals(baseAsset, StringComparison.InvariantCultureIgnoreCase)) - .Select(x => x.Value)); + .Select(x => x.Value.SharedSymbol)); } return result.ToArray(); @@ -286,18 +302,63 @@ namespace CryptoExchange.Net return exchangeInfo.Symbols .Where(x => x.Value.BaseAsset.Equals(baseAsset, StringComparison.InvariantCultureIgnoreCase)) - .Select(x => x.Value) + .Select(x => x.Value.SharedSymbol) .ToArray(); } + + internal SharedSymbolCatalog? GetSymbolCatalog(string exchange, string? key) + { + IEnumerable cachedSymbols; + if (key == null) + { + if (_noKeyCache != null) + cachedSymbols = _noKeyCache.Symbols.Values; + else + cachedSymbols = _keyedCache.Values.SelectMany(x => x.Symbols.Values); + } + else + { + if (!_keyedCache.TryGetValue(key, out var exchangeInfo) || exchangeInfo == null) + return null; + + cachedSymbols = exchangeInfo.Symbols.Values; + } + + var assets = new Dictionary(); + var symbols = new Dictionary(); + foreach (var symbol in cachedSymbols) + { + if (!assets.TryGetValue(symbol.BaseAsset, out var baseAssetInfo)) + { + baseAssetInfo = new SharedAssetInfo(symbol.BaseAsset, symbol.BaseAssetType, symbol.BaseAssetSubType); + assets.Add(symbol.BaseAsset, baseAssetInfo); + } + + if (!assets.TryGetValue(symbol.QuoteAsset, out var quoteAssetInfo)) + { + quoteAssetInfo = new SharedAssetInfo(symbol.QuoteAsset, symbol.QuoteAssetType, symbol.QuoteAssetSubType); + assets.Add(symbol.QuoteAsset, quoteAssetInfo); + } + + symbols.Add(symbol.Name, symbol); + } + + return new SharedSymbolCatalog + { + Exchange = exchange, + Assets = assets, + Symbols = symbols + }; + } } class ExchangeInfo { public DateTime UpdateTime { get; set; } - public Dictionary Symbols { get; set; } + public Dictionary Symbols { get; set; } - public ExchangeInfo(DateTime updateTime, Dictionary symbols) + public ExchangeInfo(DateTime updateTime, Dictionary symbols) { UpdateTime = updateTime; Symbols = symbols; diff --git a/CryptoExchange.Net/LibraryHelpers.cs b/CryptoExchange.Net/LibraryHelpers.cs index 63e6e8e8..4c0f66f4 100644 --- a/CryptoExchange.Net/LibraryHelpers.cs +++ b/CryptoExchange.Net/LibraryHelpers.cs @@ -4,6 +4,8 @@ using CryptoExchange.Net.Objects.Options; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.IO.Pipelines; +using System.Linq; using System.Net; using System.Net.Http; @@ -14,6 +16,61 @@ namespace CryptoExchange.Net /// public static class LibraryHelpers { + private static readonly HashSet _stableCoins = new HashSet(StringComparer.OrdinalIgnoreCase) + { + // USD + "USDT", "USDC", "DAI", "FDUSD", "USDE", "TUSD", "USDP", "PYUSD", "GUSD", + "USDD", "LUSD", "USDJ", "SUSD", "ZUSD", "BUSD", "USTC", "USDX", "USDK", + "CUSD", "USD1", "USD0", "XUSD", "BFUSD", "USDS", "RLUSD", "OUSD", "USDH", + "APXUSD", "USDQ", "USDPT", "FIDD", "AUSD", + // EUR + "EURS", "EURC", "EURI", "EURT", "AGEUR", "CEUR", "AEUR", "EURQ", "EUROP", + // Other + "CNYT", // CNY + "CREAL", "BRL1", // BRL + "XSGD", // SGD + "GYEN", // JPY + "KGST", // KGS + "QCAD", // CAD + "TGBP", // GBP + "AUDX", // AUD + "MXNB", // MXN + }; + + private static readonly HashSet _commodities = new HashSet(StringComparer.OrdinalIgnoreCase) + { + // Metals + "XAU", "XAUT", "XAG", "XPT", "XPD", "COPPER", "PAXG", "XNI", "XCU", "XAL", "GOLD", "SILVER", + // Energy + "BZ", "NATGAS", "NGAS", "CL", "XTI", "UKOIL", "USOIL", "BRENTOIL" + }; + + private static readonly HashSet _stocks = new HashSet(StringComparer.OrdinalIgnoreCase) + { + // Top stocks, will need to update periodically + "AAAU", "AADR", "AAPL", "ACWI", "ACWX", "AGG", "AMD", "AMLP", "AMZN", "ARKF", + "ARKG", "ARKK", "ARKQ", "ARKW", "AVGO", "BA", "BABA", "BND", "BNDX", "BOTZ", + "CIBR", "COIN", "DIA", "DIVB", "DVY", "EEM", "EFA", "EFAV", "ESGU", "EWG", + "EWJ", "EWT", "EWU", "EWW", "EWY", "EWZ", "FDN", "FEZ", "GLDM", "GOOGL", + "HDV", "HOOD", "HYG", "IAU", "IBB", "ICLN", "IEFA", "IEMG", "IGSB", "IJH", + "IJR", "INTC", "ITOT", "IUSB", "IUSG", "IUSV", "IWM", "IWO", "IWR", "IYR", + "JETS", "JPM", "LIT", "MCHI", "META", "MGK", "MSTR", "MTUM", "MU", "NET", + "NFLX", "NOBL", "NVDA", "OIH", "ORCL", "PAVE", "PBW", "PLTR", "QQQ", "QQQM", + "SCHB", "SCHD", "SCHF", "SCHG", "SCHH", "SCHV", "SCHX", "SKHY", "SPCX", "SPCXD", + "SPLG", "SPY", "SPYG", "SPYV", "SQQQ", "TSLA", "TSM", "TQQQ", "USMV", "VBR", + "VCIT", "VCSH", "VEA", "VEU", "VGIT", "VGK", "VGT", "VHT", "VIG", "VNQ", + "VOO", "VOT", "VTI", "VTV", "VUG", "VXUS", "XBI", "XLC", "XLE", "XLF", + "XLI", "XLK", "XLP", "XLU", "XLV", "XLY", "CSCO", "UBER", "MRVL", "RKLB", + "COHR", "SOXL", "HD", "DIS", "CBRS", "V", "BRKB", "FLNC", "LLY", "COST", + "ARM", "BMNR", "NBIS", "ASML", "AAOI", "GLW", "SHLD", "BE", "QNTX", "IBM", + "AMAT", "NOK", "ASTS", "BBX", "SLX", "SKHYNIX", "SAMSUNG", "HYUNDAI", "NVO", + "IREN", "ONDS", "CRM" , "VRT", "ZEST", "BTW", "HPE", "AXTI", "BX", "CRWD", + "CRDO", "NOW", "ZM", "DKNG", "RIVN", "URNM", "EBAY", "ADBE", "UVXY", "RDW", + "CIEN","PANW", "WIN", "PAYP", "HIMS", "CRWV", "QCOM", "LITE", "DRAM", "ANTHROPIC", + "OPENAI", "USAR", "BILL", "SNDK", "NASDAQ100", "SPX500", "BSB", "CRCL", "STRC", + "MSFT", "WDC" + }; + private static ILogger? _staticLogger; /// /// Static logger @@ -105,6 +162,67 @@ namespace CryptoExchange.Net return _defaultClientReferences.TryGetValue(key, out var id) ? id : throw new KeyNotFoundException($"{exchange} not found in configuration"); } + /// + /// Check whether an asset is a known stablecoin. Note that this is not definitive, only large known stocks are checked + /// + /// Asset name + /// Additional stablecoin names for the specific exchange + public static bool IsStableCoin(string asset, params HashSet additionalStableCoins) + { + if (string.IsNullOrEmpty(asset)) + return false; + + return _stableCoins.Contains(asset) || (additionalStableCoins != null && additionalStableCoins.Contains(asset, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Check whether an asset is a known commodity. Note that this is not definitive, only large known stocks are checked + /// + /// Asset name + /// Additional commodity names for the specific exchange + public static bool IsCommodity(string asset, params HashSet additionalCommodities) + { + if (string.IsNullOrEmpty(asset)) + return false; + + return _commodities.Contains(asset) || (additionalCommodities != null && additionalCommodities.Contains(asset, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Check whether an asset is a known stock. Note that this is not definitive, only large known stocks are checked + /// + /// Asset name + /// Additional stock names for the specific exchange + public static bool IsEquity(string asset, params HashSet additionalStocks) + => IsEquity(asset, [], additionalStocks); + + /// + /// Check whether an asset is a known stock. + /// + /// Asset name + /// Suffixes to check, for example when `X` is a potential suffix both `TSLA` and `TSLAX` will be checked + /// Additional stock names for the specific exchange + public static bool IsEquity(string asset, string[] potentialSuffixes, params HashSet additionalStocks) + { + if (string.IsNullOrEmpty(asset)) + return false; + + if (_stocks.Contains(asset) || (additionalStocks != null && additionalStocks.Contains(asset, StringComparer.OrdinalIgnoreCase))) + return true; + + foreach (var suffix in potentialSuffixes) + { + if (!asset.EndsWith(suffix)) + continue; + + var suffixAsset = asset.Substring(0, asset.Length - suffix.Length); + if (_stocks.Contains(suffixAsset) || (additionalStocks != null && additionalStocks.Contains(suffixAsset, StringComparer.OrdinalIgnoreCase))) + return true; + } + + return false; + } + /// /// Create a new HttpMessageHandler instance /// diff --git a/CryptoExchange.Net/SharedApis/Enums/SharedAssetType.cs b/CryptoExchange.Net/SharedApis/Enums/SharedAssetType.cs new file mode 100644 index 00000000..ce07bd5d --- /dev/null +++ b/CryptoExchange.Net/SharedApis/Enums/SharedAssetType.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.SharedApis +{ + /// + /// Asset type + /// + public enum SharedAssetType + { + /// + /// Unknown or unspecified asset type + /// + Unspecified, + /// + /// Cryptocurrency asset type + /// + Crypto, + /// + /// Fiat currency asset type + /// + Fiat, + /// + /// Traditional finance asset type + /// + TradFi + } + + /// + /// Asset sub type + /// + public enum SharedAssetSubType + { + // --- Crypto sub types --- + /// + /// Stable coin, can be for different fiat currencies + /// + StableCoin, + + // --- TradFi sub types --- + /// + /// Equity, can be stocks, ETFs, or indices + /// + Equity, + /// + /// Commodity, can be oil, gas, metals, etc. + /// + Commodity + } +} diff --git a/CryptoExchange.Net/SharedApis/Interfaces/Rest/Futures/IFuturesSymbolRestClient.cs b/CryptoExchange.Net/SharedApis/Interfaces/Rest/Futures/IFuturesSymbolRestClient.cs index 761c2dcb..7329596d 100644 --- a/CryptoExchange.Net/SharedApis/Interfaces/Rest/Futures/IFuturesSymbolRestClient.cs +++ b/CryptoExchange.Net/SharedApis/Interfaces/Rest/Futures/IFuturesSymbolRestClient.cs @@ -9,6 +9,11 @@ namespace CryptoExchange.Net.SharedApis /// public interface IFuturesSymbolRestClient : ISharedClient { + /// + /// Get the futures symbol catalog. Only available if has been called previously. + /// + SharedSymbolCatalog? FuturesSymbolCatalog { get; } + /// /// Futures symbol request options.
/// Use and to check for required and optional parameters for the request.
diff --git a/CryptoExchange.Net/SharedApis/Interfaces/Rest/Spot/ISpotSymbolRestClient.cs b/CryptoExchange.Net/SharedApis/Interfaces/Rest/Spot/ISpotSymbolRestClient.cs index 30d00092..64e16d02 100644 --- a/CryptoExchange.Net/SharedApis/Interfaces/Rest/Spot/ISpotSymbolRestClient.cs +++ b/CryptoExchange.Net/SharedApis/Interfaces/Rest/Spot/ISpotSymbolRestClient.cs @@ -9,6 +9,11 @@ namespace CryptoExchange.Net.SharedApis ///
public interface ISpotSymbolRestClient : ISharedClient { + /// + /// Get the spot symbol catalog. Only available if has been called previously. + /// + SharedSymbolCatalog? SpotSymbolCatalog { get; } + /// /// Spot symbols request options.
/// Use and to check for required and optional parameters for the request.
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetFuturesSymbolsOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetFuturesSymbolsOptions.cs index 8663b466..ab2b92a3 100644 --- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetFuturesSymbolsOptions.cs +++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetFuturesSymbolsOptions.cs @@ -1,4 +1,5 @@ -using System; +using CryptoExchange.Net.Objects; +using System; using System.Collections.Generic; using System.Text; @@ -15,5 +16,43 @@ namespace CryptoExchange.Net.SharedApis public GetFuturesSymbolsOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesSymbolRestClient.GetFuturesSymbolsAsync)) { } + + /// + public override Error? ValidateRequest(GetSymbolsRequest request, IFuturesSymbolRestClient client) + { + if (request.BaseAssetType != null && request.BaseAssetSubType != null) + { + var error = ValidateAssetTypeCombination(request.BaseAssetType.Value, request.BaseAssetSubType.Value); + if (error != null) + return error; + } + + if (request.QuoteAssetType != null && request.QuoteAssetSubType != null) + { + var error = ValidateAssetTypeCombination(request.QuoteAssetType.Value, request.QuoteAssetSubType.Value); + if (error != null) + return error; + } + + return base.ValidateRequest(request, client); + } + + private Error? ValidateAssetTypeCombination(SharedAssetType type, SharedAssetSubType subType) + { + if (type == SharedAssetType.Crypto + && (subType == SharedAssetSubType.Commodity + || (subType == SharedAssetSubType.Equity))) + { + return ArgumentError.Invalid(nameof(GetSymbolsRequest.BaseAssetSubType), $"Invalid combination of asset type filters: {type} and {subType}"); + } + + if (type == SharedAssetType.TradFi && subType == SharedAssetSubType.StableCoin) + return ArgumentError.Invalid(nameof(GetSymbolsRequest.BaseAssetSubType), $"Invalid combination of asset type filters: {type} and {subType}"); + + if (type == SharedAssetType.Fiat) + return ArgumentError.Invalid(nameof(GetSymbolsRequest.BaseAssetSubType), $"Invalid combination of asset type filters: {type} and {subType}"); + + return null; + } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetSpotSymbolsOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetSpotSymbolsOptions.cs index ac25a78c..db7f463b 100644 --- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetSpotSymbolsOptions.cs +++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetSpotSymbolsOptions.cs @@ -1,4 +1,5 @@ -using System; +using CryptoExchange.Net.Objects; +using System; using System.Collections.Generic; using System.Text; @@ -15,5 +16,44 @@ namespace CryptoExchange.Net.SharedApis public GetSpotSymbolsOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotSymbolRestClient.GetSpotSymbolsAsync)) { } + + + /// + public override Error? ValidateRequest(GetSymbolsRequest request, ISpotSymbolRestClient client) + { + if (request.BaseAssetType != null && request.BaseAssetSubType != null) + { + var error = ValidateAssetTypeCombination(request.BaseAssetType.Value, request.BaseAssetSubType.Value); + if (error != null) + return error; + } + + if (request.QuoteAssetType != null && request.QuoteAssetSubType != null) + { + var error = ValidateAssetTypeCombination(request.QuoteAssetType.Value, request.QuoteAssetSubType.Value); + if (error != null) + return error; + } + + return base.ValidateRequest(request, client); + } + + private Error? ValidateAssetTypeCombination(SharedAssetType type, SharedAssetSubType subType) + { + if (type == SharedAssetType.Crypto + && (subType == SharedAssetSubType.Commodity + || (subType == SharedAssetSubType.Equity))) + { + return ArgumentError.Invalid(nameof(GetSymbolsRequest.BaseAssetSubType), $"Invalid combination of asset type filters: {type} and {subType}"); + } + + if (type == SharedAssetType.TradFi && subType == SharedAssetSubType.StableCoin) + return ArgumentError.Invalid(nameof(GetSymbolsRequest.BaseAssetSubType), $"Invalid combination of asset type filters: {type} and {subType}"); + + if (type == SharedAssetType.Fiat) + return ArgumentError.Invalid(nameof(GetSymbolsRequest.BaseAssetSubType), $"Invalid combination of asset type filters: {type} and {subType}"); + + return null; + } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Rest/GetSymbolsRequest.cs b/CryptoExchange.Net/SharedApis/Models/Rest/GetSymbolsRequest.cs index ce71f088..6aa66445 100644 --- a/CryptoExchange.Net/SharedApis/Models/Rest/GetSymbolsRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/Rest/GetSymbolsRequest.cs @@ -5,13 +5,44 @@ ///
public record GetSymbolsRequest : SharedRequest { + /// + /// Base asset type filter + /// + public SharedAssetType? BaseAssetType { get; } + /// + /// Base asset subtype filter + /// + public SharedAssetSubType? BaseAssetSubType { get; } + /// + /// Quote asset type filter + /// + public SharedAssetType? QuoteAssetType { get; } + /// + /// Quote asset subtype filter + /// + public SharedAssetSubType? QuoteAssetSubType { get; } + /// /// ctor /// /// Trading mode filter + /// Filter by base asset type + /// Filter by base asset subtype + /// Filter by quote asset type + /// Filter by quote asset subtype /// Exchange specific parameters - public GetSymbolsRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters) + public GetSymbolsRequest( + TradingMode? tradingMode = null, + SharedAssetType? baseAssetType = null, + SharedAssetSubType? baseAssetSubType = null, + SharedAssetType? quoteAssetType = null, + SharedAssetSubType? quoteAssetSubType = null, + ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters) { + BaseAssetType = baseAssetType; + BaseAssetSubType = baseAssetSubType; + QuoteAssetType = quoteAssetType; + QuoteAssetSubType = quoteAssetSubType; } } } diff --git a/CryptoExchange.Net/SharedApis/Models/SharedSymbolCatalog.cs b/CryptoExchange.Net/SharedApis/Models/SharedSymbolCatalog.cs new file mode 100644 index 00000000..abd8c422 --- /dev/null +++ b/CryptoExchange.Net/SharedApis/Models/SharedSymbolCatalog.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace CryptoExchange.Net.SharedApis +{ + /// + /// Symbol and asset catalog for a shared client + /// + public class SharedSymbolCatalog + { + /// + /// Exchange name + /// + public string Exchange { get; set; } = string.Empty; + /// + /// Assets supported + /// + public IReadOnlyDictionary Assets { get; set; } = new Dictionary(); + /// + /// Symbols supported + /// + public IReadOnlyDictionary Symbols { get; set; } = new Dictionary(); + } + + /// + /// Asset info + /// + [DebuggerDisplay("{DebugView,nq}")] + public class SharedAssetInfo + { + private string DebugView => $"{Name} - {Type}{(SubType == null ? "": $" {SubType}")}"; + + /// + /// Asset name + /// + public string Name { get; set; } + /// + /// Asset type + /// + public SharedAssetType Type { get; set; } + /// + /// Asset sub type + /// + public SharedAssetSubType? SubType { get; set; } + + /// + /// ctor + /// + public SharedAssetInfo(string name, SharedAssetType type, SharedAssetSubType? subType) + { + Name = name; + Type = type; + SubType = subType; + } + } +} diff --git a/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs b/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs index a8718cf2..8db5fb81 100644 --- a/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs +++ b/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs @@ -10,7 +10,7 @@ namespace CryptoExchange.Net.SharedApis [DebuggerDisplay("{DebugView,nq}")] public record SharedFuturesSymbol : SharedSpotSymbol { - private string DebugView => $"{TradingMode} {Name}{(DeliveryTime != null ? $" Delivery: {DeliveryTime:yyyy-MM-dd}": "")}"; + private string DebugView => $"{TradingMode} {(DisplayName ?? Name)} - {BaseAssetType}{(BaseAssetSubType == null ? "" : " " + BaseAssetSubType)}{(DeliveryTime != null ? $" Delivery: {DeliveryTime:yyyy-MM-dd}": "")}"; /// /// The size of a single contract diff --git a/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs b/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs index 2233f7b7..1eb70235 100644 --- a/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs +++ b/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs @@ -1,13 +1,17 @@ -using System.Diagnostics; +using System; +using System.Data; +using System.Diagnostics; namespace CryptoExchange.Net.SharedApis { /// /// Symbol info /// - [DebuggerDisplay("{TradingMode} {Name,nq}")] + [DebuggerDisplay("{DebugView,nq}")] public record SharedSpotSymbol { + private string DebugView => $"{TradingMode} {(DisplayName ?? Name)} - {BaseAssetType} {BaseAssetSubType}"; + /// /// The trading mode of the symbol /// @@ -25,6 +29,10 @@ namespace CryptoExchange.Net.SharedApis /// public string Name { get; set; } /// + /// The display name of the symbol + /// + public string? DisplayName { get; set; } + /// /// Minimal quantity of an order in the base asset /// public decimal? MinTradeQuantity { get; set; } @@ -60,6 +68,22 @@ namespace CryptoExchange.Net.SharedApis /// Whether the symbol is currently available for trading /// public bool Trading { get; set; } + /// + /// Base asset type + /// + public SharedAssetType BaseAssetType { get; set; } + /// + /// Base asset sub type + /// + public SharedAssetSubType? BaseAssetSubType { get; set; } + /// + /// Quote asset type + /// + public SharedAssetType QuoteAssetType { get; set; } + /// + /// Quote asset sub type + /// + public SharedAssetSubType? QuoteAssetSubType { get; set; } /// /// ctor diff --git a/CryptoExchange.Net/SharedApis/SharedUtils.cs b/CryptoExchange.Net/SharedApis/SharedUtils.cs index 426c7f09..b4c098b5 100644 --- a/CryptoExchange.Net/SharedApis/SharedUtils.cs +++ b/CryptoExchange.Net/SharedApis/SharedUtils.cs @@ -1,5 +1,6 @@ using CryptoExchange.Net.Objects; using System.Collections.Generic; +using System.Linq; namespace CryptoExchange.Net.SharedApis { @@ -176,5 +177,21 @@ namespace CryptoExchange.Net.SharedApis return result.ToArray(); } + + public static T[] ApplySymbolFilter(T[] symbols, GetSymbolsRequest request) where T : SharedSpotSymbol + { + IEnumerable resultData = symbols; + if (request.TradingMode != null) + resultData = resultData.Where(x => x.TradingMode == request.TradingMode); + if (request.BaseAssetType != null) + resultData = resultData.Where(x => x.BaseAssetType == request.BaseAssetType); + if (request.QuoteAssetType != null) + resultData = resultData.Where(x => x.QuoteAssetType == request.QuoteAssetType); + if (request.BaseAssetSubType != null) + resultData = resultData.Where(x => x.BaseAssetSubType == request.BaseAssetSubType); + if (request.QuoteAssetSubType != null) + resultData = resultData.Where(x => x.QuoteAssetSubType == request.QuoteAssetSubType); + return resultData.ToArray(); + } } }