1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-10-23 19:41:02 +00:00

Compare commits

...

5 Commits

13 changed files with 71 additions and 25 deletions
+3 -3
View File
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net</PackageId> <PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors> <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> <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>13.2.0</PackageVersion> <PackageVersion>13.2.2</PackageVersion>
<AssemblyVersion>13.2.0</AssemblyVersion> <AssemblyVersion>13.2.2</AssemblyVersion>
<FileVersion>13.2.0</FileVersion> <FileVersion>13.2.2</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <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> <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> <RepositoryType>git</RepositoryType>
@@ -1083,6 +1083,10 @@ namespace CryptoExchange.Net.Sockets.Default
/// </summary> /// </summary>
protected bool IsCoveredByEarlierSubscription(Subscription subscription) protected bool IsCoveredByEarlierSubscription(Subscription subscription)
{ {
if (subscription.MessageRouter.Routes.Length == 0)
// Can be subscription which only knows what to listen to after receiving the subscribe response, for example Bitfinex
return false;
foreach (var other in _listeners.OfType<Subscription>()) foreach (var other in _listeners.OfType<Subscription>())
{ {
if (other == subscription) if (other == subscription)
@@ -168,12 +168,18 @@ namespace CryptoExchange.Net.Testing
{ {
// Validate request body // Validate request body
Dictionary<string, object> bodyParameters; Dictionary<string, object> bodyParameters;
if (result.RequestBody!.StartsWith("{") || result.RequestBody.StartsWith("[")) if (string.IsNullOrEmpty(result.RequestBody))
{ {
bodyParameters = new Dictionary<string, object>();
}
else if (result.RequestBody!.StartsWith("{") || result.RequestBody.StartsWith("["))
{
// Json
bodyParameters = JsonSerializer.Deserialize<Dictionary<string, object>>(result.RequestBody!)!; bodyParameters = JsonSerializer.Deserialize<Dictionary<string, object>>(result.RequestBody!)!;
} }
else else
{ {
// Form data
var splitKvp = result.RequestBody.Split('&'); var splitKvp = result.RequestBody.Split('&');
bodyParameters = splitKvp.Select(x => x.Split('=')).ToDictionary(x => x[0], x => (object)x[1]); bodyParameters = splitKvp.Select(x => x.Split('=')).ToDictionary(x => x[0], x => (object)x[1]);
} }
@@ -28,10 +28,11 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
SharedAccountType accountType, SharedAccountType accountType,
TrackerItemConfig config, TrackerItemConfig config,
ExchangeParameters? exchangeParameters = null ExchangeParameters? exchangeParameters = null
) : base(logger, symbolTracker, UserDataType.Balances, restClient.Exchange, config) ) : base(logger, symbolTracker, UserDataType.Balances, restClient.Exchange)
{ {
if (_socketClient == null) if (socketClient == null)
config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; config = config with { PollIntervalConnected = config.PollIntervalDisconnected };
SetConfig(config);
_restClient = restClient; _restClient = restClient;
_socketClient = socketClient; _socketClient = socketClient;
@@ -24,6 +24,7 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
private readonly Dictionary<string, int> _openOrderNotReturnedTimes = new(); private readonly Dictionary<string, int> _openOrderNotReturnedTimes = new();
private readonly TimeSpan _pollOverlapPeriod = TimeSpan.FromSeconds(3); private readonly TimeSpan _pollOverlapPeriod = TimeSpan.FromSeconds(3);
private bool _openOrderWarningDone = false;
internal event Func<UpdateSource, SharedUserTrade[], Task>? OnTradeUpdate; internal event Func<UpdateSource, SharedUserTrade[], Task>? OnTradeUpdate;
/// <summary> /// <summary>
@@ -39,10 +40,11 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
IEnumerable<SharedSymbol> symbols, IEnumerable<SharedSymbol> symbols,
bool onlyTrackProvidedSymbols, bool onlyTrackProvidedSymbols,
ExchangeParameters? exchangeParameters = null ExchangeParameters? exchangeParameters = null
) : base(logger, symbolTracker, UserDataType.Orders, openRestClient.Exchange, config) ) : base(logger, symbolTracker, UserDataType.Orders, openRestClient.Exchange)
{ {
if (_socketClient == null) if (socketClient == null)
config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; config = config with { PollIntervalConnected = config.PollIntervalDisconnected };
SetConfig(config);
_openOrdersClient = openRestClient; _openOrdersClient = openRestClient;
_closedOrdersClient = closedRestClient; _closedOrdersClient = closedRestClient;
@@ -234,7 +236,14 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
} }
else else
{ {
foreach (var symbol in _symbolTracker.GetTrackedSymbols()) var trackedSymbols = _symbolTracker.GetTrackedSymbols();
if (!_openOrderWarningDone && trackedSymbols.Count() == 0)
{
_openOrderWarningDone = true;
_logger.LogDebug("No tracked symbols specified while symbol is required to retrieve open orders. Tracked symbols should be updated before orders may be returned");
}
foreach (var symbol in trackedSymbols)
{ {
var openOrdersResult = await _openOrdersClient.GetOpenFuturesOrdersAsync(new GetOpenOrdersRequest(symbol, exchangeParameters: _exchangeParameters)).ConfigureAwait(false); var openOrdersResult = await _openOrdersClient.GetOpenFuturesOrdersAsync(new GetOpenOrdersRequest(symbol, exchangeParameters: _exchangeParameters)).ConfigureAwait(false);
if (!openOrdersResult.Success) if (!openOrdersResult.Success)
@@ -34,10 +34,11 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
IEnumerable<SharedSymbol> symbols, IEnumerable<SharedSymbol> symbols,
bool onlyTrackProvidedSymbols, bool onlyTrackProvidedSymbols,
ExchangeParameters? exchangeParameters = null ExchangeParameters? exchangeParameters = null
) : base(logger, symbolTracker, UserDataType.Trades, restClient.Exchange, config) ) : base(logger, symbolTracker, UserDataType.Trades, restClient.Exchange)
{ {
if (_socketClient == null) if (socketClient == null)
config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; config = config with { PollIntervalConnected = config.PollIntervalDisconnected };
SetConfig(config);
_restClient = restClient; _restClient = restClient;
_socketClient = socketClient; _socketClient = socketClient;
@@ -37,10 +37,11 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
bool onlyTrackProvidedSymbols, bool onlyTrackProvidedSymbols,
bool websocketPositionUpdatesAreFullSnapshots, bool websocketPositionUpdatesAreFullSnapshots,
ExchangeParameters? exchangeParameters = null ExchangeParameters? exchangeParameters = null
) : base(logger, symbolTracker, UserDataType.Positions, restClient.Exchange, config) ) : base(logger, symbolTracker, UserDataType.Positions, restClient.Exchange)
{ {
if (_socketClient == null) if (socketClient == null)
config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; config = config with { PollIntervalConnected = config.PollIntervalDisconnected };
SetConfig(config);
_restClient = restClient; _restClient = restClient;
_socketClient = socketClient; _socketClient = socketClient;
@@ -23,6 +23,7 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
private readonly bool _timeFilterSupportedClosedOrders; private readonly bool _timeFilterSupportedClosedOrders;
private readonly Dictionary<string, int> _openOrderNotReturnedTimes = new(); private readonly Dictionary<string, int> _openOrderNotReturnedTimes = new();
private readonly TimeSpan _pollOverlapPeriod = TimeSpan.FromSeconds(3); private readonly TimeSpan _pollOverlapPeriod = TimeSpan.FromSeconds(3);
private bool _openOrderWarningDone = false;
internal event Func<UpdateSource, SharedUserTrade[], Task>? OnTradeUpdate; internal event Func<UpdateSource, SharedUserTrade[], Task>? OnTradeUpdate;
@@ -39,10 +40,11 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
IEnumerable<SharedSymbol> symbols, IEnumerable<SharedSymbol> symbols,
bool onlyTrackProvidedSymbols, bool onlyTrackProvidedSymbols,
ExchangeParameters? exchangeParameters = null ExchangeParameters? exchangeParameters = null
) : base(logger, symbolTracker, UserDataType.Orders, openOrderClient.Exchange, config) ) : base(logger, symbolTracker, UserDataType.Orders, openOrderClient.Exchange)
{ {
if (_socketClient == null) if (socketClient == null)
config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; config = config with { PollIntervalConnected = config.PollIntervalDisconnected };
SetConfig(config);
_openOrderClient = openOrderClient; _openOrderClient = openOrderClient;
_closedOrderClient = closedOrderClient; _closedOrderClient = closedOrderClient;
@@ -244,7 +246,14 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
} }
else else
{ {
foreach (var symbol in _symbolTracker.GetTrackedSymbols()) var trackedSymbols = _symbolTracker.GetTrackedSymbols();
if (!_openOrderWarningDone && trackedSymbols.Count() == 0)
{
_openOrderWarningDone = true;
_logger.LogDebug("No tracked symbols specified while symbol is required to retrieve open orders. Tracked symbols should be updated before orders may be returned");
}
foreach (var symbol in trackedSymbols)
{ {
var openOrdersResult = await _openOrderClient.GetOpenSpotOrdersAsync(new GetOpenOrdersRequest(symbol, exchangeParameters: _exchangeParameters)).ConfigureAwait(false); var openOrdersResult = await _openOrderClient.GetOpenSpotOrdersAsync(new GetOpenOrdersRequest(symbol, exchangeParameters: _exchangeParameters)).ConfigureAwait(false);
if (!openOrdersResult.Success) if (!openOrdersResult.Success)
@@ -34,10 +34,11 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
IEnumerable<SharedSymbol> symbols, IEnumerable<SharedSymbol> symbols,
bool onlyTrackProvidedSymbols, bool onlyTrackProvidedSymbols,
ExchangeParameters? exchangeParameters = null ExchangeParameters? exchangeParameters = null
) : base(logger, symbolTracker, UserDataType.Trades, restClient.Exchange, config) ) : base(logger, symbolTracker, UserDataType.Trades, restClient.Exchange)
{ {
if (_socketClient == null) if (socketClient == null)
config = config with { PollIntervalConnected = config.PollIntervalDisconnected }; config = config with { PollIntervalConnected = config.PollIntervalDisconnected };
SetConfig(config);
_restClient = restClient; _restClient = restClient;
_socketClient = socketClient; _socketClient = socketClient;
@@ -242,15 +242,21 @@ namespace CryptoExchange.Net.Trackers.UserData.ItemTrackers
ILogger logger, ILogger logger,
UserDataSymbolTracker symbolTracker, UserDataSymbolTracker symbolTracker,
UserDataType dataType, UserDataType dataType,
string exchange, string exchange) : base(logger, dataType, exchange)
TrackerItemConfig config) : base(logger, dataType, exchange) {
_isSymbolModel = typeof(T).IsSubclassOf(typeof(SharedSymbolModel));
_symbolTracker = symbolTracker;
}
/// <summary>
/// Set the tracker item config
/// </summary>
protected void SetConfig(TrackerItemConfig config)
{ {
_pollIntervalDisconnected = config.PollIntervalDisconnected; _pollIntervalDisconnected = config.PollIntervalDisconnected;
_pollIntervalConnected = config.PollIntervalConnected; _pollIntervalConnected = config.PollIntervalConnected;
_pollAtStart = config.PollAtStart; _pollAtStart = config.PollAtStart;
_retentionTime = config is TrackerTimedItemConfig timeConfig ? timeConfig.RetentionTime : TimeSpan.MaxValue; _retentionTime = config is TrackerTimedItemConfig timeConfig ? timeConfig.RetentionTime : TimeSpan.MaxValue;
_isSymbolModel = typeof(T).IsSubclassOf(typeof(SharedSymbolModel));
_symbolTracker = symbolTracker;
} }
/// <summary> /// <summary>
+8
View File
@@ -127,6 +127,14 @@ Various:
* PlatformInfo now required support environment names in the constructor * PlatformInfo now required support environment names in the constructor
## Release notes ## Release notes
* Version 13.2.2 - 22 Oct 2026
* Fixed UserDataTracker config not getting set correctly
* Added UserDataTracker warning for missing tracked symbols if open orders request requires it
* Version 13.2.1 - 19 Oct 2026
* Fixed subscription without any predefined routes failing to subscribe
* Fixed test validation failing with empty body string
* Version 13.2.0 - 13 Oct 2026 * Version 13.2.0 - 13 Oct 2026
* Added Shared API ISubscribeFundingInfoSocket * Added Shared API ISubscribeFundingInfoSocket
* Added DelayAfterConnect to SocketApiOptions * Added DelayAfterConnect to SocketApiOptions
+2 -2
View File
@@ -1,8 +1,8 @@
# CryptoExchange.Net — full AI context # CryptoExchange.Net — full AI context
Library: CryptoExchange.Net Library: CryptoExchange.Net
Library version: 13.2.0 Library version: 13.2.2
CryptoExchange.Net package version: 13.2.0 CryptoExchange.Net package version: 13.2.2
Language: C#/.NET Language: C#/.NET
Targets: netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0 Targets: netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0
+1 -1
View File
@@ -2,7 +2,7 @@
> Base C#/.NET library used by 28+ cryptocurrency exchange clients. It standardizes REST, WebSocket, authentication, rate limiting, results, and exchange-agnostic Shared APIs. > Base C#/.NET library used by 28+ cryptocurrency exchange clients. It standardizes REST, WebSocket, authentication, rate limiting, results, and exchange-agnostic Shared APIs.
CryptoExchange.Net is not an exchange client by itself. Install exchange-specific packages such as Binance.Net, Bybit.Net, JK.OKX.Net, Kraken.Net, or Coinbase.Net, or install `CryptoClients.Net` for the bundle. Current release: 13.2.0. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, and net10.0; Native AOT is supported. CryptoExchange.Net is not an exchange client by itself. Install exchange-specific packages such as Binance.Net, Bybit.Net, JK.OKX.Net, Kraken.Net, or Coinbase.Net, or install `CryptoClients.Net` for the bundle. Current release: 13.2.2. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, and net10.0; Native AOT is supported.
## Shared API V2 ## Shared API V2