From e7ac1aa9adb19db8741b560c0db8b1ec45a25b07 Mon Sep 17 00:00:00 2001 From: JKorf Date: Sun, 1 Feb 2026 15:39:25 +0100 Subject: [PATCH 1/8] wip --- .../SpotApi/HTXRestClientSpotApiShared.cs | 38 +++++++++++++++++++ .../HTXRestClientUsdtFuturesApiShared.cs | 37 ++++++++++++++++++ HTX.Net/HTX.Net.csproj | 6 ++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs index be3f29a0..c5c2505b 100644 --- a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs +++ b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs @@ -109,6 +109,44 @@ async Task> ISpotSymbolRestClient.GetSpotS return response; } + public async Task> 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)); + } + + public async Task> 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)); + } + + public async Task> 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 diff --git a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs index 6b938db9..22fdaa25 100644 --- a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs +++ b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs @@ -177,7 +177,44 @@ async Task> IFuturesSymbolRestClient.Ge ExchangeSymbolCache.UpdateSymbolInfo(_topicId, response.Data); return response; } + public async Task> 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)); + } + public async Task> 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)); + } + + public async Task> 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 Futures Order Client diff --git a/HTX.Net/HTX.Net.csproj b/HTX.Net/HTX.Net.csproj index 25a06fc6..b3e997bb 100644 --- a/HTX.Net/HTX.Net.csproj +++ b/HTX.Net/HTX.Net.csproj @@ -1,4 +1,4 @@ - + net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1 enable @@ -52,10 +52,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file From ffb838f43764235645333e7d319e2e66c8c82b3f Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 2 Feb 2026 09:39:58 +0100 Subject: [PATCH 2/8] Added check for disposed client in UserClientProvider --- HTX.Net/Clients/HTXUserClientProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HTX.Net/Clients/HTXUserClientProvider.cs b/HTX.Net/Clients/HTXUserClientProvider.cs index ce108844..372cc7ab 100644 --- a/HTX.Net/Clients/HTXUserClientProvider.cs +++ b/HTX.Net/Clients/HTXUserClientProvider.cs @@ -60,7 +60,7 @@ public void ClearUserClients(string userIdentifier) /// public IHTXRestClient GetRestClient(string userIdentifier, ApiCredentials? credentials = null, HTXEnvironment? 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; @@ -69,7 +69,7 @@ public IHTXRestClient GetRestClient(string userIdentifier, ApiCredentials? crede /// public IHTXSocketClient GetSocketClient(string userIdentifier, ApiCredentials? credentials = null, HTXEnvironment? 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; From a8a6f925815d896800f1bf4e861a86c4958d8191 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 2 Feb 2026 16:36:19 +0100 Subject: [PATCH 3/8] wip --- .../Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs | 2 ++ .../Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs index 22fdaa25..1af28929 100644 --- a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs +++ b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs @@ -780,6 +780,7 @@ async Task> IFuturesOrderRestClient.GetPosit UnrealizedPnl = x.UnrealizedPnl, AverageOpenPrice = x.CostOpen, Leverage = x.LeverageRate, + PositionMode = x.PositionMode == PositionMode.SingleSide ? SharedPositionMode.OneWay : SharedPositionMode.HedgeMode, PositionSide = x.Side == OrderSide.Sell ? SharedPositionSide.Short : SharedPositionSide.Long }).ToArray()); } @@ -794,6 +795,7 @@ async Task> IFuturesOrderRestClient.GetPosit UnrealizedPnl = x.UnrealizedPnl, AverageOpenPrice = x.CostOpen, Leverage = x.LeverageRate, + PositionMode = x.PositionMode == PositionMode.SingleSide ? SharedPositionMode.OneWay : SharedPositionMode.HedgeMode, PositionSide = x.Side == OrderSide.Sell ? SharedPositionSide.Short : SharedPositionSide.Long }).ToArray()); } diff --git a/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs b/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs index 2b079fe7..4852472f 100644 --- a/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs +++ b/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs @@ -326,6 +326,7 @@ async Task> IPositionSocketClient.SubscribeTo ExchangeSymbolCache.ParseSymbol(_topicId, x.ContractCode), x.ContractCode, x.Quantity, update.Data.Timestamp) { AverageOpenPrice = x.PositionPrice, + PositionMode = x.PositionMode == PositionMode.SingleSide ? SharedPositionMode.OneWay : SharedPositionMode.HedgeMode, PositionSide = x.OrderSide == Enums.OrderSide.Sell ? SharedPositionSide.Short : SharedPositionSide.Long, Leverage = x.LeverageRate, UnrealizedPnl = x.UnrealizedPnl @@ -339,6 +340,7 @@ async Task> IPositionSocketClient.SubscribeTo update => handler(update.ToType(update.Data.Data.Select(x => new SharedPosition(ExchangeSymbolCache.ParseSymbol(_topicId, x.ContractCode), x.ContractCode, x.Quantity, update.Data.Timestamp) { AverageOpenPrice = x.PositionPrice, + PositionMode = x.PositionMode == PositionMode.SingleSide ? SharedPositionMode.OneWay : SharedPositionMode.HedgeMode, PositionSide = x.OrderSide == Enums.OrderSide.Sell ? SharedPositionSide.Short : SharedPositionSide.Long, Leverage = x.LeverageRate, UnrealizedPnl = x.UnrealizedPnl From 71c58c7f1c5f0e6d501f49e8ddb0bd46553344f4 Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 3 Feb 2026 21:08:23 +0100 Subject: [PATCH 4/8] wip --- HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs | 6 +++--- .../UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs index c5c2505b..a9207cd4 100644 --- a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs +++ b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs @@ -109,7 +109,7 @@ async Task> ISpotSymbolRestClient.GetSpotS return response; } - public async Task> GetSpotSymbolsForBaseAssetAsync(string baseAsset) + async Task> ISpotSymbolRestClient.GetSpotSymbolsForBaseAssetAsync(string baseAsset) { if (!ExchangeSymbolCache.HasCached(_topicId)) { @@ -121,7 +121,7 @@ public async Task> GetSpotSymbolsForBaseAssetAsyn return new ExchangeResult(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicId, baseAsset)); } - public async Task> SupportsSpotSymbolAsync(SharedSymbol symbol) + async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(SharedSymbol symbol) { if (symbol.TradingMode != TradingMode.Spot) throw new ArgumentException(nameof(symbol), "Only Spot symbols allowed"); @@ -136,7 +136,7 @@ public async Task> SupportsSpotSymbolAsync(SharedSymbol sym return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicId, symbol)); } - public async Task> SupportsSpotSymbolAsync(string symbolName) + async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(string symbolName) { if (!ExchangeSymbolCache.HasCached(_topicId)) { diff --git a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs index 1af28929..b95dc569 100644 --- a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs +++ b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs @@ -177,7 +177,7 @@ async Task> IFuturesSymbolRestClient.Ge ExchangeSymbolCache.UpdateSymbolInfo(_topicId, response.Data); return response; } - public async Task> GetFuturesSymbolsForBaseAssetAsync(string baseAsset) + async Task> IFuturesSymbolRestClient.GetFuturesSymbolsForBaseAssetAsync(string baseAsset) { if (!ExchangeSymbolCache.HasCached(_topicId)) { @@ -189,7 +189,7 @@ public async Task> GetFuturesSymbolsForBaseAssetA return new ExchangeResult(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicId, baseAsset)); } - public async Task> SupportsFuturesSymbolAsync(SharedSymbol symbol) + async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(SharedSymbol symbol) { if (symbol.TradingMode == TradingMode.Spot) throw new ArgumentException(nameof(symbol), "Spot symbols not allowed"); @@ -204,7 +204,7 @@ public async Task> SupportsFuturesSymbolAsync(SharedSymbol return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicId, symbol)); } - public async Task> SupportsFuturesSymbolAsync(string symbolName) + async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(string symbolName) { if (!ExchangeSymbolCache.HasCached(_topicId)) { From 1342d0a49a11355dec49b73407426e16c4431a6e Mon Sep 17 00:00:00 2001 From: Jkorf Date: Wed, 4 Feb 2026 16:31:17 +0100 Subject: [PATCH 5/8] wip --- .../HTXSocketUsdtFuturesMessageHandler.cs | 3 +- .../HTXRestClientUsdtFuturesApiShared.cs | 4 +- .../HTXSocketClientUsdtFuturesApiShared.cs | 4 +- HTX.Net/Enums/EventTrigger.cs | 7 +- HTX.Net/HTX.Net.xml | 68 +++++++++++++++++++ HTX.Net/HTXTrackerFactory.cs | 66 ++++++++++++++++++ HTX.Net/HTXUserDataTracker.cs | 65 ++++++++++++++++++ HTX.Net/Interfaces/IHTXTrackerFactory.cs | 35 +++++++++- 8 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 HTX.Net/HTXUserDataTracker.cs diff --git a/HTX.Net/Clients/MessageHandlers/HTXSocketUsdtFuturesMessageHandler.cs b/HTX.Net/Clients/MessageHandlers/HTXSocketUsdtFuturesMessageHandler.cs index 2e829f4c..040be771 100644 --- a/HTX.Net/Clients/MessageHandlers/HTXSocketUsdtFuturesMessageHandler.cs +++ b/HTX.Net/Clients/MessageHandlers/HTXSocketUsdtFuturesMessageHandler.cs @@ -26,7 +26,8 @@ internal class HTXSocketUsdtFuturesMessageHandler : JsonSocketMessageHandler { "accounts_cross.", "accounts_cross" }, { "orders_cross.", "orders_cross" }, { "positions_cross.", "positions_cross" }, - { "matchOrders.", "matchOrders" } + { "matchOrders.", "matchOrders" }, + { "matchOrders_cross.", "matchOrders_cross" } }; public override JsonSerializerOptions Options { get; } = SerializerOptions.WithConverters(HTXExchange._serializerContext); diff --git a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs index b95dc569..e4f535ee 100644 --- a/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs +++ b/HTX.Net/Clients/UsdtFutures/HTXRestClientUsdtFuturesApiShared.cs @@ -37,7 +37,7 @@ async Task> IBalanceRestClient.GetBalancesAsy if (!result) return result.AsExchangeResult(Exchange, null, default); - return result.AsExchangeResult(Exchange, SupportedTradingModes, result.Data.Select(x => new SharedBalance(x.MarginAsset, x.MarginBalance, x.MarginFrozen + x.MarginBalance)).ToArray()); + return result.AsExchangeResult(Exchange, SupportedTradingModes, result.Data.Select(x => new SharedBalance(x.MarginAsset, x.WithdrawAvailable, x.MarginBalance)).ToArray()); } else { @@ -45,7 +45,7 @@ async Task> IBalanceRestClient.GetBalancesAsy if (!result) return result.AsExchangeResult(Exchange, null, default); - return result.AsExchangeResult(Exchange, SupportedTradingModes, result.Data.Select(x => new SharedBalance(x.MarginAsset, x.MarginBalance, x.MarginFrozen + x.MarginBalance) + return result.AsExchangeResult(Exchange, SupportedTradingModes, result.Data.Select(x => new SharedBalance("USDT", x.WithdrawAvailable, x.MarginBalance) { IsolatedMarginSymbol = x.ContractCode }).ToArray()); diff --git a/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs b/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs index 4852472f..45324130 100644 --- a/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs +++ b/HTX.Net/Clients/UsdtFutures/HTXSocketClientUsdtFuturesApiShared.cs @@ -131,7 +131,7 @@ async Task> IBalanceSocketClient.SubscribeToB if (marginMode == SharedMarginMode.Cross) { var result = await SubscribeToCrossMarginBalanceUpdatesAsync( - update => handler(update.ToType(update.Data.Data.Select(x => new SharedBalance(x.MarginAsset, x.MarginBalance - x.MarginFrozen, x.MarginBalance) ).ToArray())), + update => handler(update.ToType(update.Data.Data.Select(x => new SharedBalance(x.MarginAsset, x.WithdrawAvailable, x.MarginBalance) ).ToArray())), ct: ct).ConfigureAwait(false); return new ExchangeResult(Exchange, result); @@ -139,7 +139,7 @@ async Task> IBalanceSocketClient.SubscribeToB else { var result = await SubscribeToIsolatedMarginBalanceUpdatesAsync( - update => handler(update.ToType(update.Data.Data.Select(x => new SharedBalance(x.Asset, x.MarginBalance - x.MarginFrozen, x.MarginBalance) { IsolatedMarginSymbol = x.MarginAccount }).ToArray())), + update => handler(update.ToType(update.Data.Data.Select(x => new SharedBalance("USDT", x.WithdrawAvailable, x.MarginBalance) { IsolatedMarginSymbol = x.MarginAccount }).ToArray())), ct: ct).ConfigureAwait(false); return new ExchangeResult(Exchange, result); diff --git a/HTX.Net/Enums/EventTrigger.cs b/HTX.Net/Enums/EventTrigger.cs index 25de077b..d328b63e 100644 --- a/HTX.Net/Enums/EventTrigger.cs +++ b/HTX.Net/Enums/EventTrigger.cs @@ -62,6 +62,11 @@ public enum EventTrigger /// Snapshot /// [Map("snapshot")] - Snapshot + Snapshot, + /// + /// Close order + /// + [Map("order.close")] + Close } } diff --git a/HTX.Net/HTX.Net.xml b/HTX.Net/HTX.Net.xml index 51221be0..4a20cf26 100644 --- a/HTX.Net/HTX.Net.xml +++ b/HTX.Net/HTX.Net.xml @@ -6107,6 +6107,11 @@ Snapshot + + + Close order + + Fee deduction status. @@ -8019,6 +8024,37 @@ + + + + + + + + + + + + + + + + + + ctor + + + + + + + + + + + ctor + + Client for accessing the HTX API. @@ -11258,6 +11294,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 + Margin mode + Environment + + + + Create a new futures user data tracker + + Configuration + Margin mode + Api addresses usable for the HTX clients diff --git a/HTX.Net/HTXTrackerFactory.cs b/HTX.Net/HTXTrackerFactory.cs index f1c6c96e..02732c1b 100644 --- a/HTX.Net/HTXTrackerFactory.cs +++ b/HTX.Net/HTXTrackerFactory.cs @@ -1,10 +1,13 @@ using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Trackers.Klines; using CryptoExchange.Net.Trackers.Trades; +using CryptoExchange.Net.Trackers.UserData.Interfaces; +using CryptoExchange.Net.Trackers.UserData.Objects; using HTX.Net.Clients; using HTX.Net.Interfaces; using HTX.Net.Interfaces.Clients; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging.Abstractions; namespace HTX.Net { @@ -98,5 +101,68 @@ public ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, period ); } + /// + public IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig config) + { + var restClient = _serviceProvider?.GetRequiredService() ?? new HTXRestClient(); + var socketClient = _serviceProvider?.GetRequiredService() ?? new HTXSocketClient(); + return new HTXUserSpotDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + null, + config + ); + } + + /// + public IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, SpotUserDataTrackerConfig config, ApiCredentials credentials, HTXEnvironment? environment = null) + { + var clientProvider = _serviceProvider?.GetRequiredService() ?? new HTXUserClientProvider(); + var restClient = clientProvider.GetRestClient(userIdentifier, credentials, environment); + var socketClient = clientProvider.GetSocketClient(userIdentifier, credentials, environment); + return new HTXUserSpotDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + userIdentifier, + config + ); + } + + /// + public IUserFuturesDataTracker CreateUserFuturesDataTracker(FuturesUserDataTrackerConfig config, SharedMarginMode marginMode) + { + var exchangeParams = new ExchangeParameters(new ExchangeParameter("HTX", "MarginMode", marginMode)); + + var restClient = _serviceProvider?.GetRequiredService() ?? new HTXRestClient(); + var socketClient = _serviceProvider?.GetRequiredService() ?? new HTXSocketClient(); + return new HTXUserFuturesDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + null, + config, + exchangeParams + ); + } + + /// + public IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, FuturesUserDataTrackerConfig config, ApiCredentials credentials, SharedMarginMode marginMode, HTXEnvironment? environment = null) + { + var exchangeParams = new ExchangeParameters(new ExchangeParameter("HTX", "MarginMode", marginMode)); + + var clientProvider = _serviceProvider?.GetRequiredService() ?? new HTXUserClientProvider(); + var restClient = clientProvider.GetRestClient(userIdentifier, credentials, environment); + var socketClient = clientProvider.GetSocketClient(userIdentifier, credentials, environment); + return new HTXUserFuturesDataTracker( + _serviceProvider?.GetRequiredService>() ?? new NullLogger(), + restClient, + socketClient, + userIdentifier, + config, + exchangeParams + ); + } } } diff --git a/HTX.Net/HTXUserDataTracker.cs b/HTX.Net/HTXUserDataTracker.cs new file mode 100644 index 00000000..ab643228 --- /dev/null +++ b/HTX.Net/HTXUserDataTracker.cs @@ -0,0 +1,65 @@ +using HTX.Net.Interfaces.Clients; +using CryptoExchange.Net.SharedApis; +using CryptoExchange.Net.Trackers.UserData; +using CryptoExchange.Net.Trackers.UserData.Objects; +using Microsoft.Extensions.Logging; + +namespace HTX.Net +{ + /// + public class HTXUserSpotDataTracker : UserSpotDataTracker + { + /// + /// ctor + /// + public HTXUserSpotDataTracker( + ILogger logger, + IHTXRestClient restClient, + IHTXSocketClient socketClient, + string? userIdentifier, + SpotUserDataTrackerConfig config) : base( + logger, + restClient.SpotApi.SharedClient, + null, + restClient.SpotApi.SharedClient, + socketClient.SpotApi.SharedClient, + restClient.SpotApi.SharedClient, + socketClient.SpotApi.SharedClient, + socketClient.SpotApi.SharedClient, + userIdentifier, + config) + { + } + } + + /// + public class HTXUserFuturesDataTracker : UserFuturesDataTracker + { + /// + protected override bool WebsocketPositionUpdatesAreFullSnapshots => true; + + /// + /// ctor + /// + public HTXUserFuturesDataTracker( + ILogger logger, + IHTXRestClient restClient, + IHTXSocketClient socketClient, + string? userIdentifier, + FuturesUserDataTrackerConfig config, + ExchangeParameters? exchangeParameters) : base(logger, + restClient.UsdtFuturesApi.SharedClient, + null, + restClient.UsdtFuturesApi.SharedClient, + socketClient.UsdtFuturesApi.SharedClient, + restClient.UsdtFuturesApi.SharedClient, + socketClient.UsdtFuturesApi.SharedClient, + socketClient.UsdtFuturesApi.SharedClient, + socketClient.UsdtFuturesApi.SharedClient, + userIdentifier, + config, + exchangeParameters: exchangeParameters) + { + } + } +} diff --git a/HTX.Net/Interfaces/IHTXTrackerFactory.cs b/HTX.Net/Interfaces/IHTXTrackerFactory.cs index 091bfbd8..677423da 100644 --- a/HTX.Net/Interfaces/IHTXTrackerFactory.cs +++ b/HTX.Net/Interfaces/IHTXTrackerFactory.cs @@ -1,9 +1,42 @@ -namespace HTX.Net.Interfaces +using CryptoExchange.Net.SharedApis; +using CryptoExchange.Net.Trackers.UserData.Interfaces; +using CryptoExchange.Net.Trackers.UserData.Objects; + +namespace HTX.Net.Interfaces { /// /// Tracker factory /// public interface IHTXTrackerFactory : ITrackerFactory { + /// + /// Create a new Spot user data tracker + /// + /// User identifier + /// Configuration + /// Credentials + /// Environment + IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, SpotUserDataTrackerConfig config, ApiCredentials credentials, HTXEnvironment? environment = null); + /// + /// Create a new spot user data tracker + /// + /// Configuration + IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig config); + + /// + /// Create a new futures user data tracker + /// + /// User identifier + /// Configuration + /// Credentials + /// Margin mode + /// Environment + IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, FuturesUserDataTrackerConfig config, ApiCredentials credentials, SharedMarginMode marginMode, HTXEnvironment? environment = null); + /// + /// Create a new futures user data tracker + /// + /// Configuration + /// Margin mode + IUserFuturesDataTracker CreateUserFuturesDataTracker(FuturesUserDataTrackerConfig config, SharedMarginMode marginMode); } } From 037ea022309ef9dc008e86d464fbbce31d4527f6 Mon Sep 17 00:00:00 2001 From: JKorf Date: Wed, 4 Feb 2026 21:46:28 +0100 Subject: [PATCH 6/8] Added shared deposit status --- HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs index a9207cd4..3f1d1a2f 100644 --- a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs +++ b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiShared.cs @@ -744,7 +744,15 @@ async Task> IDepositRestClient.GetDepositsAsy if (deposits.Data.Count() == (request.Limit ?? 100)) nextToken = new FromIdToken(deposits.Data.Min(x => x.Id - 1).ToString()); - return deposits.AsExchangeResult(Exchange, TradingMode.Spot, deposits.Data.Select(x => new SharedDeposit(x.Asset!.ToUpperInvariant(), x.Quantity, x.Status == WithdrawDepositStatus.Safe, x.CreateTime) + return deposits.AsExchangeResult(Exchange, TradingMode.Spot, deposits.Data.Select(x => + new SharedDeposit( + x.Asset!.ToUpperInvariant(), + x.Quantity, + x.Status == WithdrawDepositStatus.Safe, + x.CreateTime, + x.Status == WithdrawDepositStatus.Safe ? SharedTransferStatus.Completed + : x.Status == WithdrawDepositStatus.Repealed || x.Status == WithdrawDepositStatus.ConfirmError || x.Status == WithdrawDepositStatus.WalletReject || x.Status == WithdrawDepositStatus.Reject || x.Status == WithdrawDepositStatus.Canceled || x.Status == WithdrawDepositStatus.Failed ? SharedTransferStatus.Failed + : SharedTransferStatus.Failed) { Id = x.Id.ToString(), Network = x.Network, From d7490ca6293a3ffdc22303cf7085912c75c57e24 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Thu, 5 Feb 2026 16:28:33 +0100 Subject: [PATCH 7/8] wip --- HTX.Net/HTX.Net.xml | 12 ++++++------ HTX.Net/HTXTrackerFactory.cs | 8 ++++---- HTX.Net/HTXUserDataTracker.cs | 8 ++++---- HTX.Net/Interfaces/IHTXTrackerFactory.cs | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/HTX.Net/HTX.Net.xml b/HTX.Net/HTX.Net.xml index 4a20cf26..099dd2fa 100644 --- a/HTX.Net/HTX.Net.xml +++ b/HTX.Net/HTX.Net.xml @@ -8027,13 +8027,13 @@ - + - + - + @@ -11294,7 +11294,7 @@ Tracker factory - + Create a new Spot user data tracker @@ -11309,7 +11309,7 @@ Configuration - + Create a new futures user data tracker @@ -11319,7 +11319,7 @@ Margin mode Environment - + Create a new futures user data tracker diff --git a/HTX.Net/HTXTrackerFactory.cs b/HTX.Net/HTXTrackerFactory.cs index 02732c1b..2d8741fb 100644 --- a/HTX.Net/HTXTrackerFactory.cs +++ b/HTX.Net/HTXTrackerFactory.cs @@ -102,7 +102,7 @@ public ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, ); } /// - public IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig config) + public IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig? config = null) { var restClient = _serviceProvider?.GetRequiredService() ?? new HTXRestClient(); var socketClient = _serviceProvider?.GetRequiredService() ?? new HTXSocketClient(); @@ -116,7 +116,7 @@ public IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig } /// - public IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, SpotUserDataTrackerConfig config, ApiCredentials credentials, HTXEnvironment? environment = null) + public IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, ApiCredentials credentials, SpotUserDataTrackerConfig? config = null, HTXEnvironment? environment = null) { var clientProvider = _serviceProvider?.GetRequiredService() ?? new HTXUserClientProvider(); var restClient = clientProvider.GetRestClient(userIdentifier, credentials, environment); @@ -131,7 +131,7 @@ public IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, Spo } /// - public IUserFuturesDataTracker CreateUserFuturesDataTracker(FuturesUserDataTrackerConfig config, SharedMarginMode marginMode) + public IUserFuturesDataTracker CreateUserFuturesDataTracker(SharedMarginMode marginMode, FuturesUserDataTrackerConfig? config = null) { var exchangeParams = new ExchangeParameters(new ExchangeParameter("HTX", "MarginMode", marginMode)); @@ -148,7 +148,7 @@ public IUserFuturesDataTracker CreateUserFuturesDataTracker(FuturesUserDataTrack } /// - public IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, FuturesUserDataTrackerConfig config, ApiCredentials credentials, SharedMarginMode marginMode, HTXEnvironment? environment = null) + public IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, ApiCredentials credentials, SharedMarginMode marginMode, FuturesUserDataTrackerConfig? config = null, HTXEnvironment? environment = null) { var exchangeParams = new ExchangeParameters(new ExchangeParameter("HTX", "MarginMode", marginMode)); diff --git a/HTX.Net/HTXUserDataTracker.cs b/HTX.Net/HTXUserDataTracker.cs index ab643228..7c1384f6 100644 --- a/HTX.Net/HTXUserDataTracker.cs +++ b/HTX.Net/HTXUserDataTracker.cs @@ -17,7 +17,7 @@ public HTXUserSpotDataTracker( IHTXRestClient restClient, IHTXSocketClient socketClient, string? userIdentifier, - SpotUserDataTrackerConfig config) : base( + SpotUserDataTrackerConfig? config) : base( logger, restClient.SpotApi.SharedClient, null, @@ -27,7 +27,7 @@ public HTXUserSpotDataTracker( socketClient.SpotApi.SharedClient, socketClient.SpotApi.SharedClient, userIdentifier, - config) + config ?? new SpotUserDataTrackerConfig()) { } } @@ -46,7 +46,7 @@ public HTXUserFuturesDataTracker( IHTXRestClient restClient, IHTXSocketClient socketClient, string? userIdentifier, - FuturesUserDataTrackerConfig config, + FuturesUserDataTrackerConfig? config, ExchangeParameters? exchangeParameters) : base(logger, restClient.UsdtFuturesApi.SharedClient, null, @@ -57,7 +57,7 @@ public HTXUserFuturesDataTracker( socketClient.UsdtFuturesApi.SharedClient, socketClient.UsdtFuturesApi.SharedClient, userIdentifier, - config, + config ?? new FuturesUserDataTrackerConfig(), exchangeParameters: exchangeParameters) { } diff --git a/HTX.Net/Interfaces/IHTXTrackerFactory.cs b/HTX.Net/Interfaces/IHTXTrackerFactory.cs index 677423da..4c230173 100644 --- a/HTX.Net/Interfaces/IHTXTrackerFactory.cs +++ b/HTX.Net/Interfaces/IHTXTrackerFactory.cs @@ -16,12 +16,12 @@ public interface IHTXTrackerFactory : ITrackerFactory /// Configuration /// Credentials /// Environment - IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, SpotUserDataTrackerConfig config, ApiCredentials credentials, HTXEnvironment? environment = null); + IUserSpotDataTracker CreateUserSpotDataTracker(string userIdentifier, ApiCredentials credentials, SpotUserDataTrackerConfig? config = null, HTXEnvironment? environment = null); /// /// Create a new spot user data tracker /// /// Configuration - IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig config); + IUserSpotDataTracker CreateUserSpotDataTracker(SpotUserDataTrackerConfig? config = null); /// /// Create a new futures user data tracker @@ -31,12 +31,12 @@ public interface IHTXTrackerFactory : ITrackerFactory /// Credentials /// Margin mode /// Environment - IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, FuturesUserDataTrackerConfig config, ApiCredentials credentials, SharedMarginMode marginMode, HTXEnvironment? environment = null); + IUserFuturesDataTracker CreateUserFuturesDataTracker(string userIdentifier, ApiCredentials credentials, SharedMarginMode marginMode, FuturesUserDataTrackerConfig? config = null, HTXEnvironment? environment = null); /// /// Create a new futures user data tracker /// /// Configuration /// Margin mode - IUserFuturesDataTracker CreateUserFuturesDataTracker(FuturesUserDataTrackerConfig config, SharedMarginMode marginMode); + IUserFuturesDataTracker CreateUserFuturesDataTracker(SharedMarginMode marginMode, FuturesUserDataTrackerConfig? config = null); } } From 7427c3ca39cd9f13f065b5493054ae678c82704e Mon Sep 17 00:00:00 2001 From: Jkorf Date: Fri, 6 Feb 2026 13:10:59 +0100 Subject: [PATCH 8/8] CryptoExchange.Net update --- HTX.Net/HTX.Net.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/HTX.Net/HTX.Net.csproj b/HTX.Net/HTX.Net.csproj index b3e997bb..66d562d2 100644 --- a/HTX.Net/HTX.Net.csproj +++ b/HTX.Net/HTX.Net.csproj @@ -52,12 +52,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - \ No newline at end of file