using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Trackers.UserData.Objects; using Microsoft.Extensions.Logging; using System.Threading.Tasks; namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers { /// /// Balance tracker implementation /// public class BalanceTracker : UserDataItemTracker { private readonly IGetBalancesRest _restClient; private readonly ISubscribeBalancesSocket? _socketClient; private readonly ExchangeParameters? _exchangeParameters; private readonly SharedAccountType _accountType; /// /// ctor /// public BalanceTracker( ILogger logger, UserDataSymbolTracker symbolTracker, IGetBalancesRest restClient, ISubscribeBalancesSocket? socketClient, SharedAccountType accountType, TrackerItemConfig config, ExchangeParameters? exchangeParameters = null ) : base(logger, symbolTracker, UserDataType.Balances, restClient.Exchange) { if (socketClient == null) config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; SetConfig(config); _restClient = restClient; _socketClient = socketClient; _exchangeParameters = exchangeParameters; _accountType = accountType; } /// protected override bool Update(SharedBalance existingItem, SharedBalance updateItem) { var changed = false; if (existingItem.Total != updateItem.Total) { existingItem.Total = updateItem.Total; changed = true; } if (existingItem.Available != updateItem.Available) { existingItem.Available = updateItem.Available; changed = true; } return changed; } /// protected override string GetKey(SharedBalance item) => item.Asset + item.IsolatedMarginSymbol; /// protected override bool? CheckIfUpdateShouldBeApplied(SharedBalance existingItem, SharedBalance updateItem) => true; /// protected override Task> DoSubscribeAsync() { if (_socketClient == null) return Task.FromResult(new WebSocketResult(default!, default!, default)); var accountType = _accountType == SharedAccountType.Spot ? TradingMode.Spot : _accountType == SharedAccountType.PerpetualInverseFutures ? TradingMode.PerpetualInverse : _accountType == SharedAccountType.DeliveryLinearFutures ? TradingMode.DeliveryLinear : _accountType == SharedAccountType.DeliveryInverseFutures ? TradingMode.DeliveryInverse : TradingMode.PerpetualLinear; return ExchangeHelpers.ProcessQueuedAsync( async handler => await _socketClient.SubscribeToBalanceUpdatesAsync(new SubscribeBalancesRequest(accountType, exchangeParameters: _exchangeParameters), handler, ct: _cts!.Token).ConfigureAwait(false), x => HandleUpdateAsync(UpdateSource.Push, x.Data))!; } /// protected override async Task DoPollAsync() { var balances = await _restClient.GetBalancesAsync(new GetBalancesRequest(accountType: _accountType, exchangeParameters: _exchangeParameters)).ConfigureAwait(false); if (balances.Success) await HandleUpdateAsync(UpdateSource.Poll, balances.Data).ConfigureAwait(false); else _initialPollingError ??= balances.Error; return !balances.Success; } } }