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/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/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/GetSymbolsBestPriceQueryHandler.cs b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs new file mode 100644 index 0000000..b0c701b --- /dev/null +++ b/sources/core/Crypto.Compare.Services/Handlers/Queries/Symbols/GetSymbolsBestPriceQueryHandler.cs @@ -0,0 +1,65 @@ +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; + +public class GetSymbolsBestPriceQueryHandler : IRequestHandler> +{ + private readonly CryptoCompareContext _cryptoCompareContext; + + public GetSymbolsBestPriceQueryHandler(CryptoCompareContext cryptoCompareContext) + { + _cryptoCompareContext = cryptoCompareContext; + } + + public async Task> Handle(GetSymbolsBestPriceQuery request, CancellationToken cancellationToken) + { + var query = _cryptoCompareContext + .Symbols + .Include(x => x.SymbolProviders) + .AsQueryable() + .AsNoTracking(); + + if (!string.IsNullOrEmpty(request.Ticker)) + { + query = query.Where(x => x.Ticker == request.Ticker); + } + + var symbols = await query + .Select(symbol => new SymbolResult + { + Id = symbol.Id, + Ticker = symbol.Ticker, + Symbol = $"{symbol.BaseSymbol}/{symbol.QuoteSymbol}", + ProviderSellId = symbol.SymbolProviders + .OrderByDescending(sp => sp.PriceSell) + .Select(sp => sp.ProviderId) + .FirstOrDefault(), + PriceSell = symbol.SymbolProviders + .OrderByDescending(sp => sp.PriceSell) + .Select(sp => sp.PriceSell) + .FirstOrDefault(), + ProviderBuyId = symbol.SymbolProviders + .OrderBy(sp => sp.PriceBuy) + .Select(sp => sp.ProviderId) + .FirstOrDefault(), + PriceBuy = symbol.SymbolProviders + .OrderBy(sp => sp.PriceBuy) + .Select(sp => sp.PriceBuy) + .FirstOrDefault(), + UpdatedAt = symbol.UpdatedAt + }) + .Skip((int)request.Skip) + .Take(request.Rows) + .ToListAsync(cancellationToken); + + return new GetSymbolsBestPriceResult + { + Symbols = symbols + }; + } +} \ No newline at end of file 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 4d54b67..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,50 +16,28 @@ public GetSymbolsQueryHandler(CryptoCompareContext cryptoCompareContext) _cryptoCompareContext = cryptoCompareContext; } - public async Task> Handle(GetSymbolsQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetSymbolsQuery request, CancellationToken cancellationToken) { - var query = _cryptoCompareContext - .Symbols - .Include(x=>x.SymbolProviders) - .AsQueryable() - .AsNoTracking(); - - if (!string.IsNullOrEmpty(request.Ticker)) - { - query = query.Where(x => x.Ticker == request.Ticker); - } - - var symbols = await query - .Select(symbol => new SymbolResult - { - Id = symbol.Id, - Ticker = symbol.Ticker, - Symbol = $"{symbol.BaseSymbol}/{symbol.QuoteSymbol}", - ProviderSellId = symbol.SymbolProviders - .OrderByDescending(sp => sp.PriceSell) - .Select(sp => sp.ProviderId) - .FirstOrDefault(), - PriceSell = symbol.SymbolProviders - .OrderByDescending(sp => sp.PriceSell) - .Select(sp => sp.PriceSell) - .FirstOrDefault(), - ProviderBuyId = symbol.SymbolProviders - .OrderBy(sp => sp.PriceBuy) - .Select(sp => sp.ProviderId) - .FirstOrDefault(), - PriceBuy = symbol.SymbolProviders - .OrderBy(sp => sp.PriceBuy) - .Select(sp => sp.PriceBuy) - .FirstOrDefault(), - UpdatedAt = symbol.UpdatedAt - }) - .Skip((int)request.Skip) - .Take(request.Rows) - .ToListAsync(cancellationToken); - - return new GetSymbolsResult - { - Symbols = symbols - }; + return + new GetSymbolProviderResult() + { + Symbols = await _cryptoCompareContext + .SymbolProviders + .AsQueryable() + .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) + }; } } \ No newline at end of file diff --git a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs new file mode 100644 index 0000000..51d113c --- /dev/null +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsBestPriceQuery.cs @@ -0,0 +1,33 @@ +using Crypto.Compare.Services.Results.Symbols; +using FluentResults; +using MediatR; + +namespace Crypto.Compare.Services.Queries.Symbols; + +public class GetSymbolsBestPriceQuery : IRequest> +{ + private const int MaxRows = 30; + private const int DefaultRows = 10; + + public GetSymbolsBestPriceQuery(string? ticker = null, long? skip = 0, int? rows = DefaultRows) + { + Ticker = ticker?.ToLowerInvariant(); + Skip = skip ?? 0; + Rows = rows is null or < 10 or > MaxRows ? DefaultRows : rows.Value; + } + + /// + /// Ticker symbol + /// + public string? Ticker { get; } + + /// + /// Skip rows + /// + public long Skip { get; } + + /// + /// Get rows + /// + public int Rows { get; } +} \ 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 cfc3a9f..2cc187a 100644 --- a/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs +++ b/sources/core/Crypto.Compare.Services/Queries/Symbols/GetSymbolsQuery.cs @@ -1,33 +1,7 @@ -using Crypto.Compare.Services.Results.Symbols; +using Crypto.Compare.Services.Results.SymbolProviders; using FluentResults; using MediatR; namespace Crypto.Compare.Services.Queries.Symbols; -public class GetSymbolsQuery : IRequest> -{ - private const int MaxRows = 30; - private const int DefaultRows = 10; - - public GetSymbolsQuery(string? ticker = null, long? skip = 0, int? rows = DefaultRows) - { - Ticker = ticker?.ToLowerInvariant(); - Skip = skip ?? 0; - Rows = rows is null or < 10 or > MaxRows ? DefaultRows : rows.Value; - } - - /// - /// Ticker symbol - /// - public string? Ticker { get; } - - /// - /// Skip rows - /// - public long Skip { get; } - - /// - /// Get rows - /// - public int Rows { get; } -} \ No newline at end of file +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/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..8652331 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; + +public class GetSymbolsResult { - public List Symbols { get; set; } -} \ No newline at end of file + public List Symbols { get; set; } +} 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/BinanceOrderBookListener.cs b/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs new file mode 100644 index 0000000..770142e --- /dev/null +++ b/sources/integrations/Crypto.Comapre.Binance/BinanceOrderBookListener.cs @@ -0,0 +1,241 @@ +using Binance.Net; +using Binance.Net.Clients; +using Binance.Net.Interfaces; +using Binance.Net.Interfaces.Clients; +using Binance.Net.Objects.Models.Spot.Socket; +using Binance.Net.Objects.Options; +using Crypto.Compare.Adapter; +using Crypto.Compare.Adapter.AdaptersObservable; +using Crypto.Compare.Adapter.Impl; +using Crypto.Compare.Data; +using CryptoExchange.Net.Objects.Errors; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.SharedApis; +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; + BinanceSocketClient.SetDefaultOptions(x => + { + x.ApiCredentials = new BinanceCredentials(_provider.AccessKey, _provider.SecretKey); + }); + } + + 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(); + var keys = _symbols.Values.Select(x => x.Name).ToList(); + + foreach (var symbols in SplitIntoBatches(keys, 15)) + { + (socketConnection, var result) = await TrySubscribeBatchesAsync(symbols, socketConnection, cancellationToken); + if (!result) + { + return _sockets.Count >= 1; + } + } + + + return true; + } + catch (Exception e) + { + _logger.LogError(e, LogPrefix + " Subscribe on symbols failed"); + return false; + } + } + + 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.ExchangeData.SubscribeToOrderBookUpdatesAsync(symbols, 1, HandlerAction, cancellationToken).ConfigureAwait(false); + + if (!result.Success) + { + if (result.Error.ErrorType == ErrorType.Unknown) + { + // try create new connection and subscribing + socketConnection = GetConnection(); + result = await socketConnection.SpotApi.ExchangeData.SubscribeToOrderBookUpdatesAsync(symbols, 1, HandlerAction, cancellationToken); + if (result.Success) + { + return (socketConnection, true); + } + + _logger.LogWarning(LogPrefix + " Not subscribe on ticker: {Symbols}", string.Join(',', symbols)); + return (socketConnection, false); + } + + _logger.LogWarning(LogPrefix + " Not subscribe on tickers: {Symbols}", string.Join(',', symbols)); + return (socketConnection, false); + } + + _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.SetApiCredentials(new BinanceCredentials(_provider.AccessKey, _provider.SecretKey)); + + _sockets.Add(connection); + return connection; + } + + + private async Task LoadSymbolsAsync(CancellationToken cancellationToken) + { + try + { + + var exchangeInfo = await _restClient.SpotApi.SharedClient.GetSpotSymbolsAsync( + new GetSymbolsRequest(tradingMode:TradingMode.Spot), ct: cancellationToken); + + if (!exchangeInfo.Success) + { + _logger.LogError(LogPrefix + " Get symbols failed"); + return false; + } + + foreach (var symbol in exchangeInfo.Data) + { + + if (!symbol.Trading) + { + 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(DataEvent 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..256751a --- /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 = "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 new file mode 100644 index 0000000..ed3797d --- /dev/null +++ b/sources/integrations/Crypto.Comapre.Binance/Crypto.Comapre.Binance.csproj @@ -0,0 +1,17 @@ + + + + net10.0 + enable + 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..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/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..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/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..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/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..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 13f1932..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.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..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.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..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/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/Controllers/SymbolsController.cs b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs index 46d6744..d27b7ac 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Controllers/SymbolsController.cs @@ -42,16 +42,16 @@ public SymbolsProvider(IMediator mediator, IResponseMapper responseMapper) [HttpGet("")] [Produces(MediaTypeNames.Application.Json)] [SwaggerResponse(StatusCodes.Status200OK, "Get all list of symbols", typeof(GetSymbolsResponse))] - [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsResponseExample))] + [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); } /// @@ -64,7 +64,7 @@ public async Task GetSymbols([FromQuery] int? skip, [FromQuery] i [HttpGet("provider/{providerId}")] [Produces(MediaTypeNames.Application.Json)] [SwaggerResponse(StatusCodes.Status200OK, "Get list of symbols for provider", typeof(GetSymbolsResponse))] - [SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetSymbolsResponseExample))] + [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(GetSymbolsResponse))] + [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))] + [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/Crypto.Compare.PublicApi.csproj b/sources/presentation/Crypto.Compare.PublicApi/Crypto.Compare.PublicApi.csproj index e88ea21..b6bac22 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/Dtos/SymbolBestDto.cs b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolBestDto.cs new file mode 100644 index 0000000..1bd8d9a --- /dev/null +++ b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolBestDto.cs @@ -0,0 +1,44 @@ +namespace Crypto.Compare.PublicApi.Dtos; + +public class SymbolBestDto +{ + /// + /// 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 the best sell price + /// + public int ProviderSellId { get; set; } + + /// + /// Price sell + /// + public decimal PriceSell { get; set; } + + /// + /// Id provider the best buy price + /// + public int ProviderBuyId { get; set; } + + /// + /// Current sell price + /// + public decimal PriceBuy { get; set; } + + /// + /// Last date get quotes + /// + public DateTime UpdatedAt { get; set; } +} \ No newline at end of file diff --git a/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs index 365b478..271ed71 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Dtos/SymbolDto.cs @@ -6,39 +6,34 @@ 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 the best sell price + /// Id provider /// - public int ProviderSellId { get; set; } - + public int ProviderId { get; set; } + /// /// Price sell /// public decimal PriceSell { get; set; } - /// - /// Id provider the best buy price - /// - public int ProviderBuyId { get; set; } - /// /// Current sell price /// public decimal PriceBuy { get; set; } - + /// /// Last date get quotes /// public DateTime UpdatedAt { get; set; } -} \ No newline at end of file +} diff --git a/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs b/sources/presentation/Crypto.Compare.PublicApi/Ioc/ServicesRegistry.cs index 64ea9f7..de3bc20 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; @@ -10,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; @@ -33,11 +39,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 +115,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 +127,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 +142,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 +157,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 +172,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 +187,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 +203,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 +213,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; } @@ -261,11 +284,11 @@ 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>() - + .AddScoped>, GetSymbolsQueryHandler>() ; } } diff --git a/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs b/sources/presentation/Crypto.Compare.PublicApi/Mapping/MapsterProfile.cs index 1c08951..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(); 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 index b5a3c00..88d4ef9 100644 --- a/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs +++ b/sources/presentation/Crypto.Compare.PublicApi/Responses/v1/Symbols/GetSymbolsResponse.cs @@ -4,5 +4,5 @@ namespace Crypto.Compare.PublicApi.Responses.v1.Symbols; public class GetSymbolsResponse : BaseApiResponse { - public List Symbols { get; set; } -} \ No newline at end of file + public List Symbols { get; set; } +} diff --git a/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsBestResponseExample.cs b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsBestResponseExample.cs new file mode 100644 index 0000000..69b8b45 --- /dev/null +++ b/sources/presentation/Crypto.Compare.PublicApi/Swaggers/Responses/v1/Symbols/GetSymbolsBestResponseExample.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 GetSymbolsBestResponseExample : IExamplesProvider +{ + public GetSymbolsBestResponse GetExamples() + => new() + { + Symbols = new List + { + new() + { + Id = 1, + Symbol = "BTCUSDT", + PriceSell = 82000, + PriceBuy = 79000, + ProviderSellId = 1, + Ticker = "BTCUSDT" + }, + new() + { + Id = 2, + Symbol = "ETHUSDT", + PriceSell = 3200, + PriceBuy = 3100, + ProviderSellId = 1, + Ticker = "ETHUSDT" + } + } + }; +} \ 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/GetSymbolsResponseExample.cs index b0e620b..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 @@ -7,18 +7,18 @@ namespace Crypto.Compare.PublicApi.Swaggers.Responses.v1.Symbols; public class GetSymbolsResponseExample : IExamplesProvider { public GetSymbolsResponse GetExamples() - => new() + => new() + { + Symbols = new List { - Symbols = new List - { - new() + new() { Id = 1, - Symbol = "BTCUSDT", + Symbol = "ETHUSDT", PriceSell = 82000, PriceBuy = 79000, - ProviderSellId = 1, - Ticker = "BTCUSDT" + ProviderId = 1, + Ticker = "ETHUSDT" }, new() { @@ -26,9 +26,9 @@ public GetSymbolsResponse GetExamples() Symbol = "ETHUSDT", PriceSell = 3200, PriceBuy = 3100, - ProviderSellId = 1, + ProviderId = 2, Ticker = "ETHUSDT" } - } - }; + } + }; } \ No newline at end of file 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..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