diff --git a/CryptoExchange.Net/Trackers/UserData/UserDataTracker.cs b/CryptoExchange.Net/Trackers/UserData/UserDataTracker.cs
index ff27446..84131e0 100644
--- a/CryptoExchange.Net/Trackers/UserData/UserDataTracker.cs
+++ b/CryptoExchange.Net/Trackers/UserData/UserDataTracker.cs
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.Trackers.UserData
@@ -22,6 +23,11 @@ namespace CryptoExchange.Net.Trackers.UserData
/// Listen key to use for subscriptions
///
protected string? _listenKey;
+ ///
+ /// Cts
+ ///
+ protected CancellationTokenSource? _cts;
+
///
/// List of data trackers
///
@@ -68,6 +74,8 @@ namespace CryptoExchange.Net.Trackers.UserData
///
public async Task StartAsync()
{
+ _cts = new CancellationTokenSource();
+
foreach(var tracker in DataTrackers)
tracker.OnConnectedChange += (x) => OnConnectedChange?.Invoke(tracker.DataType, x);
@@ -100,12 +108,21 @@ namespace CryptoExchange.Net.Trackers.UserData
public async Task StopAsync()
{
_logger.LogDebug("Stopping UserDataTracker");
+ _cts?.Cancel();
+
var tasks = new List();
foreach (var dataTracker in DataTrackers)
tasks.Add(dataTracker.StopAsync());
+ await DoStopAsync().ConfigureAwait(false);
await Task.WhenAll(tasks).ConfigureAwait(false);
_logger.LogDebug("Stopped UserDataTracker");
}
+
+ ///
+ /// Stop implementation
+ ///
+ ///
+ protected virtual Task DoStopAsync() => Task.CompletedTask;
}
}
diff --git a/CryptoExchange.Net/Trackers/UserData/UserFuturesDataTracker.cs b/CryptoExchange.Net/Trackers/UserData/UserFuturesDataTracker.cs
index 2e05306..26d3268 100644
--- a/CryptoExchange.Net/Trackers/UserData/UserFuturesDataTracker.cs
+++ b/CryptoExchange.Net/Trackers/UserData/UserFuturesDataTracker.cs
@@ -20,6 +20,7 @@ namespace CryptoExchange.Net.Trackers.UserData
private readonly IFuturesSymbolRestClient _symbolClient;
private readonly IListenKeyRestClient? _listenKeyClient;
private readonly ExchangeParameters? _exchangeParameters;
+ private Task? _lkKeepAliveTask;
///
protected override UserDataItemTracker[] DataTrackers { get; }
@@ -115,10 +116,38 @@ namespace CryptoExchange.Net.Trackers.UserData
return lkResult;
}
+ _lkKeepAliveTask = KeepAliveListenKeyAsync();
+
_listenKey = lkResult.Data;
}
return CallResult.SuccessResult;
}
+
+ ///
+ protected override async Task DoStopAsync()
+ {
+ if (_lkKeepAliveTask != null)
+ await _lkKeepAliveTask.ConfigureAwait(false);
+ }
+
+ private async Task KeepAliveListenKeyAsync()
+ {
+ var interval = TimeSpan.FromMinutes(30);
+ while (!_cts!.IsCancellationRequested)
+ {
+ try { await Task.Delay(interval, _cts.Token).ConfigureAwait(false); } catch (Exception)
+ {
+ break;
+ }
+
+ var result = await _listenKeyClient!.KeepAliveListenKeyAsync(new KeepAliveListenKeyRequest(_listenKey!, TradingMode.Spot)).ConfigureAwait(false);
+ if (!result)
+ _logger.LogWarning("Listen key keep alive failed: " + result.Error);
+
+ // If failed shorten the delay to allow a couple more retries
+ interval = result ? TimeSpan.FromMinutes(30) : TimeSpan.FromMinutes(5);
+ }
+ }
}
}
diff --git a/CryptoExchange.Net/Trackers/UserData/UserSpotDataTracker.cs b/CryptoExchange.Net/Trackers/UserData/UserSpotDataTracker.cs
index 22f78dd..6856bca 100644
--- a/CryptoExchange.Net/Trackers/UserData/UserSpotDataTracker.cs
+++ b/CryptoExchange.Net/Trackers/UserData/UserSpotDataTracker.cs
@@ -7,6 +7,7 @@ using System.Linq;
using CryptoExchange.Net.Trackers.UserData.Interfaces;
using CryptoExchange.Net.Trackers.UserData.Objects;
using CryptoExchange.Net.Trackers.UserData.ItemTrackers;
+using System;
namespace CryptoExchange.Net.Trackers.UserData
{
@@ -18,6 +19,7 @@ namespace CryptoExchange.Net.Trackers.UserData
private readonly ISpotSymbolRestClient _symbolClient;
private readonly IListenKeyRestClient? _listenKeyClient;
private readonly ExchangeParameters? _exchangeParameters;
+ private Task? _lkKeepAliveTask;
///
protected override UserDataItemTracker[] DataTrackers { get; }
@@ -91,10 +93,39 @@ namespace CryptoExchange.Net.Trackers.UserData
return lkResult;
}
+ _lkKeepAliveTask = KeepAliveListenKeyAsync();
+
_listenKey = lkResult.Data;
}
return CallResult.SuccessResult;
}
+
+ ///
+ protected override async Task DoStopAsync()
+ {
+ if (_lkKeepAliveTask != null)
+ await _lkKeepAliveTask.ConfigureAwait(false);
+ }
+
+ private async Task KeepAliveListenKeyAsync()
+ {
+ var interval = TimeSpan.FromMinutes(30);
+ while (!_cts!.IsCancellationRequested)
+ {
+ try { await Task.Delay(interval, _cts.Token).ConfigureAwait(false); }
+ catch (Exception)
+ {
+ break;
+ }
+
+ var result = await _listenKeyClient!.KeepAliveListenKeyAsync(new KeepAliveListenKeyRequest(_listenKey!, TradingMode.Spot)).ConfigureAwait(false);
+ if (!result)
+ _logger.LogWarning("Listen key keep alive failed: " + result.Error);
+
+ // If failed shorten the delay to allow a couple more retries
+ interval = result ? TimeSpan.FromMinutes(30) : TimeSpan.FromMinutes(5);
+ }
+ }
}
}