mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-10-27 08:27:19 +00:00
Compare commits
No commits in common. "a832f0e4d40599da4d2d04541cfdd6dcf9182968" and "d88fb0d35676daef52eed94bfb7b034181fca1cd" have entirely different histories.
a832f0e4d4
...
d88fb0d356
@ -6,9 +6,9 @@
|
||||
<PackageId>CryptoExchange.Net.Protobuf</PackageId>
|
||||
<Authors>JKorf</Authors>
|
||||
<Description>Protobuf support for CryptoExchange.Net</Description>
|
||||
<PackageVersion>9.8.0</PackageVersion>
|
||||
<AssemblyVersion>9.8.0</AssemblyVersion>
|
||||
<FileVersion>9.8.0</FileVersion>
|
||||
<PackageVersion>9.7.0</PackageVersion>
|
||||
<AssemblyVersion>9.7.0</AssemblyVersion>
|
||||
<FileVersion>9.7.0</FileVersion>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@ -41,7 +41,7 @@
|
||||
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CryptoExchange.Net" Version="9.8.0" />
|
||||
<PackageReference Include="CryptoExchange.Net" Version="9.7.0" />
|
||||
<PackageReference Include="protobuf-net" Version="3.2.56" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -5,9 +5,6 @@
|
||||
Protobuf support for CryptoExchange.Net.
|
||||
|
||||
## Release notes
|
||||
* Version 9.8.0 - 30 Sep 2025
|
||||
* Updated CryptoExchange.Net version to 9.8.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
||||
|
||||
* Version 9.7.0 - 01 Sep 2025
|
||||
* Updated CryptoExchange.Net version to 9.7.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ namespace CryptoExchange.Net.UnitTests
|
||||
[TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.532, 0.532)]
|
||||
[TestCase(0.1, 1, 0.0001, RoundingType.Down, 0.5516592, 0.5516)]
|
||||
[TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.5516592, 0.5517)]
|
||||
[TestCase(0, 1, 0.000000001, RoundingType.Closest, 0.0000097232, 0.000009723)]
|
||||
public void AdjustValueStepTests(decimal min, decimal max, decimal? step, RoundingType roundingType, decimal input, decimal expected)
|
||||
{
|
||||
var result = ExchangeHelpers.AdjustValueStep(min, max, step, roundingType, input);
|
||||
|
||||
@ -227,7 +227,7 @@ namespace CryptoExchange.Net.Clients
|
||||
while (true)
|
||||
{
|
||||
// Get a new or existing socket connection
|
||||
var socketResult = await GetSocketConnection(url, subscription.Authenticated, false, ct, subscription.Topic).ConfigureAwait(false);
|
||||
var socketResult = await GetSocketConnection(url, subscription.Authenticated, false, subscription.Topic).ConfigureAwait(false);
|
||||
if (!socketResult)
|
||||
return socketResult.As<UpdateSubscription>(null);
|
||||
|
||||
@ -343,7 +343,7 @@ namespace CryptoExchange.Net.Clients
|
||||
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var socketResult = await GetSocketConnection(url, query.Authenticated, true, ct).ConfigureAwait(false);
|
||||
var socketResult = await GetSocketConnection(url, query.Authenticated, true).ConfigureAwait(false);
|
||||
if (!socketResult)
|
||||
return socketResult.As<THandlerResponse>(default);
|
||||
|
||||
@ -494,56 +494,25 @@ namespace CryptoExchange.Net.Clients
|
||||
/// <param name="address">The address the socket is for</param>
|
||||
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
||||
/// <param name="dedicatedRequestConnection">Whether a dedicated request connection should be returned</param>
|
||||
/// <param name="ct">Cancellation token</param>
|
||||
/// <param name="topic">The subscription topic, can be provided when multiple of the same topics are not allowed on a connection</param>
|
||||
/// <returns></returns>
|
||||
protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(string address, bool authenticated, bool dedicatedRequestConnection, CancellationToken ct, string? topic = null)
|
||||
protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(string address, bool authenticated, bool dedicatedRequestConnection, string? topic = null)
|
||||
{
|
||||
var socketQuery = socketConnections.Where(s => s.Value.Tag.TrimEnd('/') == address.TrimEnd('/')
|
||||
var socketQuery = socketConnections.Where(s => (s.Value.Status == SocketConnection.SocketStatus.None || s.Value.Status == SocketConnection.SocketStatus.Connected)
|
||||
&& s.Value.Tag.TrimEnd('/') == address.TrimEnd('/')
|
||||
&& s.Value.ApiClient.GetType() == GetType()
|
||||
&& (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic)))
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
&& (s.Value.Authenticated == authenticated || !authenticated)
|
||||
&& (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic))
|
||||
&& s.Value.Connected);
|
||||
|
||||
// If all current socket connections are reconnecting or resubscribing wait for that to finish as we can probably use the existing connection
|
||||
var delayStart = DateTime.UtcNow;
|
||||
var delayed = false;
|
||||
while (socketQuery.Count >= 1 && socketQuery.All(x => x.Status == SocketConnection.SocketStatus.Reconnecting || x.Status == SocketConnection.SocketStatus.Resubscribing))
|
||||
{
|
||||
if (DateTime.UtcNow - delayStart > TimeSpan.FromSeconds(10))
|
||||
{
|
||||
if (socketQuery.Count >= 1 && socketQuery.All(x => x.Status == SocketConnection.SocketStatus.Reconnecting || x.Status == SocketConnection.SocketStatus.Resubscribing))
|
||||
{
|
||||
// If after this time we still trying to reconnect/reprocess there is some issue in the connection
|
||||
_logger.TimeoutWaitingForReconnectingSocket();
|
||||
return new CallResult<SocketConnection>(new CantConnectError());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
delayed = true;
|
||||
try { await Task.Delay(50, ct).ConfigureAwait(false); } catch (Exception) { }
|
||||
|
||||
if (ct.IsCancellationRequested)
|
||||
return new CallResult<SocketConnection>(new CancellationRequestedError());
|
||||
}
|
||||
|
||||
if (delayed)
|
||||
_logger.WaitedForReconnectingSocket((long)(DateTime.UtcNow - delayStart).TotalMilliseconds);
|
||||
|
||||
socketQuery = socketQuery.Where(s => (s.Status == SocketConnection.SocketStatus.None || s.Status == SocketConnection.SocketStatus.Connected)
|
||||
&& (s.Authenticated == authenticated || !authenticated)
|
||||
&& s.Connected).ToList();
|
||||
|
||||
SocketConnection? connection;
|
||||
SocketConnection connection;
|
||||
if (!dedicatedRequestConnection)
|
||||
{
|
||||
connection = socketQuery.Where(s => !s.DedicatedRequestConnection.IsDedicatedRequestConnection).OrderBy(s => s.UserSubscriptionCount).FirstOrDefault();
|
||||
connection = socketQuery.Where(s => !s.Value.DedicatedRequestConnection.IsDedicatedRequestConnection).OrderBy(s => s.Value.UserSubscriptionCount).FirstOrDefault().Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
connection = socketQuery.Where(s => s.DedicatedRequestConnection.IsDedicatedRequestConnection).FirstOrDefault();
|
||||
connection = socketQuery.Where(s => s.Value.DedicatedRequestConnection.IsDedicatedRequestConnection).FirstOrDefault().Value;
|
||||
if (connection != null && !connection.DedicatedRequestConnection.Authenticated)
|
||||
// Mark dedicated request connection as authenticated if the request is authenticated
|
||||
connection.DedicatedRequestConnection.Authenticated = authenticated;
|
||||
@ -551,13 +520,10 @@ namespace CryptoExchange.Net.Clients
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
if (connection.UserSubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget
|
||||
|| (socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections) && socketConnections.All(s => s.Value.UserSubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
|
||||
{
|
||||
if (connection.UserSubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections) && socketConnections.All(s => s.Value.UserSubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
|
||||
// Use existing socket if it has less than target connections OR it has the least connections and we can't make new
|
||||
return new CallResult<SocketConnection>(connection);
|
||||
}
|
||||
}
|
||||
|
||||
var connectionAddress = await GetConnectionUrlAsync(address, authenticated).ConfigureAwait(false);
|
||||
if (!connectionAddress)
|
||||
@ -750,7 +716,7 @@ namespace CryptoExchange.Net.Clients
|
||||
{
|
||||
foreach (var item in DedicatedConnectionConfigs)
|
||||
{
|
||||
var socketResult = await GetSocketConnection(item.SocketAddress, item.Authenticated, true, CancellationToken.None).ConfigureAwait(false);
|
||||
var socketResult = await GetSocketConnection(item.SocketAddress, item.Authenticated, true).ConfigureAwait(false);
|
||||
if (!socketResult)
|
||||
return socketResult.AsDataless();
|
||||
|
||||
|
||||
@ -140,10 +140,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
||||
_ => throw new Exception("Invalid token type for enum deserialization: " + reader.TokenType)
|
||||
};
|
||||
|
||||
if (stringValue is null)
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
return null;
|
||||
|
||||
if (!GetValue(enumType, stringValue, out var result))
|
||||
if (!GetValue(enumType, stringValue!, out var result))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
@ -204,13 +204,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
||||
return false;
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
// An empty/null value will always fail when parsing, so just return here
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If no explicit mapping is found try to parse string
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
<PackageId>CryptoExchange.Net</PackageId>
|
||||
<Authors>JKorf</Authors>
|
||||
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
||||
<PackageVersion>9.8.0</PackageVersion>
|
||||
<AssemblyVersion>9.8.0</AssemblyVersion>
|
||||
<FileVersion>9.8.0</FileVersion>
|
||||
<PackageVersion>9.7.0</PackageVersion>
|
||||
<AssemblyVersion>9.7.0</AssemblyVersion>
|
||||
<FileVersion>9.7.0</FileVersion>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
|
||||
@ -89,6 +89,8 @@ namespace CryptoExchange.Net
|
||||
else value += (step.Value - offset);
|
||||
}
|
||||
|
||||
value = RoundDown(value, 8);
|
||||
|
||||
return value.Normalize();
|
||||
}
|
||||
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
using CryptoExchange.Net.SharedApis;
|
||||
using CryptoExchange.Net.Trackers.Klines;
|
||||
using CryptoExchange.Net.Trackers.Trades;
|
||||
using System;
|
||||
|
||||
namespace CryptoExchange.Net.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracker factory
|
||||
/// </summary>
|
||||
public interface ITrackerFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the factory supports creating a KlineTracker instance for this symbol and interval
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
/// <param name="interval">The kline interval</param>
|
||||
bool CanCreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new kline tracker
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
/// <param name="interval">Kline interval</param>
|
||||
/// <param name="limit">The max amount of klines to retain</param>
|
||||
/// <param name="period">The max period the data should be retained</param>
|
||||
/// <returns></returns>
|
||||
IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the factory supports creating a TradeTracker instance for this symbol
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
bool CanCreateTradeTracker(SharedSymbol symbol);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new trade tracker for a symbol
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
/// <param name="limit">The max amount of trades to retain</param>
|
||||
/// <param name="period">The max period the data should be retained</param>
|
||||
/// <returns></returns>
|
||||
ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null);
|
||||
}
|
||||
}
|
||||
@ -23,8 +23,6 @@ namespace CryptoExchange.Net.Logging.Extensions
|
||||
private static readonly Action<ILogger, int, int, Exception?> _unsubscribingSubscription;
|
||||
private static readonly Action<ILogger, int, Exception?> _reconnectingAllConnections;
|
||||
private static readonly Action<ILogger, DateTime, Exception?> _addingRetryAfterGuard;
|
||||
private static readonly Action<ILogger, Exception?> _timeoutWaitingForReconnectingSocket;
|
||||
private static readonly Action<ILogger, long, Exception?> _waitedForReconnectingSocket;
|
||||
|
||||
static SocketApiClientLoggingExtension()
|
||||
{
|
||||
@ -112,16 +110,6 @@ namespace CryptoExchange.Net.Logging.Extensions
|
||||
LogLevel.Warning,
|
||||
new EventId(3018, "AddRetryAfterGuard"),
|
||||
"Adding RetryAfterGuard ({RetryAfter}) because the connection attempt was rate limited");
|
||||
|
||||
_timeoutWaitingForReconnectingSocket = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
new EventId(3019, "TimeoutWaitingForReconnectingSocket"),
|
||||
"Timeout while waiting for existing socket reconnection, failing request");
|
||||
|
||||
_waitedForReconnectingSocket = LoggerMessage.Define<long>(
|
||||
LogLevel.Trace,
|
||||
new EventId(3020, "WaitedForReconnectingSocket"),
|
||||
"Waited for reconnecting socket for {Timespan}ms");
|
||||
}
|
||||
|
||||
public static void FailedToAddSubscriptionRetryOnDifferentConnection(this ILogger logger, int socketId)
|
||||
@ -208,14 +196,5 @@ namespace CryptoExchange.Net.Logging.Extensions
|
||||
{
|
||||
_addingRetryAfterGuard(logger, retryAfter, null);
|
||||
}
|
||||
|
||||
public static void TimeoutWaitingForReconnectingSocket(this ILogger logger)
|
||||
{
|
||||
_timeoutWaitingForReconnectingSocket(logger, null);
|
||||
}
|
||||
public static void WaitedForReconnectingSocket(this ILogger logger, long milliseconds)
|
||||
{
|
||||
_waitedForReconnectingSocket(logger, milliseconds, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
using CryptoExchange.Net.Sockets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CryptoExchange.Net.Objects.Sockets
|
||||
@ -14,21 +12,13 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
private readonly SocketConnection _connection;
|
||||
private readonly Subscription _listener;
|
||||
|
||||
private object _eventLock = new object();
|
||||
private List<Action> _connectionClosedEventHandlers = new List<Action>();
|
||||
private List<Action> _connectionLostEventHandlers = new List<Action>();
|
||||
private List<Action<Error>> _resubscribeFailedEventHandlers = new List<Action<Error>>();
|
||||
private List<Action<TimeSpan>> _connectionRestoredEventHandlers = new List<Action<TimeSpan>>();
|
||||
private List<Action> _activityPausedEventHandlers = new List<Action>();
|
||||
private List<Action> _activityUnpausedEventHandlers = new List<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Event when the connection is lost. The socket will automatically reconnect when possible.
|
||||
/// </summary>
|
||||
public event Action ConnectionLost
|
||||
{
|
||||
add { lock (_eventLock) _connectionLostEventHandlers.Add(value); }
|
||||
remove { lock (_eventLock) _connectionLostEventHandlers.Remove(value); }
|
||||
add => _connection.ConnectionLost += value;
|
||||
remove => _connection.ConnectionLost -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -36,8 +26,8 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
/// </summary>
|
||||
public event Action ConnectionClosed
|
||||
{
|
||||
add { lock (_eventLock) _connectionClosedEventHandlers.Add(value); }
|
||||
remove { lock (_eventLock) _connectionClosedEventHandlers.Remove(value); }
|
||||
add => _connection.ConnectionClosed += value;
|
||||
remove => _connection.ConnectionClosed -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -45,8 +35,8 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
/// </summary>
|
||||
public event Action<Error> ResubscribingFailed
|
||||
{
|
||||
add { lock (_eventLock) _resubscribeFailedEventHandlers.Add(value); }
|
||||
remove { lock (_eventLock) _resubscribeFailedEventHandlers.Remove(value); }
|
||||
add => _connection.ResubscribingFailed += value;
|
||||
remove => _connection.ResubscribingFailed -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -56,8 +46,8 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
/// </summary>
|
||||
public event Action<TimeSpan> ConnectionRestored
|
||||
{
|
||||
add { lock (_eventLock) _connectionRestoredEventHandlers.Add(value); }
|
||||
remove { lock (_eventLock) _connectionRestoredEventHandlers.Remove(value); }
|
||||
add => _connection.ConnectionRestored += value;
|
||||
remove => _connection.ConnectionRestored -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -65,8 +55,8 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
/// </summary>
|
||||
public event Action ActivityPaused
|
||||
{
|
||||
add { lock (_eventLock) _activityPausedEventHandlers.Add(value); }
|
||||
remove { lock (_eventLock) _activityPausedEventHandlers.Remove(value); }
|
||||
add => _connection.ActivityPaused += value;
|
||||
remove => _connection.ActivityPaused -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -74,8 +64,8 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
/// </summary>
|
||||
public event Action ActivityUnpaused
|
||||
{
|
||||
add { lock (_eventLock) _activityUnpausedEventHandlers.Add(value); }
|
||||
remove { lock (_eventLock) _activityUnpausedEventHandlers.Remove(value); }
|
||||
add => _connection.ActivityUnpaused += value;
|
||||
remove => _connection.ActivityUnpaused -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -105,85 +95,7 @@ namespace CryptoExchange.Net.Objects.Sockets
|
||||
public UpdateSubscription(SocketConnection connection, Subscription subscription)
|
||||
{
|
||||
_connection = connection;
|
||||
_connection.ConnectionClosed += HandleConnectionClosedEvent;
|
||||
_connection.ConnectionLost += HandleConnectionLostEvent;
|
||||
_connection.ConnectionRestored += HandleConnectionRestoredEvent;
|
||||
_connection.ResubscribingFailed += HandleResubscribeFailedEvent;
|
||||
_connection.ActivityPaused += HandlePausedEvent;
|
||||
_connection.ActivityUnpaused += HandleUnpausedEvent;
|
||||
|
||||
_listener = subscription;
|
||||
_listener.Unsubscribed += HandleUnsubscribed;
|
||||
}
|
||||
|
||||
private void HandleUnsubscribed()
|
||||
{
|
||||
_connection.ConnectionClosed -= HandleConnectionClosedEvent;
|
||||
_connection.ConnectionLost -= HandleConnectionLostEvent;
|
||||
_connection.ConnectionRestored -= HandleConnectionRestoredEvent;
|
||||
_connection.ResubscribingFailed -= HandleResubscribeFailedEvent;
|
||||
_connection.ActivityPaused -= HandlePausedEvent;
|
||||
_connection.ActivityUnpaused -= HandleUnpausedEvent;
|
||||
}
|
||||
|
||||
private void HandleConnectionClosedEvent()
|
||||
{
|
||||
List<Action> handlers;
|
||||
lock (_eventLock)
|
||||
handlers = _connectionClosedEventHandlers.ToList();
|
||||
|
||||
foreach(var callback in handlers)
|
||||
callback();
|
||||
}
|
||||
|
||||
private void HandleConnectionLostEvent()
|
||||
{
|
||||
List<Action> handlers;
|
||||
lock (_eventLock)
|
||||
handlers = _connectionLostEventHandlers.ToList();
|
||||
|
||||
foreach (var callback in handlers)
|
||||
callback();
|
||||
}
|
||||
|
||||
private void HandleConnectionRestoredEvent(TimeSpan period)
|
||||
{
|
||||
List<Action<TimeSpan>> handlers;
|
||||
lock (_eventLock)
|
||||
handlers = _connectionRestoredEventHandlers.ToList();
|
||||
|
||||
foreach (var callback in handlers)
|
||||
callback(period);
|
||||
}
|
||||
|
||||
private void HandleResubscribeFailedEvent(Error error)
|
||||
{
|
||||
List<Action<Error>> handlers;
|
||||
lock (_eventLock)
|
||||
handlers = _resubscribeFailedEventHandlers.ToList();
|
||||
|
||||
foreach (var callback in handlers)
|
||||
callback(error);
|
||||
}
|
||||
|
||||
private void HandlePausedEvent()
|
||||
{
|
||||
List<Action> handlers;
|
||||
lock (_eventLock)
|
||||
handlers = _activityPausedEventHandlers.ToList();
|
||||
|
||||
foreach (var callback in handlers)
|
||||
callback();
|
||||
}
|
||||
|
||||
private void HandleUnpausedEvent()
|
||||
{
|
||||
List<Action> handlers;
|
||||
lock (_eventLock)
|
||||
handlers = _activityUnpausedEventHandlers.ToList();
|
||||
|
||||
foreach (var callback in handlers)
|
||||
callback();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -67,10 +67,6 @@ namespace CryptoExchange.Net.SharedApis
|
||||
/// Min number of confirmations
|
||||
/// </summary>
|
||||
public int? MinConfirmations { get; set; }
|
||||
/// <summary>
|
||||
/// The contract address
|
||||
/// </summary>
|
||||
public string? ContractAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
|
||||
@ -706,8 +706,6 @@ namespace CryptoExchange.Net.Sockets
|
||||
|
||||
lock (_listenersLock)
|
||||
_listeners.Remove(subscription);
|
||||
|
||||
subscription.InvokeUnsubscribedHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -74,10 +74,6 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// Exception event
|
||||
/// </summary>
|
||||
public event Action<Exception>? Exception;
|
||||
/// <summary>
|
||||
/// Listener unsubscribed event
|
||||
/// </summary>
|
||||
public event Action? Unsubscribed;
|
||||
|
||||
/// <summary>
|
||||
/// Subscription topic
|
||||
@ -185,14 +181,6 @@ namespace CryptoExchange.Net.Sockets
|
||||
Exception?.Invoke(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke the unsubscribed event
|
||||
/// </summary>
|
||||
public void InvokeUnsubscribedHandler()
|
||||
{
|
||||
Unsubscribed?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State of this subscription
|
||||
/// </summary>
|
||||
|
||||
@ -64,14 +64,6 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
|
||||
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
|
||||
|
||||
## Release notes
|
||||
* Version 9.8.0 - 30 Sep 2025
|
||||
* Added ContractAddress to SharedAsset model
|
||||
* Added ITrackerFactory interface
|
||||
* Fixed UpdateSubscription still propagating connection events even though the specific listener is unsubscribed
|
||||
* Fixed ExchangeHelpers.AdjustValueStep high precision calculation
|
||||
* Fixed issue increasing the number of websocket connections increasing when sending a query when a previous connection was attempting to reconnect
|
||||
* Fixed EnumConverter to allow mapping empty string values
|
||||
|
||||
* Version 9.7.0 - 01 Sep 2025
|
||||
* Added LibraryHelpers.CreateHttpClientMessageHandle to standardize HttpMessageHandler creation
|
||||
* Added REST client option for selecting HTTP protocol version
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user