From c1506817e619ad5cad49f05d8dc627108af04a35 Mon Sep 17 00:00:00 2001 From: Dmitry Tarakanov Date: Wed, 1 Apr 2026 21:33:21 +0300 Subject: [PATCH 1/6] #22 new binance provider --- .../Crypto.Compare.Data/SymbolProvider.cs | 4 +- .../BinanceOrderBookListener.cs | 227 ++++++++++++++++++ .../Crypto.Comapre.Binance/Consts.cs | 6 + .../Crypto.Comapre.Binance.csproj | 9 + 4 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs create mode 100644 sources/integrations/Crypto.Comapre.Binance/Consts.cs create mode 100644 sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj diff --git a/sources/core/Crypto.Compare.Data/SymbolProvider.cs b/sources/core/Crypto.Compare.Data/SymbolProvider.cs index da46027..08cb9a0 100644 --- a/sources/core/Crypto.Compare.Data/SymbolProvider.cs +++ b/sources/core/Crypto.Compare.Data/SymbolProvider.cs @@ -41,11 +41,11 @@ public class SymbolProvider /// Last date get quotes /// public DateTime UpdatedAt { get; set; } - + /// /// id symbol /// public long SymbolId { get; set; } - + public virtual Symbol? Symbol { get; set; } } diff --git a/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs b/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs new file mode 100644 index 0000000..28f9b3e --- /dev/null +++ b/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs @@ -0,0 +1,227 @@ +using Binance.Net.Clients; +using Binance.Net.Interfaces.Clients; +using Binance.Net.Objects.Models.Spot; +using Binance.Net.Objects.Options; +using BingX.Net.Clients; +using BingX.Net.Enums; +using BingX.Net.Interfaces.Clients; +using BingX.Net.Objects.Models; +using BingX.Net.Objects.Options; +using Crypto.Compare.Adapter; +using Crypto.Compare.Adapter.AdaptersObservable; +using Crypto.Compare.Adapter.Impl; +using Crypto.Compare.Data; +using CryptoExchange.Net.Authentication; +using CryptoExchange.Net.Objects.Errors; +using CryptoExchange.Net.Objects.Options; +using CryptoExchange.Net.Objects.Sockets; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + + +namespace Crypto.Compare.BingX; + +public class BinanceOrderBookListener : IListenerAdapter +{ + private const string LogPrefix = "Binance:"; + private Provider _provider; + private bool _isStarted; + private readonly IAdaptersObservable _adaptersObservable; + private readonly IBinanceSocketClient _socketClient; + private readonly IBinanceRestClient _restClient; + private readonly ILogger _logger; + private readonly SortedDictionary _symbols = new (); + private readonly List _sockets = new (); + private readonly ILoggerFactory _loggerFactory; + private readonly IOptions _options; + + public BinanceOrderBookListener(IBinanceSocketClient socketClient, IBinanceRestClient restClient, ILogger logger, + IAdaptersObservable adaptersObservable, ILoggerFactory loggerFactory, IOptions options) + { + _socketClient = socketClient; + _restClient = restClient; + _logger = logger; + _adaptersObservable = adaptersObservable; + _loggerFactory = loggerFactory; + _options = options; + } + + public async Task StartAsync(Provider provider, CancellationToken cancellationToken) + { + _provider = provider; + + // get all symbols + if (!await LoadSymbolsAsync(cancellationToken)) + { + return; + } + + // subscribe on events + if (!await SubscribeSymbolsAsync(cancellationToken)) + { + return; + } + + // started + _isStarted = true; + } + + private async Task SubscribeSymbolsAsync(CancellationToken cancellationToken) + { + try + { + var socketConnection = GetConnection(); + + foreach (var symbol in _symbols) + { + (socketConnection, var result) = await TrySubscribeBatchesAsync(symbol.Value.Name, socketConnection, cancellationToken); + if (!result) + { + return _sockets.Count >= 1; + } + } + + + return true; + } + catch (Exception e) + { + _logger.LogError(e, LogPrefix + " Subscribe on symbols failed"); + return false; + } + } + + private async Task<(BinanceSocketClient bybitConnection, bool subscribeSymbolsAsync)> TrySubscribeBatchesAsync(string symbol, BinanceSocketClient socketConnection, + CancellationToken cancellationToken) + { + var result = await socketConnection.SpotApi.SubscribeToBookPriceUpdatesAsync(symbol, HandlerAction, cancellationToken); + + if (!result.Success) + { + if (result.Error.ErrorType == ErrorType.Unknown) + { + // try create new connection and subscribing + socketConnection = GetConnection(); + result = await socketConnection.SpotApi.SubscribeToBookPriceUpdatesAsync(symbol, HandlerAction, cancellationToken); + if (result.Success) + { + return (socketConnection, true); + } + + _logger.LogWarning(LogPrefix + " Not subscribe on ticker: {Symbol}", symbol); + return (socketConnection, false); + } + + _logger.LogWarning(LogPrefix + " Not subscribe on tickers: {Symbols}", symbol); + return (socketConnection, false); + } + + _logger.LogInformation(LogPrefix + " subscribed success, connection: {Number}, {Symbols}", _sockets.Count, symbol); + return (socketConnection, true); + } + + + private BinanceSocketClient GetConnection() + { + var connection = new BinanceSocketClient(_options, _loggerFactory); + connection.SetOptions(new UpdateOptions + { + ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey) + }); + _sockets.Add(connection); + return connection; + } + + + private async Task LoadSymbolsAsync(CancellationToken cancellationToken) + { + try + { + var exchangeInfo = await _restClient.SpotApi.ExchangeData.GetSymbolsAsync(ct: cancellationToken); + + if (!exchangeInfo.Success) + { + _logger.LogError(LogPrefix + " Get symbols failed"); + return false; + } + + foreach (var symbol in exchangeInfo.Data) + { + if (symbol.Status != SymbolStatus.Online) + { + continue; + } + + _symbols.Add(symbol.Name.ToLowerInvariant(), symbol); + } + } + catch (Exception e) + { + _logger.LogError(e, LogPrefix + " Load symbols failed"); + return false; + } + + return true; + } + + /// + /// Handler event change book of currency + /// + /// Data of event + private void HandlerAction(DataEventBinanceBookTickerUpdate> dataEvent) + { + var askPrice = 0m; + var bidPrice = 0m; + var askQuantity = 0m; + var bidQuantity = 0m; + if (string.IsNullOrEmpty(dataEvent.Symbol)) + { + return; + } + + if (!_symbols.TryGetValue(dataEvent.Symbol.ToLowerInvariant(), out var symbol)) + { + _logger.LogWarning("Symbol not found: {Symbol}", dataEvent.Symbol); + return; + } + var parts = symbol.Name.Split('-', 2); + var (baseSymbol, quoteSymbol) = (parts[0].ToLowerInvariant(), parts.Length > 1 ? parts[1].ToLowerInvariant() : string.Empty); + if (dataEvent.Data?.BestAskPrice > 0) + { + askPrice = dataEvent.Data.BestAskPrice; + askQuantity = dataEvent.Data.BestAskQuantity; + } + + if (dataEvent.Data?.BestBidPrice > 0) + { + bidPrice = dataEvent.Data.BestBidPrice; + bidQuantity = dataEvent.Data.BestBidQuantity; + } + + _adaptersObservable.OnNext(new SymbolData(ticker: dataEvent.Symbol, + providerId: _provider.Id, + askPrice: askPrice, + bidPrice: bidPrice, + askQuantity: askQuantity, + bidQuantity: bidQuantity, + baseSymbol: baseSymbol, + quoteSymbol: quoteSymbol)); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _socketClient?.Dispose(); + foreach (var socketClient in _sockets) + { + socketClient?.Dispose(); + } + + return Task.CompletedTask; + } + + public bool IsSupported(string providerName) + => string.Equals(providerName, Consts.ProviderName, StringComparison.OrdinalIgnoreCase); + + public bool IsStarted() + => _isStarted; +} diff --git a/sources/integrations/Crypto.Comapre.Binance/Consts.cs b/sources/integrations/Crypto.Comapre.Binance/Consts.cs new file mode 100644 index 0000000..b414d0c --- /dev/null +++ b/sources/integrations/Crypto.Comapre.Binance/Consts.cs @@ -0,0 +1,6 @@ +namespace Crypto.Compare.BingX; + +public class Consts +{ + public const string ProviderName = "BingX"; +} \ No newline at end of file diff --git a/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + From 58664dc3a08203917bf9663218227bcbbb14a7f2 Mon Sep 17 00:00:00 2001 From: Dmitry Tarakanov Date: Mon, 27 Apr 2026 21:06:00 +0300 Subject: [PATCH 2/6] #22 added binance market --- Crypto.Compare.sln | 7 ++ README.md | 23 +++++++ .../BinanceOrderBookListener.cs | 68 +++++++++++-------- .../Crypto.Comapre.Binance/Consts.cs | 2 +- .../Crypto.Comapre.Binance.csproj | 8 +++ .../BingXOrderBookListener.cs | 13 ++-- .../Crypto.Compare.BingX.csproj | 2 +- .../BitMartOrderBookListener.cs | 5 +- .../Crypto.Compare.BitMart.csproj | 2 +- .../BitgetOrderBookListener.cs | 18 +---- .../Crypto.Compare.Bitget.csproj | 2 +- .../ByBitOrderBookListener.cs | 15 ++-- .../Crypto.Compare.ByBit.csproj | 2 +- .../Crypto.Compare.GateIo.csproj | 2 +- .../GateIoOrderBookListener.cs | 10 +-- .../Crypto.Compare.Mexc.csproj | 2 +- .../MexcOrderBookListener.cs | 6 +- .../Crypto.Compare.Xtcom.csproj | 2 +- .../XtcomOrderBookListener.cs | 15 ++-- .../Crypto.Compare.PublicApi.csproj | 13 ++-- .../Ioc/ServicesRegistry.cs | 50 ++++++++++---- .../Crypto.Compare.PublicApi.Tests.csproj | 8 +-- 22 files changed, 166 insertions(+), 109 deletions(-) diff --git a/Crypto.Compare.sln b/Crypto.Compare.sln index 470a8ab..114c666 100644 --- a/Crypto.Compare.sln +++ b/Crypto.Compare.sln @@ -45,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crypto.Compare.GateIo", "so EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crypto.Compare.BitMart", "sources\integrations\Crypto.Compare.BitMart\Crypto.Compare.BitMart.csproj", "{B6196161-CEF3-48D7-8F56-692472A5E4FB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crypto.Comapre.Binance", "sources\integrations\Crypto.Comapre.Binance\Crypto.Comapre.Binance.csproj", "{A62F8D6B-37A8-4D7A-A759-03E3C883239B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -111,6 +113,10 @@ Global {B6196161-CEF3-48D7-8F56-692472A5E4FB}.Debug|Any CPU.Build.0 = Debug|Any CPU {B6196161-CEF3-48D7-8F56-692472A5E4FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6196161-CEF3-48D7-8F56-692472A5E4FB}.Release|Any CPU.Build.0 = Release|Any CPU + {A62F8D6B-37A8-4D7A-A759-03E3C883239B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A62F8D6B-37A8-4D7A-A759-03E3C883239B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A62F8D6B-37A8-4D7A-A759-03E3C883239B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A62F8D6B-37A8-4D7A-A759-03E3C883239B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -135,6 +141,7 @@ Global {410272B7-D024-49E8-9435-DDF0F309A134} = {A9E9246F-B758-4D14-ADAD-1EF937DCC5E8} {A2DB814C-895A-4D09-AFC1-FDD0C988F2FD} = {A9E9246F-B758-4D14-ADAD-1EF937DCC5E8} {B6196161-CEF3-48D7-8F56-692472A5E4FB} = {A9E9246F-B758-4D14-ADAD-1EF937DCC5E8} + {A62F8D6B-37A8-4D7A-A759-03E3C883239B} = {A9E9246F-B758-4D14-ADAD-1EF937DCC5E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D179164C-BFE5-45FD-BD60-476E93A84589} diff --git a/README.md b/README.md index dbfd376..e0c97b3 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,17 @@ INSERT INTO public."Providers"( Need to replace in the request: AccessKey and SecretKey - for keys from the exchange's personal account +### BINANCE + +```sql +INSERT INTO public."Providers"( + "Name", "WebSite", "BaseUrl", "AccessKey", "SecretKey", "Status", "CreatedAt", "UpdatedAt") + VALUES ('binance', 'https://binance.com', 'https://api.binance.com', 'AccessKey', 'SecretKey', 1,NOW(), NOW()); +``` + +Need to replace in the request: +AccessKey and SecretKey - for keys from the exchange's personal account + ## Getting API keys for exchanges - **ByBit:** https://www.bybit.com/en/help-center/article/How-to-create-your-API-key?category=ae1012f19fad1c184e @@ -502,6 +513,18 @@ INSERT INTO public."Providers"( "Name", "WebSite", "BaseUrl", "AccessKey", "SecretKey", "Status", "CreatedAt", "UpdatedAt", "PasswordKey") VALUES ( 'bitmart', 'https://bitmart.com', 'https://api-cloud.bitmart.com', 'AccessKey', 'SecretKey', 1,NOW(), NOW(), ''); ``` + +Нужно заменить в запросе: +AccessKey и SecretKey - на ключи с личного кабинета биржи + +### BINANCE + +```sql +INSERT INTO public."Providers"( + "Name", "WebSite", "BaseUrl", "AccessKey", "SecretKey", "Status", "CreatedAt", "UpdatedAt") + VALUES ('binance', 'https://binance.com', 'https://api.binance.com', 'AccessKey', 'SecretKey', 1,NOW(), NOW()); +``` + Нужно заменить в запросе: AccessKey и SecretKey - на ключи с личного кабинета биржи diff --git a/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs b/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs index 28f9b3e..770142e 100644 --- a/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs +++ b/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs @@ -1,20 +1,16 @@ -using Binance.Net.Clients; +using Binance.Net; +using Binance.Net.Clients; +using Binance.Net.Interfaces; using Binance.Net.Interfaces.Clients; -using Binance.Net.Objects.Models.Spot; +using Binance.Net.Objects.Models.Spot.Socket; using Binance.Net.Objects.Options; -using BingX.Net.Clients; -using BingX.Net.Enums; -using BingX.Net.Interfaces.Clients; -using BingX.Net.Objects.Models; -using BingX.Net.Objects.Options; using Crypto.Compare.Adapter; using Crypto.Compare.Adapter.AdaptersObservable; using Crypto.Compare.Adapter.Impl; using Crypto.Compare.Data; -using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Objects.Errors; -using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.SharedApis; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -30,8 +26,8 @@ public class BinanceOrderBookListener : IListenerAdapter private readonly IBinanceSocketClient _socketClient; private readonly IBinanceRestClient _restClient; private readonly ILogger _logger; - private readonly SortedDictionary _symbols = new (); - private readonly List _sockets = new (); + private readonly SortedDictionary _symbols = new(); + private readonly List _sockets = new(); private readonly ILoggerFactory _loggerFactory; private readonly IOptions _options; @@ -44,6 +40,10 @@ public BinanceOrderBookListener(IBinanceSocketClient socketClient, IBinanceRestC _adaptersObservable = adaptersObservable; _loggerFactory = loggerFactory; _options = options; + BinanceSocketClient.SetDefaultOptions(x => + { + x.ApiCredentials = new BinanceCredentials(_provider.AccessKey, _provider.SecretKey); + }); } public async Task StartAsync(Provider provider, CancellationToken cancellationToken) @@ -71,10 +71,11 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok try { var socketConnection = GetConnection(); + var keys = _symbols.Values.Select(x => x.Name).ToList(); - foreach (var symbol in _symbols) + foreach (var symbols in SplitIntoBatches(keys, 15)) { - (socketConnection, var result) = await TrySubscribeBatchesAsync(symbol.Value.Name, socketConnection, cancellationToken); + (socketConnection, var result) = await TrySubscribeBatchesAsync(symbols, socketConnection, cancellationToken); if (!result) { return _sockets.Count >= 1; @@ -91,10 +92,18 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok } } - private async Task<(BinanceSocketClient bybitConnection, bool subscribeSymbolsAsync)> TrySubscribeBatchesAsync(string symbol, BinanceSocketClient socketConnection, - CancellationToken cancellationToken) + private static IEnumerable> SplitIntoBatches(List source, int batchSize) + { + for (var i = 0; i < source.Count; i += batchSize) + { + yield return source.GetRange(i, Math.Min(batchSize, source.Count - i)); + } + } + + private async Task<(BinanceSocketClient connection, bool subscribeSymbolsAsync)> + TrySubscribeBatchesAsync(List symbols, BinanceSocketClient socketConnection, CancellationToken cancellationToken) { - var result = await socketConnection.SpotApi.SubscribeToBookPriceUpdatesAsync(symbol, HandlerAction, cancellationToken); + var result = await socketConnection.SpotApi.ExchangeData.SubscribeToOrderBookUpdatesAsync(symbols, 1, HandlerAction, cancellationToken).ConfigureAwait(false); if (!result.Success) { @@ -102,32 +111,34 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok { // try create new connection and subscribing socketConnection = GetConnection(); - result = await socketConnection.SpotApi.SubscribeToBookPriceUpdatesAsync(symbol, HandlerAction, cancellationToken); + result = await socketConnection.SpotApi.ExchangeData.SubscribeToOrderBookUpdatesAsync(symbols, 1, HandlerAction, cancellationToken); if (result.Success) { return (socketConnection, true); } - _logger.LogWarning(LogPrefix + " Not subscribe on ticker: {Symbol}", symbol); + _logger.LogWarning(LogPrefix + " Not subscribe on ticker: {Symbols}", string.Join(',', symbols)); return (socketConnection, false); } - _logger.LogWarning(LogPrefix + " Not subscribe on tickers: {Symbols}", symbol); + _logger.LogWarning(LogPrefix + " Not subscribe on tickers: {Symbols}", string.Join(',', symbols)); return (socketConnection, false); } - _logger.LogInformation(LogPrefix + " subscribed success, connection: {Number}, {Symbols}", _sockets.Count, symbol); + _logger.LogInformation(LogPrefix + " subscribed success, connection: {Number}, {Symbols}", _sockets.Count, string.Join(',', symbols)); return (socketConnection, true); } + private void HandlerAction(DataEvent @event) + { + throw new NotImplementedException(); + } private BinanceSocketClient GetConnection() { var connection = new BinanceSocketClient(_options, _loggerFactory); - connection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey) - }); + connection.SetApiCredentials(new BinanceCredentials(_provider.AccessKey, _provider.SecretKey)); + _sockets.Add(connection); return connection; } @@ -137,7 +148,9 @@ private async Task LoadSymbolsAsync(CancellationToken cancellationToken) { try { - var exchangeInfo = await _restClient.SpotApi.ExchangeData.GetSymbolsAsync(ct: cancellationToken); + + var exchangeInfo = await _restClient.SpotApi.SharedClient.GetSpotSymbolsAsync( + new GetSymbolsRequest(tradingMode:TradingMode.Spot), ct: cancellationToken); if (!exchangeInfo.Success) { @@ -147,7 +160,8 @@ private async Task LoadSymbolsAsync(CancellationToken cancellationToken) foreach (var symbol in exchangeInfo.Data) { - if (symbol.Status != SymbolStatus.Online) + + if (!symbol.Trading) { continue; } @@ -168,7 +182,7 @@ private async Task LoadSymbolsAsync(CancellationToken cancellationToken) /// Handler event change book of currency /// /// Data of event - private void HandlerAction(DataEventBinanceBookTickerUpdate> dataEvent) + private void HandlerAction(DataEvent dataEvent) { var askPrice = 0m; var bidPrice = 0m; diff --git a/sources/integrations/Crypto.Comapre.Binance/Consts.cs b/sources/integrations/Crypto.Comapre.Binance/Consts.cs index b414d0c..256751a 100644 --- a/sources/integrations/Crypto.Comapre.Binance/Consts.cs +++ b/sources/integrations/Crypto.Comapre.Binance/Consts.cs @@ -2,5 +2,5 @@ public class Consts { - public const string ProviderName = "BingX"; + public const string ProviderName = "Binance"; } \ No newline at end of file diff --git a/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj index b760144..91faeb8 100644 --- a/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj +++ b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj @@ -6,4 +6,12 @@ enable + + + + + + + + diff --git a/sources/integrations/Crypto.Compare.BingX/BingXOrderBookListener.cs b/sources/integrations/Crypto.Compare.BingX/BingXOrderBookListener.cs index ad8236a..d250266 100644 --- a/sources/integrations/Crypto.Compare.BingX/BingXOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.BingX/BingXOrderBookListener.cs @@ -1,4 +1,5 @@ -using BingX.Net.Clients; +using BingX.Net; +using BingX.Net.Clients; using BingX.Net.Enums; using BingX.Net.Interfaces.Clients; using BingX.Net.Objects.Models; @@ -26,8 +27,8 @@ public class BingXOrderBookListener : IListenerAdapter private readonly IBingXSocketClient _socketClient; private readonly IBingXRestClient _restClient; private readonly ILogger _logger; - private readonly SortedDictionary _symbols = new (); - private readonly List _sockets = new (); + private readonly SortedDictionary _symbols = new(); + private readonly List _sockets = new(); private readonly ILoggerFactory _loggerFactory; private readonly IOptions _options; @@ -120,10 +121,8 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private BingXSocketClient GetConnection() { var connection = new BingXSocketClient(_options, _loggerFactory); - connection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey) - }); + connection.SetApiCredentials(new BingXCredentials(_provider.AccessKey, _provider.SecretKey)); + _sockets.Add(connection); return connection; } diff --git a/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj b/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj index 1941b03..424ef34 100644 --- a/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj +++ b/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.BitMart/BitMartOrderBookListener.cs b/sources/integrations/Crypto.Compare.BitMart/BitMartOrderBookListener.cs index 1645169..c615bdb 100644 --- a/sources/integrations/Crypto.Compare.BitMart/BitMartOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.BitMart/BitMartOrderBookListener.cs @@ -120,10 +120,7 @@ protected override async Task SubscribeSymbolsAsync(CancellationToken canc private BitMartSocketClient GetConnection() { var connection = new BitMartSocketClient(_options, _loggerFactory); - connection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(Provider.AccessKey, Provider.SecretKey, Provider.PasswordKey) - }); + _sockets.Add(connection); return connection; } diff --git a/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj b/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj index 3a30df7..e3e7a76 100644 --- a/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj +++ b/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj @@ -12,7 +12,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.Bitget/BitgetOrderBookListener.cs b/sources/integrations/Crypto.Compare.Bitget/BitgetOrderBookListener.cs index be0804f..0db4d82 100644 --- a/sources/integrations/Crypto.Compare.Bitget/BitgetOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.Bitget/BitgetOrderBookListener.cs @@ -26,7 +26,7 @@ public class BitgetOrderBookListener : IListenerAdapter private readonly IBitgetRestClient _restClient; private readonly ILogger _logger; private readonly Dictionary _symbols = new Dictionary(); - private readonly List _sockets = new (); + private readonly List _sockets = new(); private readonly ILoggerFactory _loggerFactory; private readonly IOptions _options; @@ -43,7 +43,7 @@ public BitgetOrderBookListener(IBitgetSocketClient bitgetSocketClient, IBitgetRe public async Task StartAsync(Provider provider, CancellationToken cancellationToken) { - + _provider = provider; // get all symbols @@ -121,23 +121,11 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private BitgetSocketClient GetConnection() { var connection = new BitgetSocketClient(_options, _loggerFactory); - connection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey, _provider.PasswordKey) - }); + _sockets.Add(connection); return connection; } - - private static IEnumerable> SplitIntoBatches(List source, int batchSize) - { - for (var i = 0; i < source.Count; i += batchSize) - { - yield return source.GetRange(i, Math.Min(batchSize, source.Count - i)); - } - } - private async Task LoadSymbolsAsync(CancellationToken cancellationToken) { try diff --git a/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj b/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj index ddcf034..652f48a 100644 --- a/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj +++ b/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.ByBit/ByBitOrderBookListener.cs b/sources/integrations/Crypto.Compare.ByBit/ByBitOrderBookListener.cs index 5e778fc..5b8f9bb 100644 --- a/sources/integrations/Crypto.Compare.ByBit/ByBitOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.ByBit/ByBitOrderBookListener.cs @@ -1,4 +1,5 @@ -using Bybit.Net.Clients; +using Bybit.Net; +using Bybit.Net.Clients; using Bybit.Net.Enums; using Bybit.Net.Interfaces.Clients; using Bybit.Net.Objects.Models.V5; @@ -27,7 +28,7 @@ public class ByBitOrderBookListener : IListenerAdapter private readonly IBybitRestClient _restClient; private readonly ILogger _logger; private readonly IDictionary _symbols = new Dictionary(); - private readonly List _sockets = new (); + private readonly List _sockets = new(); private readonly ILoggerFactory _loggerFactory; private readonly IOptions _options; @@ -91,7 +92,7 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private async Task<(BybitSocketClient bybitConnection, bool subscribeSymbolsAsync)> TrySubscribeBatchesAsync(CancellationToken cancellationToken, List symbols, BybitSocketClient bybitConnection) { - var result = await bybitConnection.V5SpotApi.SubscribeToOrderbookUpdatesAsync(symbols, 1, HandlerAction, cancellationToken); + var result = await bybitConnection.V5SpotApi.SubscribeToOrderbookUpdatesAsync(symbols, 100, HandlerAction, cancellationToken); if (!result.Success) { @@ -99,7 +100,7 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok { // try create new connection and subscribing bybitConnection = GetConnection(); - result = await bybitConnection.V5SpotApi.SubscribeToOrderbookUpdatesAsync(symbols, 1, HandlerAction, cancellationToken); + result = await bybitConnection.V5SpotApi.SubscribeToOrderbookUpdatesAsync(symbols, 100, HandlerAction, cancellationToken); if (result.Success) { return (bybitConnection, true); @@ -120,10 +121,8 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private BybitSocketClient GetConnection() { var connection = new BybitSocketClient(_options, _loggerFactory); - connection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey) - }); + connection.SetApiCredentials(new BybitCredentials(_provider.AccessKey, _provider.SecretKey)); + _sockets.Add(connection); return connection; } diff --git a/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj b/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj index c264857..a995d8c 100644 --- a/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj +++ b/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj b/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj index 13f1932..33d9141 100644 --- a/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj +++ b/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.GateIo/GateIoOrderBookListener.cs b/sources/integrations/Crypto.Compare.GateIo/GateIoOrderBookListener.cs index 015db04..7229efc 100644 --- a/sources/integrations/Crypto.Compare.GateIo/GateIoOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.GateIo/GateIoOrderBookListener.cs @@ -6,6 +6,7 @@ using CryptoExchange.Net.Objects.Errors; using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; +using GateIo.Net; using GateIo.Net.Clients; using GateIo.Net.Enums; using GateIo.Net.Interfaces.Clients; @@ -119,10 +120,11 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private GateIoSocketClient GetConnection() { var connection = new GateIoSocketClient(_options, _loggerFactory); - connection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey, _provider.PasswordKey) - }); + connection.SetApiCredentials(new GateIoCredentials(_provider.AccessKey, _provider.SecretKey)); + //connection.SetOptions(new UpdateOptions + //{ + // ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey, _provider.PasswordKey) + //}); _sockets.Add(connection); return connection; } diff --git a/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj b/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj index 4a34681..5baed1e 100644 --- a/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj +++ b/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj @@ -11,7 +11,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.Mexc/MexcOrderBookListener.cs b/sources/integrations/Crypto.Compare.Mexc/MexcOrderBookListener.cs index eda55cf..ebfcb42 100644 --- a/sources/integrations/Crypto.Compare.Mexc/MexcOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.Mexc/MexcOrderBookListener.cs @@ -6,6 +6,7 @@ using CryptoExchange.Net.Objects.Errors; using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; +using Mexc.Net; using Mexc.Net.Clients; using Mexc.Net.Enums; using Mexc.Net.Interfaces.Clients; @@ -119,10 +120,7 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private MexcSocketClient GetConnection() { var mexcConnection = new MexcSocketClient(_options, _loggerFactory); - mexcConnection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey) - }); + mexcConnection.SetApiCredentials(new MexcCredentials(_provider.AccessKey, _provider.SecretKey)); _mexcSockets.Add(mexcConnection); return mexcConnection; } diff --git a/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj b/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj index 700b47f..1b52b6d 100644 --- a/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj +++ b/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj @@ -10,7 +10,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.Xtcom/XtcomOrderBookListener.cs b/sources/integrations/Crypto.Compare.Xtcom/XtcomOrderBookListener.cs index e81122c..10b2952 100644 --- a/sources/integrations/Crypto.Compare.Xtcom/XtcomOrderBookListener.cs +++ b/sources/integrations/Crypto.Compare.Xtcom/XtcomOrderBookListener.cs @@ -7,6 +7,7 @@ using CryptoExchange.Net.Objects.Sockets; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using XT.Net; using XT.Net.Clients; using XT.Net.Enums; using XT.Net.Interfaces.Clients; @@ -23,7 +24,7 @@ public class XtcomOrderBookListener : IListenerAdapter private readonly IXTSocketClient _xtSocketClient; private readonly IXTRestClient _restClient; private readonly ILogger _logger; - private readonly SortedDictionary _symbols = new (); + private readonly SortedDictionary _symbols = new(); private readonly List _xtSockets = new(); private readonly ILoggerFactory _loggerFactory; private readonly IOptions _options; @@ -65,7 +66,7 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok { var xtConnection = GetConnection(); - var keys = _symbols.Values.Select(x=>x.Symbol).ToList(); + var keys = _symbols.Values.Select(x => x.Symbol).ToList(); foreach (var symbols in SplitIntoBatches(keys, 15)) { (xtConnection, var result) = await TrySubscribeBatchesAsync(cancellationToken, symbols, xtConnection); @@ -117,10 +118,8 @@ private async Task SubscribeSymbolsAsync(CancellationToken cancellationTok private XTSocketClient GetConnection() { var xtConnection = new XTSocketClient(_options, _loggerFactory); - xtConnection.SetOptions(new UpdateOptions - { - ApiCredentials = new ApiCredentials(_provider.AccessKey, _provider.SecretKey) - }); + xtConnection.SetApiCredentials(new XTCredentials(_provider.AccessKey, _provider.SecretKey)); + _xtSockets.Add(xtConnection); return xtConnection; } @@ -176,7 +175,7 @@ private void HandlerAction(DataEvent dataEvent) var bidPrice = 0m; var askQuantity = 0m; var bidQuantity = 0m; - + if (string.IsNullOrEmpty(dataEvent.Symbol)) { return; @@ -187,7 +186,7 @@ private void HandlerAction(DataEvent dataEvent) _logger.LogWarning("Symbol not found: {Symbol}", dataEvent.Symbol); return; } - + if (dataEvent.Data?.Asks.Length > 0) { askPrice = dataEvent.Data.Asks[0].Price; diff --git a/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj b/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj index e88ea21..7dfeae4 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj +++ b/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj @@ -11,17 +11,17 @@ - + - - - + + + - - + + @@ -36,6 +36,7 @@ + diff --git a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs index 64ea9f7..f15bb4a 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs @@ -1,4 +1,9 @@ -using Crypto.Compare.Adapter; +using Binance.Net; +using BingX.Net; +using Bitget.Net; +using BitMart.Net; +using Bybit.Net; +using Crypto.Compare.Adapter; using Crypto.Compare.Adapter.AdaptersObservable; using Crypto.Compare.Adapter.Impl; using Crypto.Compare.BingX; @@ -33,11 +38,14 @@ using Crypto.Compare.Xtcom; using CryptoExchange.Net.Authentication; using FluentResults; +using GateIo.Net; using Mapster; using MapsterMapper; using MediatR; +using Mexc.Net; using Microsoft.EntityFrameworkCore; using System.Reflection; +using XT.Net; namespace Crypto.Compare.PublicApi.Ioc; @@ -106,7 +114,7 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, if (!string.IsNullOrEmpty(apiSecret) && !string.IsNullOrEmpty(apiKey)) { - services.AddMexc(options => { options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret); }); + services.AddMexc(options => { options.Socket.ApiCredentials = new MexcCredentials(apiKey, apiSecret); }); } else { @@ -118,8 +126,8 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, { services.AddXT(options => { - options.ApiCredentials = new ApiCredentials(apiKey, apiSecret); - options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret); + options.ApiCredentials = new XTCredentials(apiKey, apiSecret); + options.Socket.ApiCredentials = new XTCredentials(apiKey, apiSecret); options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); }); } @@ -133,8 +141,8 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, { services.AddBybit(options => { - options.ApiCredentials = new ApiCredentials(apiKey, apiSecret); - options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret); + options.ApiCredentials = new BybitCredentials(apiKey, apiSecret); + options.Socket.ApiCredentials = new BybitCredentials(apiKey, apiSecret); options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); }); } @@ -148,8 +156,8 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, { services.AddBingX(options => { - options.ApiCredentials = new ApiCredentials(apiKey, apiSecret); - options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret); + options.ApiCredentials = new BingXCredentials(apiKey, apiSecret); + options.Socket.ApiCredentials = new BingXCredentials(apiKey, apiSecret); options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); }); } @@ -163,8 +171,8 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, { services.AddBitget(options => { - options.ApiCredentials = new ApiCredentials(apiKey, apiSecret, passwordKey); - options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret, passwordKey); + options.ApiCredentials = new BitgetCredentials(apiKey, apiSecret, passwordKey); + options.Socket.ApiCredentials = new BitgetCredentials(apiKey, apiSecret, passwordKey); options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); }); } @@ -178,8 +186,8 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, { services.AddGateIo(options => { - options.ApiCredentials = new ApiCredentials(apiKey, apiSecret); - options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret); + options.ApiCredentials = new GateIoCredentials(apiKey, apiSecret); + options.Socket.ApiCredentials = new GateIoCredentials(apiKey, apiSecret); options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); }); } @@ -194,8 +202,8 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, { services.AddBitMart(options => { - options.ApiCredentials = new ApiCredentials(apiKey, apiSecret, passwordKey); - options.Socket.ApiCredentials = new ApiCredentials(apiKey, apiSecret, passwordKey); + options.ApiCredentials = new BitMartCredentials(apiKey, apiSecret, passwordKey); + options.Socket.ApiCredentials = new BitMartCredentials(apiKey, apiSecret, passwordKey); options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); }); } @@ -204,6 +212,20 @@ private static IServiceCollection AddAdapters(this IServiceCollection services, services.AddBitMart(); } + (apiKey, apiSecret, _) = LoadCredentialsSync("binance", configuration); + if (!string.IsNullOrEmpty(apiSecret) && !string.IsNullOrEmpty(apiKey)) + { + services.AddBinance(options => + { + options.ApiCredentials = new BinanceCredentials(apiKey, apiSecret); + options.Socket.ApiCredentials = new BinanceCredentials(apiKey, apiSecret); + options.Rest.RequestTimeout = TimeSpan.FromMinutes(5); + }); + } + else + { + services.AddBinance(); + } return services; } diff --git a/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj b/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj index aafbce2..d3f6736 100644 --- a/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj +++ b/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj @@ -10,11 +10,11 @@ - + - - - + + + all From a6b72cd85922f265fbd7c609bb9f0daac71c6e97 Mon Sep 17 00:00:00 2001 From: Dmitry Tarakanov Date: Mon, 27 Apr 2026 21:35:07 +0300 Subject: [PATCH 3/6] #23 rename old endpoint to the best price packages were updated --- .../Crypto.Compare.Services.csproj | 2 +- ....cs => GetSymbolsBestPriceQueryHandler.cs} | 8 ++-- ...lsQuery.cs => GetSymbolsBestPriceQuery.cs} | 4 +- .../Crypto.Compare.DataAccess.csproj | 4 +- .../Crypto.Comapre.Binance.csproj | 2 +- .../Crypto.Compare.BingX.csproj | 2 +- .../Crypto.Compare.BitMart.csproj | 2 +- .../Crypto.Compare.Bitget.csproj | 2 +- .../Crypto.Compare.ByBit.csproj | 2 +- .../Crypto.Compare.GateIo.csproj | 2 +- .../Crypto.Compare.Mexc.csproj | 3 +- .../Crypto.Compare.Xtcom.csproj | 2 +- .../Controllers/SymbolsController.cs | 37 ++++++++++++++----- .../Crypto.Compare.PublicApi.csproj | 10 ++--- .../Dtos/{SymbolDto.cs => SymbolBestDto.cs} | 2 +- .../Ioc/ServicesRegistry.cs | 2 +- .../Mapping/MapsterProfile.cs | 4 +- .../v1/Symbols/GetSymbolsBestResponse.cs | 7 ++++ .../v1/Symbols/GetSymbolsResponse.cs | 8 ---- ...le.cs => GetSymbolsBestResponseExample.cs} | 6 +-- .../Crypto.Compare.PublicApi.Tests.csproj | 12 +++--- .../Crypto.Compare.Services.Tests.csproj | 6 +-- 22 files changed, 73 insertions(+), 56 deletions(-) rename sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/{GetSymbolsQueryHandler.cs => GetSymbolsBestPriceQueryHandler.cs} (86%) rename sources/core/Crypto.Compare.Services/Queries/Symbols/{GetSymbolsQuery.cs => GetSymbolsBestPriceQuery.cs} (78%) rename sources/presentation/Crypto.Compare.PublicApi/Dtos/{SymbolDto.cs => SymbolBestDto.cs} (97%) create mode 100644 sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsBestResponse.cs delete mode 100644 sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs rename sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/{GetSymbolsResponseExample.cs => GetSymbolsBestResponseExample.cs} (82%) diff --git a/sources/core/Crypto.Compare.Services/Crypto.Compare.Services.csproj b/sources/core/Crypto.Compare.Services/Crypto.Compare.Services.csproj index aa8f946..2bb644a 100644 --- a/sources/core/Crypto.Compare.Services/Crypto.Compare.Services.csproj +++ b/sources/core/Crypto.Compare.Services/Crypto.Compare.Services.csproj @@ -12,7 +12,7 @@ - + diff --git a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs similarity index 86% rename from sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs rename to sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs index 4d54b67..df46c1a 100644 --- a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs +++ b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs @@ -7,20 +7,20 @@ namespace Crypto.Compare.Services.Handlers.Queries.Symbols; -public class GetSymbolsQueryHandler : IRequestHandler> +public class GetSymbolsBestPriceQueryHandler : IRequestHandler> { private readonly CryptoCompareContext _cryptoCompareContext; - public GetSymbolsQueryHandler(CryptoCompareContext cryptoCompareContext) + public GetSymbolsBestPriceQueryHandler(CryptoCompareContext cryptoCompareContext) { _cryptoCompareContext = cryptoCompareContext; } - public async Task> Handle(GetSymbolsQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetSymbolsBestPriceQuery request, CancellationToken cancellationToken) { var query = _cryptoCompareContext .Symbols - .Include(x=>x.SymbolProviders) + .Include(x => x.SymbolProviders) .AsQueryable() .AsNoTracking(); diff --git a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs similarity index 78% rename from sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs rename to sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs index cfc3a9f..9fa5413 100644 --- a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs @@ -4,12 +4,12 @@ namespace Crypto.Compare.Services.Queries.Symbols; -public class GetSymbolsQuery : IRequest> +public class GetSymbolsBestPriceQuery : IRequest> { private const int MaxRows = 30; private const int DefaultRows = 10; - public GetSymbolsQuery(string? ticker = null, long? skip = 0, int? rows = DefaultRows) + public GetSymbolsBestPriceQuery(string? ticker = null, long? skip = 0, int? rows = DefaultRows) { Ticker = ticker?.ToLowerInvariant(); Skip = skip ?? 0; diff --git a/sources/infrastructure/Crypto.Compare.DataAccess/Crypto.Compare.DataAccess.csproj b/sources/infrastructure/Crypto.Compare.DataAccess/Crypto.Compare.DataAccess.csproj index a40eccd..f1f699e 100644 --- a/sources/infrastructure/Crypto.Compare.DataAccess/Crypto.Compare.DataAccess.csproj +++ b/sources/infrastructure/Crypto.Compare.DataAccess/Crypto.Compare.DataAccess.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj index 91faeb8..ed3797d 100644 --- a/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj +++ b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj b/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj index 424ef34..fef50f8 100644 --- a/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj +++ b/sources/integrations/Crypto.Compare.BingX/Crypto.Compare.BingX.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj b/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj index e3e7a76..cbf2318 100644 --- a/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj +++ b/sources/integrations/Crypto.Compare.BitMart/Crypto.Compare.BitMart.csproj @@ -12,7 +12,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj b/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj index 652f48a..7ba9ad7 100644 --- a/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj +++ b/sources/integrations/Crypto.Compare.Bitget/Crypto.Compare.Bitget.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj b/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj index a995d8c..0eaa9a3 100644 --- a/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj +++ b/sources/integrations/Crypto.Compare.ByBit/Crypto.Compare.ByBit.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj b/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj index 33d9141..0e3ed04 100644 --- a/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj +++ b/sources/integrations/Crypto.Compare.GateIo/Crypto.Compare.GateIo.csproj @@ -7,7 +7,7 @@ - + diff --git a/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj b/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj index 5baed1e..78328eb 100644 --- a/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj +++ b/sources/integrations/Crypto.Compare.Mexc/Crypto.Compare.Mexc.csproj @@ -11,7 +11,8 @@ - + + diff --git a/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj b/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj index 1b52b6d..d60dc53 100644 --- a/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj +++ b/sources/integrations/Crypto.Compare.Xtcom/Crypto.Compare.Xtcom.csproj @@ -10,7 +10,7 @@ - + diff --git a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs index 46d6744..1e204b0 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs @@ -41,17 +41,17 @@ public SymbolsProvider(IMediator mediator, IResponseMapper responseMapper) /// Cancellation token [HttpGet("")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get all list of symbols", typeof(GetSymbolsResponse))] - [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsResponseExample))] + [SwaggerResponse(StatusCodes.Status200OK, "Get all list of symbols", typeof(GetSymbolsBestResponse))] + [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] public async Task GetSymbols([FromQuery] int? skip, [FromQuery] int? rows, CancellationToken cancellationToken) { - var command = new GetSymbolsQuery( + var command = new GetSymbolsBestPriceQuery( skip: skip, rows: rows); var result = await _mediator.Send(command, cancellationToken); - return _responseMapper.ToCustomResponse(result); + return _responseMapper.ToCustomResponse(result); } /// @@ -63,8 +63,8 @@ public async Task GetSymbols([FromQuery] int? skip, [FromQuery] i /// Cancellation token [HttpGet("provider/{providerId}")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols for provider", typeof(GetSymbolsResponse))] - [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsResponseExample))] + [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols for provider", typeof(GetSymbolsBestResponse))] + [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] public async Task GetSymbolsByProvider([FromRoute] int providerId, [FromQuery] int? skip, [FromQuery] int? rows, CancellationToken cancellationToken) { var command = new GetSymbolProvidersQuery(providerId: providerId, @@ -93,19 +93,36 @@ public async Task GetSymbolsByProvider([FromRoute] long id, Cance } /// - /// Get list of symbols + /// Get list of symbols with best price + /// + /// Ticker of symbols + /// Cancellation token + [HttpGet("ticker/best/{ticker}")] + [Produces(MediaTypeNames.Application.Json)] + [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols with best price", typeof(GetSymbolsBestResponse))] + [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] + public async Task GetSymbolsBestByTicker([FromRoute] string ticker, CancellationToken cancellationToken) + { + var command = new GetSymbolsBestPriceQuery(ticker: ticker); + var result = await _mediator.Send(command, cancellationToken); + + return _responseMapper.ToCustomResponse(result); + } + + /// + /// Get list of symbols by ticker /// /// Ticker of symbols /// Cancellation token [HttpGet("ticker/{ticker}")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols", typeof(GetSymbolsResponse))] - [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsResponseExample))] + [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols by ticker", typeof(GetSymbolsBestResponse))] + [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] public async Task GetSymbolsByTicker([FromRoute] string ticker, CancellationToken cancellationToken) { var command = new GetSymbolsQuery(ticker: ticker); var result = await _mediator.Send(command, cancellationToken); - return _responseMapper.ToCustomResponse(result); + return _responseMapper.ToCustomResponse(result); } } diff --git a/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj b/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj index 7dfeae4..b6bac22 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj +++ b/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj @@ -11,13 +11,13 @@ - + - - - - + + + + diff --git a/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolBestDto.cs similarity index 97% rename from sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs rename to sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolBestDto.cs index 365b478..1bd8d9a 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolBestDto.cs @@ -1,6 +1,6 @@ namespace Crypto.Compare.PublicApi.Dtos; -public class SymbolDto +public class SymbolBestDto { /// /// Uniq id diff --git a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs index f15bb4a..d57ce22 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs @@ -283,7 +283,7 @@ private static IServiceCollection AddMediator(this IServiceCollection services) .AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetCallingAssembly())) .AddScoped>, GetSymbolQueryHandler>() - .AddScoped>, GetSymbolsQueryHandler>() + .AddScoped>, GetSymbolsBestPriceQueryHandler>() .AddScoped>, GetProviderQueryHandler>() .AddScoped>, GetProvidersQueryHandler>() .AddScoped>, GetSymbolProvidersQueryHandler>() diff --git a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs index 1c08951..1202ce7 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs @@ -16,9 +16,9 @@ public void Register(TypeAdapterConfig config) { // Простые маппинги (аналогичны CreateMap) config.NewConfig(); - config.NewConfig(); + config.NewConfig(); config.NewConfig(); - config.NewConfig(); + config.NewConfig(); config.NewConfig(); config.NewConfig(); diff --git a/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsBestResponse.cs b/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsBestResponse.cs new file mode 100644 index 0000000..c92a3ae --- /dev/null +++ b/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsBestResponse.cs @@ -0,0 +1,7 @@ +using Crypto.Compare.PublicApi.Dtos; +namespace Crypto.Compare.PublicApi.Responses.v1.Symbols; + +public class GetSymbolsBestResponse : BaseApiResponse +{ + public List Symbols { get; set; } +} \ No newline at end of file diff --git a/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs b/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs deleted file mode 100644 index b5a3c00..0000000 --- a/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Crypto.Compare.PublicApi.Dtos; - -namespace Crypto.Compare.PublicApi.Responses.v1.Symbols; - -public class GetSymbolsResponse : BaseApiResponse -{ - public List Symbols { get; set; } -} \ No newline at end of file diff --git a/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsBestResponseExample.cs similarity index 82% rename from sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs rename to sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsBestResponseExample.cs index b0e620b..69b8b45 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsBestResponseExample.cs @@ -4,12 +4,12 @@ namespace Crypto.Compare.PublicApi.Swaggers.Responses.v1.Symbols; -public class GetSymbolsResponseExample : IExamplesProvider +public class GetSymbolsBestResponseExample : IExamplesProvider { - public GetSymbolsResponse GetExamples() + public GetSymbolsBestResponse GetExamples() => new() { - Symbols = new List + Symbols = new List { new() { diff --git a/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj b/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj index d3f6736..6dc868b 100644 --- a/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj +++ b/tests/Crypto.Compare.PublicApi.Tests/Crypto.Compare.PublicApi.Tests.csproj @@ -10,13 +10,13 @@ - + - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Crypto.Compare.Services.Tests/Crypto.Compare.Services.Tests.csproj b/tests/Crypto.Compare.Services.Tests/Crypto.Compare.Services.Tests.csproj index 4333e04..fe5719b 100644 --- a/tests/Crypto.Compare.Services.Tests/Crypto.Compare.Services.Tests.csproj +++ b/tests/Crypto.Compare.Services.Tests/Crypto.Compare.Services.Tests.csproj @@ -9,9 +9,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 3a1456d14d07106e4997f7b0028456fac2972d80 Mon Sep 17 00:00:00 2001 From: Dmitry Tarakanov Date: Tue, 28 Apr 2026 21:29:18 +0300 Subject: [PATCH 4/6] #23 chnage old models --- .../Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs | 6 +++--- .../Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs | 10 ++++++++++ .../Queries/Symbols/GetSymbolsBestPriceQuery.cs | 2 +- .../Queries/Symbols/GetSymbolsQuery.cs | 3 +++ .../Results/Symbols/GetSymbolsBestPriceResult.cs | 6 ++++++ .../Results/Symbols/GetSymbolsResult.cs | 10 ++++++---- .../Controllers/SymbolsController.cs | 4 ++-- .../Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs | 2 +- .../Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs | 2 +- 9 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs create mode 100644 sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs create mode 100644 sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsBestPriceResult.cs diff --git a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs index df46c1a..b0c701b 100644 --- a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs +++ b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs @@ -7,7 +7,7 @@ namespace Crypto.Compare.Services.Handlers.Queries.Symbols; -public class GetSymbolsBestPriceQueryHandler : IRequestHandler> +public class GetSymbolsBestPriceQueryHandler : IRequestHandler> { private readonly CryptoCompareContext _cryptoCompareContext; @@ -16,7 +16,7 @@ public GetSymbolsBestPriceQueryHandler(CryptoCompareContext cryptoCompareContext _cryptoCompareContext = cryptoCompareContext; } - public async Task> Handle(GetSymbolsBestPriceQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetSymbolsBestPriceQuery request, CancellationToken cancellationToken) { var query = _cryptoCompareContext .Symbols @@ -57,7 +57,7 @@ public async Task> Handle(GetSymbolsBestPriceQuery requ .Take(request.Rows) .ToListAsync(cancellationToken); - return new GetSymbolsResult + return new GetSymbolsBestPriceResult { Symbols = symbols }; diff --git a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs new file mode 100644 index 0000000..bd7bce3 --- /dev/null +++ b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs @@ -0,0 +1,10 @@ +using Crypto.Compare.Services.Queries.Symbols; +using Crypto.Compare.Services.Results.Symbols; +using FluentResults; +using MediatR; + +namespace Crypto.Compare.Services.Handlers.Queries.Symbols; + +internal class GetSymbolsQueryHandler : IRequestHandler> +{ +} diff --git a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs index 9fa5413..51d113c 100644 --- a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs @@ -4,7 +4,7 @@ namespace Crypto.Compare.Services.Queries.Symbols; -public class GetSymbolsBestPriceQuery : IRequest> +public class GetSymbolsBestPriceQuery : IRequest> { private const int MaxRows = 30; private const int DefaultRows = 10; diff --git a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs new file mode 100644 index 0000000..5cb70ca --- /dev/null +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs @@ -0,0 +1,3 @@ +namespace Crypto.Compare.Services.Queries.Symbols; + +internal record GetSymbolsQuery(string Ticker); diff --git a/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsBestPriceResult.cs b/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsBestPriceResult.cs new file mode 100644 index 0000000..cc4c86a --- /dev/null +++ b/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsBestPriceResult.cs @@ -0,0 +1,6 @@ +namespace Crypto.Compare.Services.Results.Symbols; + +public class GetSymbolsBestPriceResult : IHandlerResult +{ + public List Symbols { get; set; } +} \ No newline at end of file diff --git a/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs b/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs index 9ee52e2..98cfb45 100644 --- a/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs +++ b/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs @@ -1,6 +1,8 @@ -namespace Crypto.Compare.Services.Results.Symbols; +using Crypto.Compare.Data; -public class GetSymbolsResult : IHandlerResult +namespace Crypto.Compare.Services.Results.Symbols; + +internal class GetSymbolsResult { - public List Symbols { get; set; } -} \ No newline at end of file + public List Symbols { get; set; } +} diff --git a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs index 1e204b0..43bdf2b 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs @@ -51,7 +51,7 @@ public async Task GetSymbols([FromQuery] int? skip, [FromQuery] i var result = await _mediator.Send(command, cancellationToken); - return _responseMapper.ToCustomResponse(result); + return _responseMapper.ToCustomResponse(result); } /// @@ -106,7 +106,7 @@ public async Task GetSymbolsBestByTicker([FromRoute] string ticke var command = new GetSymbolsBestPriceQuery(ticker: ticker); var result = await _mediator.Send(command, cancellationToken); - return _responseMapper.ToCustomResponse(result); + return _responseMapper.ToCustomResponse(result); } /// diff --git a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs index d57ce22..870993d 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs @@ -283,7 +283,7 @@ private static IServiceCollection AddMediator(this IServiceCollection services) .AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetCallingAssembly())) .AddScoped>, GetSymbolQueryHandler>() - .AddScoped>, GetSymbolsBestPriceQueryHandler>() + .AddScoped>, GetSymbolsBestPriceQueryHandler>() .AddScoped>, GetProviderQueryHandler>() .AddScoped>, GetProvidersQueryHandler>() .AddScoped>, GetSymbolProvidersQueryHandler>() diff --git a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs index 1202ce7..262c44c 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs @@ -18,7 +18,7 @@ public void Register(TypeAdapterConfig config) config.NewConfig(); config.NewConfig(); config.NewConfig(); - config.NewConfig(); + config.NewConfig(); config.NewConfig(); config.NewConfig(); From 088b2eaadbae9a94af67ef9bc77e68e6ded01a8b Mon Sep 17 00:00:00 2001 From: Dmitry Tarakanov Date: Wed, 29 Apr 2026 20:17:29 +0300 Subject: [PATCH 5/6] #22 correct result to controller --- .../Queries/Symbols/GetSymbolsQueryHandler.cs | 29 ++++++++++++-- .../Queries/Symbols/GetSymbolsQuery.cs | 9 ++++- .../Results/Symbols/GetSymbolsResult.cs | 2 +- .../Controllers/SymbolsController.cs | 16 ++++---- .../Dtos/SymbolDto.cs | 39 +++++++++++++++++++ .../Ioc/ServicesRegistry.cs | 2 +- .../Mapping/MapsterProfile.cs | 7 +++- .../v1/Symbols/GetSymbolsResponse.cs | 8 ++++ .../v1/Symbols/GetSymbolsResponseExample.cs | 34 ++++++++++++++++ 9 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs create mode 100644 sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs create mode 100644 sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs diff --git a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs index bd7bce3..37d8209 100644 --- a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs +++ b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs @@ -1,10 +1,33 @@ -using Crypto.Compare.Services.Queries.Symbols; +using Crypto.Compare.DataAccess; +using Crypto.Compare.Services.Queries.Symbols; using Crypto.Compare.Services.Results.Symbols; using FluentResults; using MediatR; +using Microsoft.EntityFrameworkCore; namespace Crypto.Compare.Services.Handlers.Queries.Symbols; -internal class GetSymbolsQueryHandler : IRequestHandler> +public class GetSymbolsQueryHandler : IRequestHandler> { -} + private readonly CryptoCompareContext _cryptoCompareContext; + + public GetSymbolsQueryHandler(CryptoCompareContext cryptoCompareContext) + { + _cryptoCompareContext = cryptoCompareContext; + } + + public async Task> Handle(GetSymbolsQuery request, CancellationToken cancellationToken) + { + return + new GetSymbolsResult() + { + Symbols = await _cryptoCompareContext + .SymbolProviders + .AsQueryable() + .AsNoTracking() + .Include(x => x.Symbol) + .Where(x => x.Symbol.Ticker == request.Ticker) + .ToListAsync(cancellationToken) + }; + } +} \ No newline at end of file diff --git a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs index 5cb70ca..9dbd30d 100644 --- a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs @@ -1,3 +1,8 @@ -namespace Crypto.Compare.Services.Queries.Symbols; +using Crypto.Compare.Data; +using Crypto.Compare.Services.Results.Symbols; +using FluentResults; +using MediatR; -internal record GetSymbolsQuery(string Ticker); +namespace Crypto.Compare.Services.Queries.Symbols; + +public record GetSymbolsQuery(string Ticker) : IRequest>; diff --git a/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs b/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs index 98cfb45..8652331 100644 --- a/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs +++ b/sources/core/Crypto.Compare.Services/Results/Symbols/GetSymbolsResult.cs @@ -2,7 +2,7 @@ namespace Crypto.Compare.Services.Results.Symbols; -internal class GetSymbolsResult +public class GetSymbolsResult { public List Symbols { get; set; } } diff --git a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs index 43bdf2b..d27b7ac 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs @@ -41,7 +41,7 @@ public SymbolsProvider(IMediator mediator, IResponseMapper responseMapper) /// Cancellation token [HttpGet("")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get all list of symbols", typeof(GetSymbolsBestResponse))] + [SwaggerResponse(StatusCodes.Status200OK, "Get all list of symbols", typeof(GetSymbolsResponse))] [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] public async Task GetSymbols([FromQuery] int? skip, [FromQuery] int? rows, CancellationToken cancellationToken) { @@ -51,7 +51,7 @@ public async Task GetSymbols([FromQuery] int? skip, [FromQuery] i var result = await _mediator.Send(command, cancellationToken); - return _responseMapper.ToCustomResponse(result); + return _responseMapper.ToCustomResponse(result); } /// @@ -63,7 +63,7 @@ public async Task GetSymbols([FromQuery] int? skip, [FromQuery] i /// Cancellation token [HttpGet("provider/{providerId}")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols for provider", typeof(GetSymbolsBestResponse))] + [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols for provider", typeof(GetSymbolsResponse))] [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] public async Task GetSymbolsByProvider([FromRoute] int providerId, [FromQuery] int? skip, [FromQuery] int? rows, CancellationToken cancellationToken) { @@ -99,7 +99,7 @@ public async Task GetSymbolsByProvider([FromRoute] long id, Cance /// Cancellation token [HttpGet("ticker/best/{ticker}")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols with best price", typeof(GetSymbolsBestResponse))] + [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols with best price", typeof(GetSymbolsResponse))] [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] public async Task GetSymbolsBestByTicker([FromRoute] string ticker, CancellationToken cancellationToken) { @@ -116,13 +116,13 @@ public async Task GetSymbolsBestByTicker([FromRoute] string ticke /// Cancellation token [HttpGet("ticker/{ticker}")] [Produces(MediaTypeNames.Application.Json)] - [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols by ticker", typeof(GetSymbolsBestResponse))] - [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsBestResponseExample))] + [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols by ticker", typeof(GetSymbolsResponse))] + [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsResponseExample))] public async Task GetSymbolsByTicker([FromRoute] string ticker, CancellationToken cancellationToken) { - var command = new GetSymbolsQuery(ticker: ticker); + var command = new GetSymbolsQuery(ticker); var result = await _mediator.Send(command, cancellationToken); - return _responseMapper.ToCustomResponse(result); + return _responseMapper.ToCustomResponse(result); } } diff --git a/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs new file mode 100644 index 0000000..271ed71 --- /dev/null +++ b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs @@ -0,0 +1,39 @@ +namespace Crypto.Compare.PublicApi.Dtos; + +public class SymbolDto +{ + /// + /// Uniq id + /// + public long Id { get; set; } + + /// + /// Ticker name + /// + public string Ticker { get; set; } + + /// + /// symbol or viewed name + /// + public string Symbol { get; set; } + + /// + /// Id provider + /// + public int ProviderId { get; set; } + + /// + /// Price sell + /// + public decimal PriceSell { get; set; } + + /// + /// Current sell price + /// + public decimal PriceBuy { get; set; } + + /// + /// Last date get quotes + /// + public DateTime UpdatedAt { get; set; } +} diff --git a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs index 870993d..792e723 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs @@ -287,7 +287,7 @@ private static IServiceCollection AddMediator(this IServiceCollection services) .AddScoped>, GetProviderQueryHandler>() .AddScoped>, GetProvidersQueryHandler>() .AddScoped>, GetSymbolProvidersQueryHandler>() - + .AddScoped>, GetSymbolsQueryHandler>() ; } } diff --git a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs index 262c44c..94104e1 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs @@ -16,9 +16,12 @@ public void Register(TypeAdapterConfig config) { // Простые маппинги (аналогичны CreateMap) config.NewConfig(); - config.NewConfig(); config.NewConfig(); - config.NewConfig(); + config.NewConfig(); + config.NewConfig(); + config.NewConfig(); + config.NewConfig(); + config.NewConfig(); config.NewConfig(); diff --git a/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs b/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs new file mode 100644 index 0000000..88d4ef9 --- /dev/null +++ b/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs @@ -0,0 +1,8 @@ +using Crypto.Compare.PublicApi.Dtos; + +namespace Crypto.Compare.PublicApi.Responses.v1.Symbols; + +public class GetSymbolsResponse : BaseApiResponse +{ + public List Symbols { get; set; } +} diff --git a/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs new file mode 100644 index 0000000..4b05307 --- /dev/null +++ b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs @@ -0,0 +1,34 @@ +using Crypto.Compare.PublicApi.Dtos; +using Crypto.Compare.PublicApi.Responses.v1.Symbols; +using Swashbuckle.AspNetCore.Filters; + +namespace Crypto.Compare.PublicApi.Swaggers.Responses.v1.Symbols; + +public class GetSymbolsResponseExample : IExamplesProvider +{ + public GetSymbolsResponse GetExamples() + => new() + { + Symbols = new List + { + new() + { + Id = 1, + Symbol = "ETHUSDT", + PriceSell = 82000, + PriceBuy = 79000, + ProviderId = 1, + Ticker = "ETHUSDT" + }, + new() + { + Id = 2, + Symbol = "ETHUSDT", + PriceSell = 3200, + PriceBuy = 3100, + ProviderId = 2, + Ticker = "ETHUSDT" + } + } + }; +} \ No newline at end of file From e79a3b0f9ac3a3d4f97896218415ec3c174ea8b3 Mon Sep 17 00:00:00 2001 From: Dmitry Tarakanov Date: Mon, 4 May 2026 21:48:08 +0300 Subject: [PATCH 6/6] #23 fix error start application --- .../Queries/Symbols/GetSymbolsQueryHandler.cs | 18 ++++++++++++++---- .../Queries/Symbols/GetSymbolsQuery.cs | 5 ++--- .../SymbolProviders/GetSymbolProviderResult.cs | 4 +--- .../Ioc/ServicesRegistry.cs | 3 ++- .../v1/Symbols/GetSymbolsResponseExample.cs | 2 +- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs index 37d8209..b3f5568 100644 --- a/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs +++ b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsQueryHandler.cs @@ -1,13 +1,13 @@ using Crypto.Compare.DataAccess; using Crypto.Compare.Services.Queries.Symbols; -using Crypto.Compare.Services.Results.Symbols; +using Crypto.Compare.Services.Results.SymbolProviders; using FluentResults; using MediatR; using Microsoft.EntityFrameworkCore; namespace Crypto.Compare.Services.Handlers.Queries.Symbols; -public class GetSymbolsQueryHandler : IRequestHandler> +public class GetSymbolsQueryHandler : IRequestHandler> { private readonly CryptoCompareContext _cryptoCompareContext; @@ -16,10 +16,10 @@ public GetSymbolsQueryHandler(CryptoCompareContext cryptoCompareContext) _cryptoCompareContext = cryptoCompareContext; } - public async Task> Handle(GetSymbolsQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetSymbolsQuery request, CancellationToken cancellationToken) { return - new GetSymbolsResult() + new GetSymbolProviderResult() { Symbols = await _cryptoCompareContext .SymbolProviders @@ -27,6 +27,16 @@ public async Task> Handle(GetSymbolsQuery request, Canc .AsNoTracking() .Include(x => x.Symbol) .Where(x => x.Symbol.Ticker == request.Ticker) + .Select(s => new SymbolProviderResult + { + Id = s.Symbol.Id, + Ticker = s.Ticker, + Symbol = $"{s.Symbol.BaseSymbol}/{s.Symbol.QuoteSymbol}", + ProviderId = s.ProviderId, + PriceSell = s.PriceSell, + PriceBuy = s.PriceBuy, + UpdatedAt = s.UpdatedAt + }) .ToListAsync(cancellationToken) }; } diff --git a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs index 9dbd30d..2cc187a 100644 --- a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs @@ -1,8 +1,7 @@ -using Crypto.Compare.Data; -using Crypto.Compare.Services.Results.Symbols; +using Crypto.Compare.Services.Results.SymbolProviders; using FluentResults; using MediatR; namespace Crypto.Compare.Services.Queries.Symbols; -public record GetSymbolsQuery(string Ticker) : IRequest>; +public record GetSymbolsQuery(string Ticker) : IRequest>; diff --git a/sources/core/Crypto.Compare.Services/Results/SymbolProviders/GetSymbolProviderResult.cs b/sources/core/Crypto.Compare.Services/Results/SymbolProviders/GetSymbolProviderResult.cs index 20e1683..5453bb5 100644 --- a/sources/core/Crypto.Compare.Services/Results/SymbolProviders/GetSymbolProviderResult.cs +++ b/sources/core/Crypto.Compare.Services/Results/SymbolProviders/GetSymbolProviderResult.cs @@ -1,6 +1,4 @@ -using Crypto.Compare.Data; - -namespace Crypto.Compare.Services.Results.SymbolProviders; +namespace Crypto.Compare.Services.Results.SymbolProviders; public class GetSymbolProviderResult : IHandlerResult { diff --git a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs index 792e723..de3bc20 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs @@ -15,6 +15,7 @@ using Crypto.Compare.DataAccess; using Crypto.Compare.GateIo; using Crypto.Compare.Mexc; +using Crypto.Compare.PublicApi.Dtos; using Crypto.Compare.PublicApi.HostedServices; using Crypto.Compare.PublicApi.Mapping; using Crypto.Compare.Services.AdaptersObservable.Impl; @@ -287,7 +288,7 @@ private static IServiceCollection AddMediator(this IServiceCollection services) .AddScoped>, GetProviderQueryHandler>() .AddScoped>, GetProvidersQueryHandler>() .AddScoped>, GetSymbolProvidersQueryHandler>() - .AddScoped>, GetSymbolsQueryHandler>() + .AddScoped>, GetSymbolsQueryHandler>() ; } } diff --git a/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs index 4b05307..d888181 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsResponseExample.cs @@ -9,7 +9,7 @@ public class GetSymbolsResponseExample : IExamplesProvider public GetSymbolsResponse GetExamples() => new() { - Symbols = new List + Symbols = new List { new() {