diff --git a/Bitget.Net/Bitget.Net.csproj b/Bitget.Net/Bitget.Net.csproj index cbf08da..4a613c7 100644 --- a/Bitget.Net/Bitget.Net.csproj +++ b/Bitget.Net/Bitget.Net.csproj @@ -1,4 +1,4 @@ - + net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1 latest @@ -55,7 +55,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Bitget.Net/Bitget.Net.xml b/Bitget.Net/Bitget.Net.xml index 3ffd7b5..4a6d153 100644 --- a/Bitget.Net/Bitget.Net.xml +++ b/Bitget.Net/Bitget.Net.xml @@ -155,6 +155,37 @@ + + + + + + + + + + + + + + + + + + ctor + + + + + + + + + + + ctor + + @@ -7443,6 +7474,38 @@ Tracker factory + + + Create a new Spot user data tracker + + User identifier + Configuration + Credentials + Environment + + + + Create a new spot user data tracker + + Configuration + + + + Create a new futures user data tracker + + User identifier + Configuration + Credentials + Environment + Product type + + + + Create a new futures user data tracker + + Configuration + Product type + Bitget Api addresses diff --git a/Bitget.Net/BitgetTrackerFactory.cs b/Bitget.Net/BitgetTrackerFactory.cs index 4f5b8a1..740fa27 100644 --- a/Bitget.Net/BitgetTrackerFactory.cs +++ b/Bitget.Net/BitgetTrackerFactory.cs @@ -1,11 +1,17 @@ using Bitget.Net.Clients; +using Bitget.Net.Enums; using Bitget.Net.Interfaces; using Bitget.Net.Interfaces.Clients; +using CryptoExchange.Net.Authentication; using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Trackers.Klines; using CryptoExchange.Net.Trackers.Trades; +using CryptoExchange.Net.Trackers.UserData; +using CryptoExchange.Net.Trackers.UserData.Interfaces; +using CryptoExchange.Net.Trackers.UserData.Objects; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Bitget.Net { @@ -100,5 +106,69 @@ public ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, period ); } + + /// + public IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig? config = null) + { + var restClient = _serviceProvider?.GetRequiredService() ?? new BitgetRestClient(); + var socketClient = _serviceProvider?.GetRequiredService() ?? new BitgetSocketClient(); + return new BitgetUserSpotDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + null, + config + ); + } + + /// + public IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, ApiCredentials credentials, SpotUserDataTrackerConfig? config = null, BitgetEnvironment? environment = null) + { + var clientProvider = _serviceProvider?.GetRequiredService() ?? new BitgetUserClientProvider(); + var restClient = clientProvider.GetRestClient(userIdentifier, credentials, environment); + var socketClient = clientProvider.GetSocketClient(userIdentifier, credentials, environment); + return new BitgetUserSpotDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + userIdentifier, + config + ); + } + + /// + public IUserFuturesDataTracker CreateUserFuturesDataTracker(BitgetProductTypeV2 productType, FuturesUserDataTrackerConfig? config = null) + { + var exchangeParams = new ExchangeParameters(new ExchangeParameter("Bitget", "ProductType", productType.ToString())); + + var restClient = _serviceProvider?.GetRequiredService() ?? new BitgetRestClient(); + var socketClient = _serviceProvider?.GetRequiredService() ?? new BitgetSocketClient(); + return new BitgetUserFuturesDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + null, + config, + exchangeParams + ); + } + + /// + public IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, ApiCredentials credentials, BitgetProductTypeV2 productType, FuturesUserDataTrackerConfig? config = null, BitgetEnvironment? environment = null) + { + var exchangeParams = new ExchangeParameters(new ExchangeParameter("Bitget", "ProductType", productType.ToString())); + + var clientProvider = _serviceProvider?.GetRequiredService() ?? new BitgetUserClientProvider(); + var restClient = clientProvider.GetRestClient(userIdentifier, credentials, environment); + var socketClient = clientProvider.GetSocketClient(userIdentifier, credentials, environment); + return new BitgetUserFuturesDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + userIdentifier, + config, + exchangeParams + ); + } } } diff --git a/Bitget.Net/BitgetUserDataTracker.cs b/Bitget.Net/BitgetUserDataTracker.cs new file mode 100644 index 0000000..4713b3e --- /dev/null +++ b/Bitget.Net/BitgetUserDataTracker.cs @@ -0,0 +1,64 @@ +using Bitget.Net.Interfaces.Clients; +using CryptoExchange.Net.SharedApis; +using CryptoExchange.Net.Trackers.UserData; +using CryptoExchange.Net.Trackers.UserData.Objects; +using Microsoft.Extensions.Logging; + +namespace Bitget.Net +{ + /// + public class BitgetUserSpotDataTracker : UserSpotDataTracker + { + /// + /// ctor + /// + public BitgetUserSpotDataTracker( + ILogger logger, + IBitgetRestClient restClient, + IBitgetSocketClient socketClient, + string? userIdentifier, + SpotUserDataTrackerConfig? config) : base( + logger, + restClient.SpotApiV2.SharedClient, + null, + restClient.SpotApiV2.SharedClient, + socketClient.SpotApiV2.SharedClient, + restClient.SpotApiV2.SharedClient, + socketClient.SpotApiV2.SharedClient, + socketClient.SpotApiV2.SharedClient, + userIdentifier, + config ?? new SpotUserDataTrackerConfig()) + { + } + } + + /// + public class BitgetUserFuturesDataTracker : UserFuturesDataTracker + { + /// + protected override bool WebsocketPositionUpdatesAreFullSnapshots => false; + + /// + /// ctor + /// + public BitgetUserFuturesDataTracker( + ILogger logger, + IBitgetRestClient restClient, + IBitgetSocketClient socketClient, + string? userIdentifier, + FuturesUserDataTrackerConfig? config, + ExchangeParameters exchangeParameters) : base(logger, + restClient.FuturesApiV2.SharedClient, + null, + restClient.FuturesApiV2.SharedClient, + socketClient.FuturesApiV2.SharedClient, + restClient.FuturesApiV2.SharedClient, + socketClient.FuturesApiV2.SharedClient, + socketClient.FuturesApiV2.SharedClient, + socketClient.FuturesApiV2.SharedClient, + userIdentifier, + config ?? new FuturesUserDataTrackerConfig()) + { + } + } +} diff --git a/Bitget.Net/Clients/BitgetUserClientProvider.cs b/Bitget.Net/Clients/BitgetUserClientProvider.cs index f2cef9e..92f483d 100644 --- a/Bitget.Net/Clients/BitgetUserClientProvider.cs +++ b/Bitget.Net/Clients/BitgetUserClientProvider.cs @@ -62,7 +62,7 @@ public void ClearUserClients(string userIdentifier) /// public IBitgetRestClient GetRestClient(string userIdentifier, ApiCredentials? credentials = null, BitgetEnvironment? environment = null) { - if (!_restClients.TryGetValue(userIdentifier, out var client)) + if (!_restClients.TryGetValue(userIdentifier, out var client) || client.Disposed) client = CreateRestClient(userIdentifier, credentials, environment); return client; @@ -71,7 +71,7 @@ public IBitgetRestClient GetRestClient(string userIdentifier, ApiCredentials? cr /// public IBitgetSocketClient GetSocketClient(string userIdentifier, ApiCredentials? credentials = null, BitgetEnvironment? environment = null) { - if (!_socketClients.TryGetValue(userIdentifier, out var client)) + if (!_socketClients.TryGetValue(userIdentifier, out var client) || client.Disposed) client = CreateSocketClient(userIdentifier, credentials, environment); return client; diff --git a/Bitget.Net/Clients/FuturesApiV2/BitgetRestClientFuturesApiShared.cs b/Bitget.Net/Clients/FuturesApiV2/BitgetRestClientFuturesApiShared.cs index 26ad597..87049f8 100644 --- a/Bitget.Net/Clients/FuturesApiV2/BitgetRestClientFuturesApiShared.cs +++ b/Bitget.Net/Clients/FuturesApiV2/BitgetRestClientFuturesApiShared.cs @@ -218,6 +218,44 @@ async Task> IFuturesSymbolRestClient.Ge return response; } + async Task> IFuturesSymbolRestClient.GetFuturesSymbolsForBaseAssetAsync(string baseAsset) + { + if (!ExchangeSymbolCache.HasCached(_topicId)) + { + var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); + if (!symbols) + return new ExchangeResult(Exchange, symbols.Error!); + } + + return new ExchangeResult(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicId, baseAsset)); + } + + async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(SharedSymbol symbol) + { + if (symbol.TradingMode == TradingMode.Spot) + throw new ArgumentException(nameof(symbol), "Spot symbols not allowed"); + + if (!ExchangeSymbolCache.HasCached(_topicId)) + { + var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); + if (!symbols) + return new ExchangeResult(Exchange, symbols.Error!); + } + + return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicId, symbol)); + } + + async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(string symbolName) + { + if (!ExchangeSymbolCache.HasCached(_topicId)) + { + var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); + if (!symbols) + return new ExchangeResult(Exchange, symbols.Error!); + } + + return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicId, symbolName)); + } #endregion #region Klines client @@ -958,6 +996,7 @@ async Task> IFuturesOrderRestClient.GetPosit LiquidationPrice = x.LiquidationPrice, AverageOpenPrice = x.AverageOpenPrice, Leverage = x.Leverage, + PositionMode = x.PositionSide == PositionSide.Oneway ? SharedPositionMode.OneWay : SharedPositionMode.HedgeMode, PositionSide = x.PositionSide == PositionSide.Short ? SharedPositionSide.Short : SharedPositionSide.Long }).ToArray()); } diff --git a/Bitget.Net/Clients/FuturesApiV2/BitgetSocketClientFuturesApiShared.cs b/Bitget.Net/Clients/FuturesApiV2/BitgetSocketClientFuturesApiShared.cs index 02cf5c0..40e00df 100644 --- a/Bitget.Net/Clients/FuturesApiV2/BitgetSocketClientFuturesApiShared.cs +++ b/Bitget.Net/Clients/FuturesApiV2/BitgetSocketClientFuturesApiShared.cs @@ -144,9 +144,6 @@ async Task> IBalanceSocketClient.SubscribeToB var result = await SubscribeToBalanceUpdatesAsync( productType, update => { - if (update.UpdateType == SocketUpdateType.Snapshot) - return; - handler(update.ToType(update.Data.Select(x => new SharedBalance(x.MarginAsset, x.Available, x.Equity)).ToArray())); }, ct: ct).ConfigureAwait(false); @@ -358,6 +355,7 @@ async Task> IPositionSocketClient.SubscribeTo handler(update.ToType(update.Data.Select(x => new SharedPosition(ExchangeSymbolCache.ParseSymbol(_topicId, x.Symbol), x.Symbol, x.Total, x.UpdateTime) { AverageOpenPrice = x.AverageOpenPrice, + PositionMode = x.PositionSide == PositionSide.Oneway ? SharedPositionMode.OneWay : SharedPositionMode.HedgeMode, PositionSide = x.PositionSide == PositionSide.Short ? SharedPositionSide.Short : SharedPositionSide.Long, UnrealizedPnl = x.UnrealizedProfitAndLoss, Leverage = x.Leverage, diff --git a/Bitget.Net/Clients/SpotApiV2/BitgetRestClientSpotApiShared.cs b/Bitget.Net/Clients/SpotApiV2/BitgetRestClientSpotApiShared.cs index 8080b13..affd775 100644 --- a/Bitget.Net/Clients/SpotApiV2/BitgetRestClientSpotApiShared.cs +++ b/Bitget.Net/Clients/SpotApiV2/BitgetRestClientSpotApiShared.cs @@ -112,6 +112,44 @@ async Task> ISpotSymbolRestClient.GetSpotS return response; } + async Task> ISpotSymbolRestClient.GetSpotSymbolsForBaseAssetAsync(string baseAsset) + { + if (!ExchangeSymbolCache.HasCached(_topicId)) + { + var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); + if (!symbols) + return new ExchangeResult(Exchange, symbols.Error!); + } + + return new ExchangeResult(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicId, baseAsset)); + } + + async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(SharedSymbol symbol) + { + if (symbol.TradingMode != TradingMode.Spot) + throw new ArgumentException(nameof(symbol), "Only Spot symbols allowed"); + + if (!ExchangeSymbolCache.HasCached(_topicId)) + { + var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); + if (!symbols) + return new ExchangeResult(Exchange, symbols.Error!); + } + + return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicId, symbol)); + } + + async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(string symbolName) + { + if (!ExchangeSymbolCache.HasCached(_topicId)) + { + var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); + if (!symbols) + return new ExchangeResult(Exchange, symbols.Error!); + } + + return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicId, symbolName)); + } #endregion #region Ticker client @@ -677,7 +715,14 @@ async Task> IDepositRestClient.GetDepositsAsy if (deposits.Data.Count() == limit) nextToken = new FromIdToken(deposits.Data.Min(x => x.OrderId)!); - return deposits.AsExchangeResult(Exchange, TradingMode.Spot, deposits.Data.Select(x => new SharedDeposit(x.Asset, x.Quantity, x.Status == TransferStatus.Success, x.CreateTime) + return deposits.AsExchangeResult(Exchange, TradingMode.Spot, deposits.Data.Select(x => + new SharedDeposit( + x.Asset, + x.Quantity, + x.Status == TransferStatus.Success, + x.CreateTime, + x.Status == TransferStatus.Success ? SharedTransferStatus.Completed : + x.Status == TransferStatus.Failed ? SharedTransferStatus.Failed: SharedTransferStatus.InProgress) { Id = x.OrderId, Network = x.Network, diff --git a/Bitget.Net/Interfaces/IBitgetTrackerFactory.cs b/Bitget.Net/Interfaces/IBitgetTrackerFactory.cs index fa84131..2631e5e 100644 --- a/Bitget.Net/Interfaces/IBitgetTrackerFactory.cs +++ b/Bitget.Net/Interfaces/IBitgetTrackerFactory.cs @@ -1,4 +1,9 @@ -using CryptoExchange.Net.Interfaces; +using Bitget.Net.Enums; +using CryptoExchange.Net.Authentication; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Trackers.UserData; +using CryptoExchange.Net.Trackers.UserData.Interfaces; +using CryptoExchange.Net.Trackers.UserData.Objects; namespace Bitget.Net.Interfaces { @@ -7,5 +12,34 @@ namespace Bitget.Net.Interfaces /// public interface IBitgetTrackerFactory : ITrackerFactory { + /// + /// Create a new Spot user data tracker + /// + /// User identifier + /// Configuration + /// Credentials + /// Environment + IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, ApiCredentials credentials, SpotUserDataTrackerConfig? config = null, BitgetEnvironment? environment = null); + /// + /// Create a new spot user data tracker + /// + /// Configuration + IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig? config = null); + + /// + /// Create a new futures user data tracker + /// + /// User identifier + /// Configuration + /// Credentials + /// Environment + /// Product type + IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, ApiCredentials credentials, BitgetProductTypeV2 productType, FuturesUserDataTrackerConfig? config = null, BitgetEnvironment? environment = null); + /// + /// Create a new futures user data tracker + /// + /// Configuration + /// Product type + IUserFuturesDataTracker CreateUserFuturesDataTracker(BitgetProductTypeV2 productType, FuturesUserDataTrackerConfig? config = null); } }