mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-14 18:02:58 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9be8798ccf | |||
| 4803ed91cd | |||
| 20bddd5c37 | |||
| 0e75ddb3d0 | |||
| 0e5b46002c | |||
| 8f7c71f9ce | |||
| 73377fbb87 | |||
| 3a00d6371a | |||
| caf6d36bcd | |||
| e078a373da | |||
| 007743f5a1 |
@@ -38,6 +38,10 @@ After calling `GetSpotSymbolsAsync`, `ISpotSymbolRestClient.SpotSymbolCatalog` m
|
|||||||
|
|
||||||
When implementing an exchange library, use `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` only as best-effort classifiers and supply exchange-specific additions where needed.
|
When implementing an exchange library, use `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` only as best-effort classifiers and supply exchange-specific additions where needed.
|
||||||
|
|
||||||
|
## Shared market-data quantities
|
||||||
|
|
||||||
|
In 12.4.0, use `SharedOrderQuantity`-valued `Volumes` on shared spot/futures tickers and klines, and `Quantities` on shared trades. The scalar `Volume`, `QuoteVolume`, and `Quantity` members are obsolete.
|
||||||
|
|
||||||
## Result pattern
|
## Result pattern
|
||||||
|
|
||||||
REST methods return `HttpResult<T>` and websocket subscription methods return `WebSocketResult<UpdateSubscription>`. Always check `.Success`. `.Exchange` property identifies which exchange responded — useful for logging.
|
REST methods return `HttpResult<T>` and websocket subscription methods return `WebSocketResult<UpdateSubscription>`. Always check `.Success`. `.Exchange` property identifies which exchange responded — useful for logging.
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ CryptoExchange.Net 12.2.0 classifies the base and quote sides of `SharedSpotSymb
|
|||||||
|
|
||||||
`ISpotSymbolRestClient.SpotSymbolCatalog` is populated by `GetSpotSymbolsAsync`; `IFuturesSymbolRestClient.FuturesSymbolCatalog` is populated by `GetFuturesSymbolsAsync`. Do not assume a catalog is available before that request. For exchange-library implementations, `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` offer best-effort classification and can be extended with exchange-specific values.
|
`ISpotSymbolRestClient.SpotSymbolCatalog` is populated by `GetSpotSymbolsAsync`; `IFuturesSymbolRestClient.FuturesSymbolCatalog` is populated by `GetFuturesSymbolsAsync`. Do not assume a catalog is available before that request. For exchange-library implementations, `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` offer best-effort classification and can be extended with exchange-specific values.
|
||||||
|
|
||||||
|
## Shared market-data quantities
|
||||||
|
|
||||||
|
CryptoExchange.Net 12.4.0 uses `SharedOrderQuantity` for market-data quantities. Prefer `Volumes` on `SharedSpotTicker`, `SharedFuturesTicker`, and `SharedKline`, and `Quantities` on `SharedTrade`; the scalar `Volume`, `QuoteVolume`, and `Quantity` members are obsolete.
|
||||||
|
|
||||||
## Single-exchange code uses the exchange's own client
|
## Single-exchange code uses the exchange's own client
|
||||||
|
|
||||||
For Binance-only code, use `BinanceRestClient` directly (see Binance.Net repo `AGENTS.md`). SharedApis is for portability — use it when you need that.
|
For Binance-only code, use `BinanceRestClient` directly (see Binance.Net repo `AGENTS.md`). SharedApis is for portability — use it when you need that.
|
||||||
|
|||||||
@@ -85,6 +85,10 @@ After calling `GetSpotSymbolsAsync` or `GetFuturesSymbolsAsync`, use the client'
|
|||||||
|
|
||||||
For exchange-library implementations, `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` provide best-effort classification of known assets and accept exchange-specific additions. These helpers are heuristics, not an exhaustive source of truth.
|
For exchange-library implementations, `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` provide best-effort classification of known assets and accept exchange-specific additions. These helpers are heuristics, not an exhaustive source of truth.
|
||||||
|
|
||||||
|
## Shared Market-Data Quantities
|
||||||
|
|
||||||
|
Since CryptoExchange.Net 12.4.0, shared market-data models use `SharedOrderQuantity` so base-asset, quote-asset, and contract quantities remain explicit. Read `SharedSpotTicker.Volumes`, `SharedFuturesTicker.Volumes`, and `SharedKline.Volumes`; read `SharedTrade.Quantities`. The former scalar `Volume`, `QuoteVolume`, and `Quantity` members are obsolete.
|
||||||
|
|
||||||
## Available Shared Interfaces
|
## Available Shared Interfaces
|
||||||
|
|
||||||
**REST:**
|
**REST:**
|
||||||
|
|||||||
@@ -625,6 +625,21 @@ namespace CryptoExchange.Net.Clients
|
|||||||
return Task.FromResult(CallResult.Ok());
|
return Task.FromResult(CallResult.Ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the connection can be used for a new subscription or query with the provided parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection">The connection to check</param>
|
||||||
|
/// <param name="address">The address set by the request</param>
|
||||||
|
/// <param name="authenticated">Whether the request needs an authenticated connection</param>
|
||||||
|
/// <param name="topic">Topic of the request</param>
|
||||||
|
/// <returns>True if connection can be used</returns>
|
||||||
|
protected virtual bool ConnectionCanBeUsedFor(SocketConnection connection, string address, bool authenticated, string? topic = null)
|
||||||
|
{
|
||||||
|
return connection.ConnectionUriString.Equals(address.TrimEnd('/'), StringComparison.Ordinal)
|
||||||
|
&& connection.ApiClient.ClientName.Equals(ClientName, StringComparison.Ordinal)
|
||||||
|
&& (AllowTopicsOnTheSameConnection || !connection.Topics.Contains(topic));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a connection for a new subscription or query. Can be an existing if there are open position or a new one.
|
/// Gets a connection for a new subscription or query. Can be an existing if there are open position or a new one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -643,10 +658,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
string? topic = null,
|
string? topic = null,
|
||||||
int individualSubscriptionCount = 1)
|
int individualSubscriptionCount = 1)
|
||||||
{
|
{
|
||||||
var socketQuery = _socketConnections.Where(s => s.Value.ConnectionUriString.Equals(address.TrimEnd('/'), StringComparison.Ordinal)
|
var socketQuery = _socketConnections.Where(s => ConnectionCanBeUsedFor(s.Value, address, authenticated, topic)).Select(x => x.Value); // Don't ToList this so the query is executed again when called
|
||||||
&& s.Value.ApiClient.ClientName.Equals(ClientName, StringComparison.Ordinal)
|
|
||||||
&& (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic)))
|
|
||||||
.Select(x => x.Value); // Don't ToList this so the query is executed again when called
|
|
||||||
|
|
||||||
// If all current socket connections are reconnecting or resubscribing wait for that to finish as we can probably use the existing connection
|
// 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 delayStart = DateTime.UtcNow;
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson.MessageHandlers
|
|||||||
stream.Seek(0, SeekOrigin.Begin);
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
var written = new StreamReader(stream).ReadBlock(dataSnippet, 0, _errorResponseSnippetLimit);
|
var written = new StreamReader(stream).ReadBlock(dataSnippet, 0, _errorResponseSnippetLimit);
|
||||||
var data = new string(dataSnippet, 0, written);
|
var data = new string(dataSnippet, 0, written);
|
||||||
errorMsg += $": {data}";
|
errorMsg += $": {(string.IsNullOrEmpty(data) ? "(empty)" : data)}";
|
||||||
if (data.Length == _errorResponseSnippetLimit)
|
if (data.Length == _errorResponseSnippetLimit)
|
||||||
errorMsg += " (truncated)";
|
errorMsg += " (truncated)";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>12.2.0</PackageVersion>
|
<PackageVersion>12.4.0</PackageVersion>
|
||||||
<AssemblyVersion>12.2.0</AssemblyVersion>
|
<AssemblyVersion>12.4.0</AssemblyVersion>
|
||||||
<FileVersion>12.2.0</FileVersion>
|
<FileVersion>12.4.0</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>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -8,8 +9,11 @@ namespace CryptoExchange.Net.Objects;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call result
|
/// Call result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{DebugView,nq}")]
|
||||||
public record CallResult : ICallResult
|
public record CallResult : ICallResult
|
||||||
{
|
{
|
||||||
|
private string DebugView => Success ? "Success" : $"Error: {Error}";
|
||||||
|
|
||||||
private static CallResult _successResult = new CallResult();
|
private static CallResult _successResult = new CallResult();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using CryptoExchange.Net.SharedApis;
|
using CryptoExchange.Net.SharedApis;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -12,8 +13,11 @@ namespace CryptoExchange.Net.Objects;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// HTTP call result
|
/// HTTP call result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{DebugView,nq}")]
|
||||||
public record HttpResult : IHttpResult
|
public record HttpResult : IHttpResult
|
||||||
{
|
{
|
||||||
|
private string DebugView => $"[Req {RequestId}] " + (Success ? "Success" : $"Error: {Error}");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new success HTTP result
|
/// Create a new success HTTP result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -251,8 +255,32 @@ public record HttpResult : IHttpResult
|
|||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
[DebuggerDisplay("{DebugView,nq}")]
|
||||||
public record HttpResult<T> : HttpResult, IHttpResult<T>
|
public record HttpResult<T> : HttpResult, IHttpResult<T>
|
||||||
{
|
{
|
||||||
|
private string DebugView
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var result = new StringBuilder($"[Req {RequestId}] " + (Success ? "Success" : $"Error: {Error}"));
|
||||||
|
if (Data != null)
|
||||||
|
{
|
||||||
|
result.Append(", ");
|
||||||
|
var typeName = typeof(T).Name;
|
||||||
|
if (Data is Array ar)
|
||||||
|
{
|
||||||
|
result.Append($"{ar.Length} {typeName.Substring(0, typeName.Length - 2)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Append(typeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -8,8 +9,11 @@ namespace CryptoExchange.Net.Objects;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// WebSocket call result
|
/// WebSocket call result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{DebugView,nq}")]
|
||||||
public record WebSocketResult : IWebSocketResult
|
public record WebSocketResult : IWebSocketResult
|
||||||
{
|
{
|
||||||
|
private string DebugView => $"[Sckt {ConnectionId}] " + (RequestId == null ? "" : $"[Req {RequestId}] ") + (Success ? "Success" : $"Error: {Error}");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -68,9 +68,14 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
return ArgumentError.Invalid("TradingMode", $"TradingMode.{tradingMode} is not supported, supported types: {string.Join(", ", supportedTradingModes)}");
|
return ArgumentError.Invalid("TradingMode", $"TradingMode.{tradingMode} is not supported, supported types: {string.Join(", ", supportedTradingModes)}");
|
||||||
|
|
||||||
foreach (var param in RequiredExchangeParameters)
|
foreach (var param in RequiredExchangeParameters)
|
||||||
{
|
{
|
||||||
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, Exchange, x, param.ValueType) != true))
|
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, Exchange, x, param.ValueType) != true))
|
||||||
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
|
{
|
||||||
|
if (param.Names.Length == 1)
|
||||||
|
return ArgumentError.Invalid(string.Join("/", param.Names!), $"Exchange parameter `{param.Names[0]}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
|
||||||
|
else
|
||||||
|
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -149,7 +154,12 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
foreach (var param in RequiredOptionalParameters)
|
foreach (var param in RequiredOptionalParameters)
|
||||||
{
|
{
|
||||||
if (param.Names!.All(x => _requestProperties.Single(p => p.Name == x).GetValue(request, null) == null))
|
if (param.Names!.All(x => _requestProperties.Single(p => p.Name == x).GetValue(request, null) == null))
|
||||||
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
|
{
|
||||||
|
if (param.Names.Length == 1)
|
||||||
|
return ArgumentError.Invalid(string.Join("/", param.Names!), $"Optional parameter `{param.Names[0]}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
|
||||||
|
else
|
||||||
|
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request is SharedSymbolRequest symbolsRequest)
|
if (request is SharedSymbolRequest symbolsRequest)
|
||||||
|
|||||||
@@ -54,10 +54,18 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// Order price
|
/// Order price
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal? OrderPrice { get; set; }
|
public decimal? OrderPrice { get; set; }
|
||||||
|
private decimal? _averagePrice;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Average price
|
/// Average fill price
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal? AveragePrice { get; set; }
|
public decimal? AveragePrice
|
||||||
|
{
|
||||||
|
get => _averagePrice > 0 ? _averagePrice
|
||||||
|
: (QuantityFilled?.QuantityInBaseAsset > 0 && QuantityFilled?.QuantityInQuoteAsset > 0
|
||||||
|
? QuantityFilled.QuantityInQuoteAsset / QuantityFilled.QuantityInBaseAsset
|
||||||
|
: null);
|
||||||
|
set => _averagePrice = value;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Client order id
|
/// Client order id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -24,7 +24,24 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The volume in the last 24h
|
/// The volume in the last 24h
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Volume { get; set; }
|
public SharedOrderQuantity Volumes { get; set; }
|
||||||
|
|
||||||
|
private decimal? _volume;
|
||||||
|
/// <summary>
|
||||||
|
/// The volume in the last 24h
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("Use `Volumes` instead")]
|
||||||
|
public decimal Volume
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_volume.HasValue)
|
||||||
|
return _volume.Value;
|
||||||
|
|
||||||
|
return Volumes.QuantityInBaseAsset ?? Volumes.QuantityInContracts ?? 0;
|
||||||
|
}
|
||||||
|
set => _volume = value;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change percentage in the last 24h
|
/// Change percentage in the last 24h
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -49,13 +66,20 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedFuturesTicker(SharedSymbol? sharedSymbol, string symbol, decimal? lastPrice, decimal? highPrice, decimal? lowPrice, decimal volume, decimal? changePercentage)
|
public SharedFuturesTicker(
|
||||||
|
SharedSymbol? sharedSymbol,
|
||||||
|
string symbol,
|
||||||
|
decimal? lastPrice,
|
||||||
|
decimal? highPrice,
|
||||||
|
decimal? lowPrice,
|
||||||
|
SharedOrderQuantity volumes,
|
||||||
|
decimal? changePercentage)
|
||||||
:base(sharedSymbol, symbol)
|
:base(sharedSymbol, symbol)
|
||||||
{
|
{
|
||||||
LastPrice = lastPrice;
|
LastPrice = lastPrice;
|
||||||
HighPrice = highPrice;
|
HighPrice = highPrice;
|
||||||
LowPrice = lowPrice;
|
LowPrice = lowPrice;
|
||||||
Volume = volume;
|
Volumes = volumes;
|
||||||
ChangePercentage = changePercentage;
|
ChangePercentage = changePercentage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kline info
|
/// Kline info
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DebuggerDisplay("[{OpenTime}] O: {OpenPrice} H: {HighPrice} L: {LowPrice} C: {ClosePrice} V: {Volume}")]
|
[DebuggerDisplay("[{OpenTime}] O: {OpenPrice} H: {HighPrice} L: {LowPrice} C: {ClosePrice} V: {Volumes}")]
|
||||||
public record SharedKline : SharedSymbolModel
|
public record SharedKline : SharedSymbolModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -29,15 +29,40 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// Open price
|
/// Open price
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal OpenPrice { get; set; }
|
public decimal OpenPrice { get; set; }
|
||||||
|
|
||||||
|
private decimal? _volume;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Volume in the base asset
|
/// Volume in the base asset
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Volume { get; set; }
|
[Obsolete("Use `Volumes` instead")]
|
||||||
|
public decimal Volume
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_volume.HasValue)
|
||||||
|
return _volume.Value;
|
||||||
|
|
||||||
|
return Volumes.QuantityInBaseAsset ?? Volumes.QuantityInContracts ?? 0;
|
||||||
|
}
|
||||||
|
set => _volume = value;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The volume in the last 24h
|
||||||
|
/// </summary>
|
||||||
|
public SharedOrderQuantity Volumes { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedKline(SharedSymbol? sharedSymbol, string symbol, DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice, decimal volume)
|
public SharedKline(
|
||||||
|
SharedSymbol? sharedSymbol,
|
||||||
|
string symbol,
|
||||||
|
DateTime openTime,
|
||||||
|
decimal closePrice,
|
||||||
|
decimal highPrice,
|
||||||
|
decimal lowPrice,
|
||||||
|
decimal openPrice,
|
||||||
|
SharedOrderQuantity volumes)
|
||||||
: base(sharedSymbol, symbol)
|
: base(sharedSymbol, symbol)
|
||||||
{
|
{
|
||||||
OpenTime = openTime;
|
OpenTime = openTime;
|
||||||
@@ -45,7 +70,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
HighPrice = highPrice;
|
HighPrice = highPrice;
|
||||||
LowPrice = lowPrice;
|
LowPrice = lowPrice;
|
||||||
OpenPrice = openPrice;
|
OpenPrice = openPrice;
|
||||||
Volume = volume;
|
Volumes = volumes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,18 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// Order price
|
/// Order price
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal? OrderPrice { get; set; }
|
public decimal? OrderPrice { get; set; }
|
||||||
|
private decimal? _averagePrice;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Average fill price
|
/// Average fill price
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal? AveragePrice { get; set; }
|
public decimal? AveragePrice
|
||||||
|
{
|
||||||
|
get => _averagePrice > 0 ? _averagePrice
|
||||||
|
: (QuantityFilled?.QuantityInBaseAsset > 0 && QuantityFilled?.QuantityInQuoteAsset > 0
|
||||||
|
? QuantityFilled.QuantityInQuoteAsset / QuantityFilled.QuantityInBaseAsset
|
||||||
|
: null);
|
||||||
|
set => _averagePrice = value;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Client order id
|
/// Client order id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.SharedApis
|
namespace CryptoExchange.Net.SharedApis
|
||||||
{
|
{
|
||||||
@@ -21,13 +22,31 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal? LowPrice { get; set; }
|
public decimal? LowPrice { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// The volume in the last 24h
|
||||||
|
/// </summary>
|
||||||
|
public SharedOrderQuantity Volumes { get; set; }
|
||||||
|
|
||||||
|
private decimal? _volume;
|
||||||
|
/// <summary>
|
||||||
/// Trade volume in base asset in the last 24h
|
/// Trade volume in base asset in the last 24h
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Volume { get; set; }
|
[Obsolete("Use `Volumes` instead")]
|
||||||
|
public decimal Volume
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_volume.HasValue)
|
||||||
|
return _volume.Value;
|
||||||
|
|
||||||
|
return Volumes.QuantityInBaseAsset ?? Volumes.QuantityInContracts ?? 0;
|
||||||
|
}
|
||||||
|
set => _volume = value;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Trade volume in quote asset in the last 24h
|
/// Trade volume in quote asset in the last 24h
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal? QuoteVolume { get; set; }
|
[Obsolete("Use `Volumes` instead")]
|
||||||
|
public decimal? QuoteVolume => Volumes?.QuantityInQuoteAsset;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change percentage in the last 24h
|
/// Change percentage in the last 24h
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -36,13 +55,20 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedSpotTicker(SharedSymbol? sharedSymbol, string symbol, decimal? lastPrice, decimal? highPrice, decimal? lowPrice, decimal volume, decimal? changePercentage)
|
public SharedSpotTicker(
|
||||||
|
SharedSymbol? sharedSymbol,
|
||||||
|
string symbol,
|
||||||
|
decimal? lastPrice,
|
||||||
|
decimal? highPrice,
|
||||||
|
decimal? lowPrice,
|
||||||
|
SharedOrderQuantity volumes,
|
||||||
|
decimal? changePercentage)
|
||||||
: base(sharedSymbol, symbol)
|
: base(sharedSymbol, symbol)
|
||||||
{
|
{
|
||||||
LastPrice = lastPrice;
|
LastPrice = lastPrice;
|
||||||
HighPrice = highPrice;
|
HighPrice = highPrice;
|
||||||
LowPrice = lowPrice;
|
LowPrice = lowPrice;
|
||||||
Volume = volume;
|
Volumes = volumes;
|
||||||
ChangePercentage = changePercentage;
|
ChangePercentage = changePercentage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quantity of the trade
|
/// Quantity of the trade
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Quantity { get; set; }
|
[Obsolete("Use `Quantities` instead")]
|
||||||
|
public decimal Quantity => Quantities.QuantityInBaseAsset ?? Quantities.QuantityInContracts ?? 0;
|
||||||
|
/// <summary>
|
||||||
|
/// The quantities of the trade
|
||||||
|
/// </summary>
|
||||||
|
public SharedOrderQuantity Quantities { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Price of the trade
|
/// Price of the trade
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -29,9 +34,9 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedTrade(SharedSymbol? sharedSymbol, string symbol, decimal quantity, decimal price, DateTime timestamp) : base(sharedSymbol, symbol)
|
public SharedTrade(SharedSymbol? sharedSymbol, string symbol, SharedOrderQuantity quantities, decimal price, DateTime timestamp) : base(sharedSymbol, symbol)
|
||||||
{
|
{
|
||||||
Quantity = quantity;
|
Quantities = quantities;
|
||||||
Price = price;
|
Price = price;
|
||||||
Timestamp = timestamp;
|
Timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,6 +142,11 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
[JsonConverter(typeof(SharedOrderQuantityConverter))]
|
[JsonConverter(typeof(SharedOrderQuantityConverter))]
|
||||||
public record SharedOrderQuantity : SharedQuantityReference
|
public record SharedOrderQuantity : SharedQuantityReference
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The average price based on the base and quote asset quantities
|
||||||
|
/// </summary>
|
||||||
|
public decimal? AveragePrice => QuantityInBaseAsset == 0 ? null : QuantityInQuoteAsset / QuantityInBaseAsset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -155,6 +160,22 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the quantity in quote asset. Will use the set `QuantityInQuoteAsset` property if it has a value, or `QuantityInBaseAsset` * `price` if not. Null otherwise.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="price">The price to use for the QuantityInBaseAsset to quote asset quantity calculation</param>
|
||||||
|
/// <returns>Quantity in quote asset if it's available or can be calculated, null otherwise</returns>
|
||||||
|
public decimal? GetQuantityInQuoteAsset(decimal? price)
|
||||||
|
{
|
||||||
|
if (QuantityInQuoteAsset != null)
|
||||||
|
return QuantityInQuoteAsset;
|
||||||
|
|
||||||
|
if (QuantityInBaseAsset != null && price != null)
|
||||||
|
return QuantityInBaseAsset * price;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString() => base.ToString();
|
public override string ToString() => base.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
var issues = SystemTextJsonComparer.CompareData(expressionBody.Method.Name, data, originalData, compareNestedProperty, ignoreProperties, useSingleArrayItem ?? false);
|
var issues = SystemTextJsonComparer.CompareData(expressionBody.Method.Name, data, originalData, compareNestedProperty, ignoreProperties, useSingleArrayItem ?? false);
|
||||||
foreach(var issue in issues)
|
foreach(var issue in issues)
|
||||||
{
|
{
|
||||||
if (issue is MissingPropertyException)
|
if (issue is MissingPropertyException && !warnings?.Any(x => x.Message == issue.Message) == true)
|
||||||
warnings?.Add(issue);
|
warnings?.Add(issue);
|
||||||
else
|
else
|
||||||
errors.Add(issue);
|
errors.Add(issue);
|
||||||
|
|||||||
@@ -284,8 +284,10 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
LastOpenTime = klines.Last().OpenTime,
|
LastOpenTime = klines.Last().OpenTime,
|
||||||
HighPrice = klines.Select(d => d.LowPrice).Max(),
|
HighPrice = klines.Select(d => d.LowPrice).Max(),
|
||||||
LowPrice = klines.Select(d => d.HighPrice).Min(),
|
LowPrice = klines.Select(d => d.HighPrice).Min(),
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete | Temporary to maintain previous behavior
|
||||||
Volume = klines.Select(d => d.Volume).Sum(),
|
Volume = klines.Select(d => d.Volume).Sum(),
|
||||||
AverageVolume = Math.Round(klines.OrderByDescending(d => d.OpenTime).Skip(1).Select(d => d.Volume).DefaultIfEmpty().Average(), 8)
|
AverageVolume = Math.Round(klines.OrderByDescending(d => d.OpenTime).Skip(1).Select(d => d.Volume).DefaultIfEmpty().Average(), 8)
|
||||||
|
#pragma warning restore
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,6 +138,11 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of quantity the trades and stats are denoted in
|
||||||
|
/// </summary>
|
||||||
|
public TradeQuantityType QuantityType { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Func<SharedTrade, Task>? OnAdded;
|
public event Func<SharedTrade, Task>? OnAdded;
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -156,12 +161,14 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
SharedSymbol symbol,
|
SharedSymbol symbol,
|
||||||
int? limit = null,
|
int? limit = null,
|
||||||
TimeSpan? period = null,
|
TimeSpan? period = null,
|
||||||
|
TradeQuantityType tradeQuantityType = TradeQuantityType.BaseAsset,
|
||||||
ExchangeParameters? exchangeParameters = null)
|
ExchangeParameters? exchangeParameters = null)
|
||||||
{
|
{
|
||||||
_logger = logger ?? new NullLogger<TradeTracker>();
|
_logger = logger ?? new NullLogger<TradeTracker>();
|
||||||
_recentRestClient = recentRestClient;
|
_recentRestClient = recentRestClient;
|
||||||
_historyRestClient = historyRestClient;
|
_historyRestClient = historyRestClient;
|
||||||
_socketClient = socketClient;
|
_socketClient = socketClient;
|
||||||
|
QuantityType = tradeQuantityType;
|
||||||
_exchangeParameters = exchangeParameters;
|
_exchangeParameters = exchangeParameters;
|
||||||
Exchange = socketClient.Exchange;
|
Exchange = socketClient.Exchange;
|
||||||
Symbol = symbol;
|
Symbol = symbol;
|
||||||
@@ -170,22 +177,41 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
Period = period;
|
Period = period;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TradesStats GetStats(IEnumerable<SharedTrade> trades)
|
private TradesStats GetStats(IEnumerable<SharedTrade> trades)
|
||||||
{
|
{
|
||||||
if (!trades.Any())
|
if (!trades.Any())
|
||||||
return new TradesStats();
|
return new TradesStats();
|
||||||
|
|
||||||
return new TradesStats
|
|
||||||
|
var stats = new TradesStats
|
||||||
{
|
{
|
||||||
TradeCount = trades.Count(),
|
TradeCount = trades.Count(),
|
||||||
FirstTradeTime = trades.First().Timestamp,
|
FirstTradeTime = trades.First().Timestamp,
|
||||||
LastTradeTime = trades.Last().Timestamp,
|
LastTradeTime = trades.Last().Timestamp,
|
||||||
AveragePrice = Math.Round(trades.Select(d => d.Price).DefaultIfEmpty().Average(), 8),
|
AveragePrice = Math.Round(trades.Select(d => d.Price).DefaultIfEmpty().Average(), 8),
|
||||||
VolumeWeightedAveragePrice = trades.Any() ? Math.Round(trades.Select(d => d.Price * d.Quantity).DefaultIfEmpty().Sum() / trades.Select(d => d.Quantity).DefaultIfEmpty().Sum(), 8) : null,
|
QuoteVolume = Math.Round(trades.Sum(d => d.Quantities.GetQuantityInQuoteAsset(d.Price) ?? 0), 8),
|
||||||
Volume = Math.Round(trades.Sum(d => d.Quantity), 8),
|
|
||||||
QuoteVolume = Math.Round(trades.Sum(d => d.Quantity * d.Price), 8),
|
|
||||||
BuySellRatio = Math.Round(trades.Where(x => x.Side == SharedOrderSide.Buy).Sum(x => x.Quantity) / trades.Sum(x => x.Quantity), 8)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (QuantityType == TradeQuantityType.BaseAsset)
|
||||||
|
{
|
||||||
|
stats.VolumeWeightedAveragePrice =
|
||||||
|
trades.Any()
|
||||||
|
? Math.Round(trades.Select(d => d.Quantities.GetQuantityInQuoteAsset(d.Price) ?? 0).DefaultIfEmpty().Sum() / trades.Select(d => d.Quantities.QuantityInBaseAsset!.Value).DefaultIfEmpty().Sum(), 8)
|
||||||
|
: null;
|
||||||
|
stats.Volume = Math.Round(trades.Sum(d => d.Quantities.QuantityInBaseAsset!.Value), 8);
|
||||||
|
stats.BuySellRatio = Math.Round(trades.Where(x => x.Side == SharedOrderSide.Buy).Sum(x => x.Quantities.QuantityInBaseAsset!.Value) / trades.Sum(x => x.Quantities.QuantityInBaseAsset!.Value), 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stats.VolumeWeightedAveragePrice =
|
||||||
|
trades.Any()
|
||||||
|
? Math.Round(trades.Select(d => d.Quantities.GetQuantityInQuoteAsset(d.Price) ?? 0).DefaultIfEmpty().Sum() / trades.Select(d => d.Quantities.QuantityInContracts!.Value).DefaultIfEmpty().Sum(), 8)
|
||||||
|
: null;
|
||||||
|
stats.Volume = Math.Round(trades.Sum(d => d.Quantities.QuantityInContracts!.Value), 8);
|
||||||
|
stats.BuySellRatio = Math.Round(trades.Where(x => x.Side == SharedOrderSide.Buy).Sum(x => x.Quantities.QuantityInContracts!.Value) / trades.Sum(x => x.Quantities.QuantityInContracts!.Value), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -498,4 +524,19 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
Status = SyncStatus.Synced;
|
Status = SyncStatus.Synced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The quantities to use for trade tracking
|
||||||
|
/// </summary>
|
||||||
|
public enum TradeQuantityType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base asset
|
||||||
|
/// </summary>
|
||||||
|
BaseAsset,
|
||||||
|
/// <summary>
|
||||||
|
/// Contracts
|
||||||
|
/// </summary>
|
||||||
|
Contracts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider us
|
|||||||
||Lighter|DEX|[JKorf/Lighter.Net](https://github.com/JKorf/Lighter.Net)|[](https://www.nuget.org/packages/JKorf.Lighter.Net)|-|-|
|
||Lighter|DEX|[JKorf/Lighter.Net](https://github.com/JKorf/Lighter.Net)|[](https://www.nuget.org/packages/JKorf.Lighter.Net)|-|-|
|
||||||
||Mexc|CEX|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[](https://www.nuget.org/packages/JK.Mexc.Net)|-|-|
|
||Mexc|CEX|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[](https://www.nuget.org/packages/JK.Mexc.Net)|-|-|
|
||||||
||OKX|CEX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[](https://www.nuget.org/packages/JK.OKX.Net)|[Link](https://www.okx.com/join/14592495)|20%|
|
||OKX|CEX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[](https://www.nuget.org/packages/JK.OKX.Net)|[Link](https://www.okx.com/join/14592495)|20%|
|
||||||
|
||Pionex|CEX|[JKorf/Pionex.Net](https://github.com/JKorf/Pionex.Net)|[](https://www.nuget.org/packages/Pionex.Net)|-|-|
|
||||||
||Polymarket|DEX|[JKorf/Polymarket.Net](https://github.com/JKorf/Polymarket.Net)|[](https://www.nuget.org/packages/Polymarket.Net)|-|-|
|
||Polymarket|DEX|[JKorf/Polymarket.Net](https://github.com/JKorf/Polymarket.Net)|[](https://www.nuget.org/packages/Polymarket.Net)|-|-|
|
||||||
||Toobit|CEX|[JKorf/Toobit.Net](https://github.com/JKorf/Toobit.Net)|[](https://www.nuget.org/packages/Toobit.Net)|[Link](https://www.toobit.com/en-US/register?invite_code=zsV19h)|-|
|
||Toobit|CEX|[JKorf/Toobit.Net](https://github.com/JKorf/Toobit.Net)|[](https://www.nuget.org/packages/Toobit.Net)|[Link](https://www.toobit.com/en-US/register?invite_code=zsV19h)|-|
|
||||||
||Upbit|CEX|[JKorf/Upbit.Net](https://github.com/JKorf/Upbit.Net)|[](https://www.nuget.org/packages/JKorf.Upbit.Net)|-|-|
|
||Upbit|CEX|[JKorf/Upbit.Net](https://github.com/JKorf/Upbit.Net)|[](https://www.nuget.org/packages/JKorf.Upbit.Net)|-|-|
|
||||||
@@ -127,6 +128,18 @@ Various:
|
|||||||
* PlatformInfo now required support environment names in the constructor
|
* PlatformInfo now required support environment names in the constructor
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
* Version 12.4.0 - 28 Jul 2026
|
||||||
|
* Added AveragePrice property to SharedQuantity model
|
||||||
|
* Added DebuggerDisplay attributes to Result objects
|
||||||
|
* Updated SharedFuturesTicker, SharedSpotTicker, SharedTrade and SharedKline to use SharedOrderQuantity for volumes/quantities
|
||||||
|
* Updated REST json deserialization error for empty response
|
||||||
|
|
||||||
|
* Version 12.3.0 - 23 Jul 2026
|
||||||
|
* Added calculation of AveragePrice on Shared order models if data is available and AveragePrice is not set
|
||||||
|
* Extracted ConnectionCanBeUsedFor method in SocketApiClient for easier custom logic implementation
|
||||||
|
* Updated some Shared APIs error messages
|
||||||
|
* Remove duplicate warnings from testing output
|
||||||
|
|
||||||
* Version 12.2.0 - 20 Jul 2026
|
* Version 12.2.0 - 20 Jul 2026
|
||||||
* Added SpotSymbolCatalog to Shared ISpotSymbolRestClient interface
|
* Added SpotSymbolCatalog to Shared ISpotSymbolRestClient interface
|
||||||
* Added FuturesSymbolCatalog to Shared IFuturesSymbolRestClient interface
|
* Added FuturesSymbolCatalog to Shared IFuturesSymbolRestClient interface
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
> Base C#/.NET library for cryptocurrency exchange API client implementations. Provides a standardized abstraction (REST, WebSocket, authentication, rate limiting, error handling, order book management, shared cross-exchange interfaces) that 28+ exchange-specific libraries are built on top of.
|
> Base C#/.NET library for cryptocurrency exchange API client implementations. Provides a standardized abstraction (REST, WebSocket, authentication, rate limiting, error handling, order book management, shared cross-exchange interfaces) that 28+ exchange-specific libraries are built on top of.
|
||||||
|
|
||||||
CryptoExchange.Net itself is not used directly — install one of the exchange-specific libraries (Binance.Net, Bybit.Net, OKX.Net, Kraken.Net, Coinbase.Net, etc.) or `CryptoClients.Net` to access all exchanges via a single bundle. The base library is what makes the entire ecosystem feel consistent: same `HttpResult<T>` REST result pattern, same `WebSocketResult<UpdateSubscription>` websocket subscription pattern, same DI registration, same shared interfaces across all exchanges. Current version: 12.2.0. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. Native AOT supported.
|
CryptoExchange.Net itself is not used directly — install one of the exchange-specific libraries (Binance.Net, Bybit.Net, OKX.Net, Kraken.Net, Coinbase.Net, etc.) or `CryptoClients.Net` to access all exchanges via a single bundle. The base library is what makes the entire ecosystem feel consistent: same `HttpResult<T>` REST result pattern, same `WebSocketResult<UpdateSubscription>` websocket subscription pattern, same DI registration, same shared interfaces across all exchanges. Current version: 12.4.0. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. Native AOT supported.
|
||||||
|
|
||||||
The standout feature for cross-exchange code is `CryptoExchange.Net.SharedApis` — a set of interfaces (`ISpotTickerRestClient`, `ISpotOrderRestClient`, `IBalanceRestClient`, etc.) implemented by every exchange library. Same call signature works against any exchange.
|
The standout feature for cross-exchange code is `CryptoExchange.Net.SharedApis` — a set of interfaces (`ISpotTickerRestClient`, `ISpotOrderRestClient`, `IBalanceRestClient`, etc.) implemented by every exchange library. Same call signature works against any exchange.
|
||||||
|
|
||||||
Version 12.2.0 adds typed asset metadata to shared symbol discovery. `SharedSpotSymbol` and `SharedFuturesSymbol` expose `DisplayName` plus base/quote `SharedAssetType` and `SharedAssetSubType` values. `GetSymbolsRequest` can filter on those four type fields. After symbol discovery, `ISpotSymbolRestClient.SpotSymbolCatalog` and `IFuturesSymbolRestClient.FuturesSymbolCatalog` provide asset and symbol dictionaries; each catalog is available only after the corresponding `Get*SymbolsAsync` call. `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` are best-effort helpers for exchange-library implementations.
|
Version 12.2.0 adds typed asset metadata to shared symbol discovery. `SharedSpotSymbol` and `SharedFuturesSymbol` expose `DisplayName` plus base/quote `SharedAssetType` and `SharedAssetSubType` values. `GetSymbolsRequest` can filter on those four type fields. After symbol discovery, `ISpotSymbolRestClient.SpotSymbolCatalog` and `IFuturesSymbolRestClient.FuturesSymbolCatalog` provide asset and symbol dictionaries; each catalog is available only after the corresponding `Get*SymbolsAsync` call. `LibraryHelpers.IsStableCoin`, `IsCommodity`, and `IsEquity` are best-effort helpers for exchange-library implementations.
|
||||||
|
|
||||||
|
Version 12.4.0 represents market-data quantities with `SharedOrderQuantity`: use `Volumes` on `SharedSpotTicker`, `SharedFuturesTicker`, and `SharedKline`, and `Quantities` on `SharedTrade`. The old scalar `Volume`, `QuoteVolume`, and `Quantity` members are obsolete. Exchange-library implementations must pass `SharedOrderQuantity` to these model constructors.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [README](https://github.com/JKorf/CryptoExchange.Net/blob/master/README.md): Overview, full ecosystem table (28+ exchange libraries), installation per exchange, complete release notes
|
- [README](https://github.com/JKorf/CryptoExchange.Net/blob/master/README.md): Overview, full ecosystem table (28+ exchange libraries), installation per exchange, complete release notes
|
||||||
@@ -24,7 +26,7 @@ Version 12.2.0 adds typed asset metadata to shared symbol discovery. `SharedSpot
|
|||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
- [Ecosystem libraries list](https://github.com/JKorf/CryptoExchange.Net#cryptoexchangenet-ecosystem): Aster, Binance, BingX, Bitfinex, Bitget, BitMart, BitMEX, Bitstamp, BloFin, Bybit, Coinbase, CoinEx, CoinW, CoinGecko, Crypto.com, DeepCoin, Gate.io, HTX, HyperLiquid, Kraken, Kucoin, Mexc, OKX, Polymarket, Toobit, Upbit, Weex, WhiteBit, XT
|
- [Ecosystem libraries list](https://github.com/JKorf/CryptoExchange.Net#cryptoexchangenet-ecosystem): Aster, Binance, BingX, Bitfinex, Bitget, BitMart, BitMEX, Bitstamp, BloFin, Bybit, Coinbase, CoinEx, CoinW, CoinGecko, Crypto.com, DeepCoin, Gate.io, HTX, HyperLiquid, Kraken, Kucoin, Mexc, OKX, Pionex, Polymarket, Toobit, Upbit, Weex, WhiteBit, XT
|
||||||
- [CryptoClients.Net](https://github.com/JKorf/CryptoClients.Net): Single bundle package for all exchange libraries
|
- [CryptoClients.Net](https://github.com/JKorf/CryptoClients.Net): Single bundle package for all exchange libraries
|
||||||
- [CryptoManager.Net](https://github.com/JKorf/CryptoManager.Net): Full demo application using CryptoClients.Net
|
- [CryptoManager.Net](https://github.com/JKorf/CryptoManager.Net): Full demo application using CryptoClients.Net
|
||||||
- [NuGet Package](https://www.nuget.org/packages/CryptoExchange.Net): Latest stable release on NuGet
|
- [NuGet Package](https://www.nuget.org/packages/CryptoExchange.Net): Latest stable release on NuGet
|
||||||
|
|||||||
Reference in New Issue
Block a user