From 2f82e2015b1a5485d0837b27c42d4df97364897a Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 4 Aug 2025 09:39:30 +0200 Subject: [PATCH] Updated Shared symbol requests/subscriptions to allow multiple symbols in one call if supported --- CryptoExchange.Net/ExchangeSymbolCache.cs | 4 +-- .../Options/Endpoints/EndpointOptions.cs | 22 +++++++++++++ .../SharedApis/Models/SharedSymbolRequest.cs | 33 +++++++++++++++++-- .../Socket/SubscribeBookTickerRequest.cs | 21 +++++++++++- .../Models/Socket/SubscribeKlineRequest.cs | 25 +++++++++++++- .../Socket/SubscribeOrderBookRequest.cs | 21 +++++++++++- .../Models/Socket/SubscribeTickerRequest.cs | 22 ++++++++++++- .../Models/Socket/SubscribeTradeRequest.cs | 21 +++++++++++- .../ResponseModels/SharedFuturesSymbol.cs | 3 ++ .../ResponseModels/SharedSpotSymbol.cs | 6 ++++ .../Sockets/SocketConnection.cs | 4 +-- 11 files changed, 171 insertions(+), 11 deletions(-) diff --git a/CryptoExchange.Net/ExchangeSymbolCache.cs b/CryptoExchange.Net/ExchangeSymbolCache.cs index 74c4dc1..765b1e1 100644 --- a/CryptoExchange.Net/ExchangeSymbolCache.cs +++ b/CryptoExchange.Net/ExchangeSymbolCache.cs @@ -23,14 +23,14 @@ namespace CryptoExchange.Net { if(!_symbolInfos.TryGetValue(topicId, out var exchangeInfo)) { - exchangeInfo = new ExchangeInfo(DateTime.UtcNow, updateData.ToDictionary(x => x.Name, x => new SharedSymbol(x.TradingMode, x.BaseAsset.ToUpperInvariant(), x.QuoteAsset.ToUpperInvariant(), (x as SharedFuturesSymbol)?.DeliveryTime) { SymbolName = x.Name })); + exchangeInfo = new ExchangeInfo(DateTime.UtcNow, updateData.ToDictionary(x => x.Name, x => x.SharedSymbol)); _symbolInfos.TryAdd(topicId, exchangeInfo); } if (DateTime.UtcNow - exchangeInfo.UpdateTime < TimeSpan.FromMinutes(60)) return; - _symbolInfos[topicId] = new ExchangeInfo(DateTime.UtcNow, updateData.ToDictionary(x => x.Name, x => new SharedSymbol(x.TradingMode, x.BaseAsset.ToUpperInvariant(), x.QuoteAsset.ToUpperInvariant(), (x as SharedFuturesSymbol)?.DeliveryTime) { SymbolName = x.Name })); + _symbolInfos[topicId] = new ExchangeInfo(DateTime.UtcNow, updateData.ToDictionary(x => x.Name, x => x.SharedSymbol)); } /// diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs index 3bc4b4b..0113ac4 100644 --- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs +++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs @@ -109,6 +109,15 @@ namespace CryptoExchange.Net.SharedApis /// public List RequiredOptionalParameters { get; set; } = new List(); + /// + /// Whether this accepts multiple symbols (Only applicable to request requiring symbol parameters) + /// + public bool SupportsMultipleSymbols { get; set; } = false; + /// + /// The max number of symbols which can be passed in a call (Only applicable to request requiring symbol parameters) + /// + public int? MaxSymbolCount { get; set; } + /// /// ctor /// @@ -141,6 +150,19 @@ namespace CryptoExchange.Net.SharedApis } + if (request is SharedSymbolRequest symbolsRequest) + { + if (symbolsRequest.Symbols != null) + { + if (!SupportsMultipleSymbols) + return new ArgumentError($"Only a single symbol parameter is allowed, multiple symbols are not supported"); + + if (symbolsRequest.Symbols.Length > MaxSymbolCount) + return new ArgumentError($"Max number of symbols is {MaxSymbolCount} but {symbolsRequest.Symbols.Length} were passed"); + } + + } + return ValidateRequest(exchange, request.ExchangeParameters, tradingMode, supportedTradingModes); } diff --git a/CryptoExchange.Net/SharedApis/Models/SharedSymbolRequest.cs b/CryptoExchange.Net/SharedApis/Models/SharedSymbolRequest.cs index b022692..a50355c 100644 --- a/CryptoExchange.Net/SharedApis/Models/SharedSymbolRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/SharedSymbolRequest.cs @@ -1,14 +1,27 @@ -namespace CryptoExchange.Net.SharedApis +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace CryptoExchange.Net.SharedApis { /// /// Symbol request /// public record SharedSymbolRequest : SharedRequest { + /// + /// Trading mode + /// + public TradingMode TradingMode { get; } /// /// The symbol /// - public SharedSymbol Symbol { get; set; } + public SharedSymbol? Symbol { get; set; } + /// + /// Symbols + /// + public SharedSymbol[]? Symbols { get; set; } /// /// ctor @@ -16,6 +29,22 @@ public SharedSymbolRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters) { Symbol = symbol; + TradingMode = symbol.TradingMode; + } + + /// + /// ctor + /// + public SharedSymbolRequest(IEnumerable symbols, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters) + { + if (!symbols.Any()) + throw new ArgumentException("Empty symbol list"); + + if (symbols.GroupBy(x => x.TradingMode).Count() > 1) + throw new ArgumentException("All symbols in the symbol list should have the same trading mode"); + + Symbols = symbols.ToArray(); + TradingMode = Symbols.First().TradingMode; } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeBookTickerRequest.cs b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeBookTickerRequest.cs index 5cc5563..6cabc03 100644 --- a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeBookTickerRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeBookTickerRequest.cs @@ -1,4 +1,6 @@ -namespace CryptoExchange.Net.SharedApis +using System.Collections.Generic; + +namespace CryptoExchange.Net.SharedApis { /// /// Request to subscribe to book ticker updates @@ -13,5 +15,22 @@ public SubscribeBookTickerRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters) { } + + /// + /// ctor + /// + /// The symbols to subscribe to + /// Exchange specific parameters + public SubscribeBookTickerRequest(IEnumerable symbols, ExchangeParameters? exchangeParameters = null) : base(symbols, exchangeParameters) + { + } + + /// + /// ctor + /// + /// The symbols to subscribe to + public SubscribeBookTickerRequest(params SharedSymbol[] symbols) : base(symbols, null) + { + } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeKlineRequest.cs b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeKlineRequest.cs index 315423f..04a6dbb 100644 --- a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeKlineRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeKlineRequest.cs @@ -1,4 +1,6 @@ -namespace CryptoExchange.Net.SharedApis +using System.Collections.Generic; + +namespace CryptoExchange.Net.SharedApis { /// /// Request to subscribe to kline/candlestick updates @@ -20,5 +22,26 @@ { Interval = interval; } + + /// + /// ctor + /// + /// The symbols to subscribe to + /// Kline interval + /// Exchange specific parameters + public SubscribeKlineRequest(IEnumerable symbols, SharedKlineInterval interval, ExchangeParameters? exchangeParameters = null) : base(symbols, exchangeParameters) + { + Interval = interval; + } + + /// + /// ctor + /// + /// Kline interval + /// The symbols to subscribe to + public SubscribeKlineRequest(SharedKlineInterval interval, params SharedSymbol[] symbols) : base(symbols, null) + { + Interval = interval; + } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeOrderBookRequest.cs b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeOrderBookRequest.cs index e88ae1d..65f744d 100644 --- a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeOrderBookRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeOrderBookRequest.cs @@ -1,4 +1,6 @@ -namespace CryptoExchange.Net.SharedApis +using System.Collections.Generic; + +namespace CryptoExchange.Net.SharedApis { /// /// Request to subscribe to order book snapshot updates @@ -20,5 +22,22 @@ { Limit = limit; } + + /// + /// ctor + /// + /// The symbols to subscribe to + /// Exchange specific parameters + public SubscribeOrderBookRequest(IEnumerable symbols, ExchangeParameters? exchangeParameters = null) : base(symbols, exchangeParameters) + { + } + + /// + /// ctor + /// + /// The symbols to subscribe to + public SubscribeOrderBookRequest(params SharedSymbol[] symbols) : base(symbols, null) + { + } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTickerRequest.cs b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTickerRequest.cs index 5eef7ae..46f3636 100644 --- a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTickerRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTickerRequest.cs @@ -1,4 +1,7 @@ -namespace CryptoExchange.Net.SharedApis +using System.Collections; +using System.Collections.Generic; + +namespace CryptoExchange.Net.SharedApis { /// /// Request to subscribe to ticker updates @@ -13,5 +16,22 @@ public SubscribeTickerRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters) { } + + /// + /// ctor + /// + /// The symbols to subscribe to + /// Exchange specific parameters + public SubscribeTickerRequest(IEnumerable symbols, ExchangeParameters? exchangeParameters = null) : base(symbols, exchangeParameters) + { + } + + /// + /// ctor + /// + /// The symbols to subscribe to + public SubscribeTickerRequest(params SharedSymbol[] symbols) : base(symbols, null) + { + } } } diff --git a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTradeRequest.cs b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTradeRequest.cs index d416075..115cc1f 100644 --- a/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTradeRequest.cs +++ b/CryptoExchange.Net/SharedApis/Models/Socket/SubscribeTradeRequest.cs @@ -1,4 +1,6 @@ -namespace CryptoExchange.Net.SharedApis +using System.Collections.Generic; + +namespace CryptoExchange.Net.SharedApis { /// /// Request to subscribe to trade updates @@ -13,5 +15,22 @@ public SubscribeTradeRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters) { } + + /// + /// ctor + /// + /// The symbols to subscribe to + /// Exchange specific parameters + public SubscribeTradeRequest(IEnumerable symbols, ExchangeParameters? exchangeParameters = null) : base(symbols, exchangeParameters) + { + } + + /// + /// ctor + /// + /// The symbols to subscribe to + public SubscribeTradeRequest(params SharedSymbol[] symbols) : base(symbols, null) + { + } } } diff --git a/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs b/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs index ed4e84e..7f24acd 100644 --- a/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs +++ b/CryptoExchange.Net/SharedApis/ResponseModels/SharedFuturesSymbol.cs @@ -30,5 +30,8 @@ namespace CryptoExchange.Net.SharedApis public SharedFuturesSymbol(TradingMode symbolType, string baseAsset, string quoteAsset, string symbol, bool trading) : base(baseAsset, quoteAsset, symbol, trading, symbolType) { } + + /// + public override SharedSymbol SharedSymbol => new SharedSymbol(TradingMode, BaseAsset.ToUpperInvariant(), QuoteAsset.ToUpperInvariant(), DeliveryTime) { SymbolName = Name }; } } diff --git a/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs b/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs index 71cd41d..ef8a9bd 100644 --- a/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs +++ b/CryptoExchange.Net/SharedApis/ResponseModels/SharedSpotSymbol.cs @@ -69,5 +69,11 @@ Name = symbol; Trading = trading; } + + /// + /// The SharedSymbol of this symbol + /// + /// + public virtual SharedSymbol SharedSymbol => new SharedSymbol(TradingMode, BaseAsset.ToUpperInvariant(), QuoteAsset.ToUpperInvariant()) { SymbolName = Name }; } } diff --git a/CryptoExchange.Net/Sockets/SocketConnection.cs b/CryptoExchange.Net/Sockets/SocketConnection.cs index 7abc49d..3dc3fee 100644 --- a/CryptoExchange.Net/Sockets/SocketConnection.cs +++ b/CryptoExchange.Net/Sockets/SocketConnection.cs @@ -864,7 +864,7 @@ namespace CryptoExchange.Net.Sockets { if (ApiClient.MessageSendSizeLimit != null && data.Length > ApiClient.MessageSendSizeLimit.Value) { - var info = $"Message to send exceeds the max server message size ({ApiClient.MessageSendSizeLimit.Value} bytes). Split the request into batches to keep below this limit"; + var info = $"Message to send exceeds the max server message size ({data.Length} vs {ApiClient.MessageSendSizeLimit.Value} bytes). Split the request into batches to keep below this limit"; _logger.LogWarning("[Sckt {SocketId}] [Req {RequestId}] {Info}", SocketId, requestId, info); return new CallResult(new InvalidOperationError(info)); } @@ -899,7 +899,7 @@ namespace CryptoExchange.Net.Sockets { if (ApiClient.MessageSendSizeLimit != null && data.Length > ApiClient.MessageSendSizeLimit.Value) { - var info = $"Message to send exceeds the max server message size ({ApiClient.MessageSendSizeLimit.Value} bytes). Split the request into batches to keep below this limit"; + var info = $"Message to send exceeds the max server message size ({data.Length} vs {ApiClient.MessageSendSizeLimit.Value} bytes). Split the request into batches to keep below this limit"; _logger.LogWarning("[Sckt {SocketId}] [Req {RequestId}] {Info}", SocketId, requestId, info); return new CallResult(new InvalidOperationError(info)); }