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