mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-10-05 02:41:10 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a4930d658 | |||
| ca245959a2 | |||
| ff43d202d4 | |||
| d1947e643b | |||
| 6fec65cd6a | |||
| 053bcf6ecc | |||
| 6957ba44f5 |
@@ -0,0 +1,130 @@
|
||||
using CryptoExchange.Net.Objects;
|
||||
using CryptoExchange.Net.Sockets;
|
||||
using CryptoExchange.Net.Sockets.Default;
|
||||
using CryptoExchange.Net.Sockets.Default.Routing;
|
||||
using CryptoExchange.Net.UnitTests.Implementations;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
namespace CryptoExchange.Net.UnitTests.SocketRoutingTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SubscriptionTests
|
||||
{
|
||||
[Test]
|
||||
public void Handle_Should_OnlyCompleteSubscriptionQuery_ForMatchingTopic()
|
||||
{
|
||||
// arrange
|
||||
var topicASubscription = new TopicSubscription("topic-a");
|
||||
var topicBSubscription = new TopicSubscription("topic-b");
|
||||
var topicAQuery = topicASubscription.CreateSubscriptionQuery(null!)!;
|
||||
var topicBQuery = topicBSubscription.CreateSubscriptionQuery(null!)!;
|
||||
|
||||
// act
|
||||
var topicAHandled = topicASubscription.Handle("type", "topic-a", null!, DateTime.UtcNow, "original", "data");
|
||||
var topicBHandled = topicBSubscription.Handle("type", "topic-a", null!, DateTime.UtcNow, "original", "data");
|
||||
|
||||
// assert
|
||||
Assert.That(topicAHandled, Is.True);
|
||||
Assert.That(topicBHandled, Is.False);
|
||||
Assert.That(topicAQuery.Completed, Is.True);
|
||||
Assert.That(topicAQuery.Success, Is.True);
|
||||
Assert.That(topicBQuery.Completed, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Handle_Should_CompleteSubscriptionQuery_BeforeInvokingMatchingHandler()
|
||||
{
|
||||
// arrange
|
||||
Query? query = null;
|
||||
var queryCompletedWhenHandlerInvoked = false;
|
||||
var subscription = new TopicSubscription("topic", () => queryCompletedWhenHandlerInvoked = query!.Completed);
|
||||
query = subscription.CreateSubscriptionQuery(null!)!;
|
||||
|
||||
// act
|
||||
subscription.Handle("type", "topic", null!, DateTime.UtcNow, "original", "data");
|
||||
|
||||
// assert
|
||||
Assert.That(queryCompletedWhenHandlerInvoked, Is.True);
|
||||
}
|
||||
|
||||
[TestCase(null)]
|
||||
[TestCase("topic")]
|
||||
public void Handle_Should_CompleteSubscriptionQuery_ForUnfilteredRoute(string? topicFilter)
|
||||
{
|
||||
// arrange
|
||||
var subscription = new TopicSubscription(topic: null);
|
||||
var query = subscription.CreateSubscriptionQuery(null!)!;
|
||||
|
||||
// act
|
||||
var handled = subscription.Handle("type", topicFilter, null!, DateTime.UtcNow, "original", "data");
|
||||
|
||||
// assert
|
||||
Assert.That(handled, Is.True);
|
||||
Assert.That(query.Completed, Is.True);
|
||||
Assert.That(query.Success, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Handle_Should_TreatEmptyTopicFilterAsUnfilteredRoute()
|
||||
{
|
||||
// arrange
|
||||
var subscription = new TopicSubscription(string.Empty);
|
||||
var query = subscription.CreateSubscriptionQuery(null!)!;
|
||||
|
||||
// act
|
||||
var handled = subscription.Handle("type", "topic", null!, DateTime.UtcNow, "original", "data");
|
||||
|
||||
// assert
|
||||
Assert.That(handled, Is.True);
|
||||
Assert.That(query.Completed, Is.True);
|
||||
Assert.That(query.Success, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Handle_Should_CompleteSubscriptionQuery_ForAnyMatchingTopic()
|
||||
{
|
||||
// arrange
|
||||
var subscription = new TopicSubscription(["topic-a", "topic-b"]);
|
||||
var query = subscription.CreateSubscriptionQuery(null!)!;
|
||||
|
||||
// act
|
||||
var handled = subscription.Handle("type", "topic-b", null!, DateTime.UtcNow, "original", "data");
|
||||
|
||||
// assert
|
||||
Assert.That(handled, Is.True);
|
||||
Assert.That(query.Completed, Is.True);
|
||||
Assert.That(query.Success, Is.True);
|
||||
}
|
||||
|
||||
private sealed class TopicSubscription : Subscription
|
||||
{
|
||||
public TopicSubscription(string? topic, Action? handler = null)
|
||||
: base(NullLogger.Instance, false)
|
||||
{
|
||||
MessageRouter = MessageRouter.CreateForEvent<string>("type", topic, (_, _, _, _) =>
|
||||
{
|
||||
handler?.Invoke();
|
||||
return CallResult.Ok();
|
||||
});
|
||||
}
|
||||
|
||||
public TopicSubscription(string[] topics)
|
||||
: base(NullLogger.Instance, false)
|
||||
{
|
||||
MessageRouter = MessageRouter.CreateForEvent<string>("type", topics, (_, _, _, _) => CallResult.Ok());
|
||||
}
|
||||
|
||||
protected override Query? GetSubQuery(SocketConnection connection)
|
||||
{
|
||||
return new TestQuery(new TestSocketMessage { Id = 1, Data = "Sub" }, false)
|
||||
{
|
||||
TimeoutBehavior = TimeoutBehavior.Succeed
|
||||
};
|
||||
}
|
||||
|
||||
protected override Query? GetUnsubQuery(SocketConnection connection) => null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,7 @@ namespace CryptoExchange.Net.Clients
|
||||
string? cacheKey = null;
|
||||
if (ShouldCache(definition))
|
||||
{
|
||||
cacheKey = definition.FullUrl + definition + uriParameters?.ToFormData();
|
||||
cacheKey = GetCacheKey(definition, uriParameters);
|
||||
_logger.CheckingCache(cacheKey);
|
||||
var cachedValue = _cache.Get(cacheKey, ClientOptions.CachingMaxAge);
|
||||
if (cachedValue != null)
|
||||
@@ -800,6 +800,13 @@ namespace CryptoExchange.Net.Clients
|
||||
&& definition.Method == HttpMethod.Get
|
||||
&& !definition.PreventCaching;
|
||||
|
||||
/// <summary>
|
||||
/// Produce a unique cache key for the request based on the request definition and parameters.
|
||||
/// </summary>
|
||||
protected virtual string GetCacheKey(RequestDefinition definition, Parameters? parameters)
|
||||
{
|
||||
return definition.FullUrl + definition + parameters?.ToFormData();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void SetOptions(UpdateOptions options)
|
||||
@@ -889,6 +896,12 @@ namespace CryptoExchange.Net.Clients
|
||||
|
||||
ApiCredentials = (TApiCredentials?)options.ApiCredentials?.Copy() ?? ApiCredentials;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetCacheKey(RequestDefinition definition, Parameters? parameters)
|
||||
{
|
||||
return definition.FullUrl + definition + parameters?.ToFormData() + GetAuthenticationProvider()?.Key;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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>12.5.0</PackageVersion>
|
||||
<AssemblyVersion>12.5.0</AssemblyVersion>
|
||||
<FileVersion>12.5.0</FileVersion>
|
||||
<PackageVersion>12.5.1</PackageVersion>
|
||||
<AssemblyVersion>12.5.1</AssemblyVersion>
|
||||
<FileVersion>12.5.1</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>
|
||||
|
||||
@@ -289,6 +289,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
if (!startResult.Success)
|
||||
{
|
||||
Status = OrderBookStatus.Disconnected;
|
||||
_queueEvent.Set();
|
||||
return CallResult.Fail(startResult.Error!);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace CryptoExchange.Net.Sockets.Default.Routing
|
||||
Routes = routes;
|
||||
}
|
||||
|
||||
private RouteCollection? GetRoutes(string typeIdentifier)
|
||||
=> (_routingTable ?? throw new NullReferenceException("Routing table not build before handling")).GetRoutes(typeIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Build the route mapping
|
||||
/// </summary>
|
||||
@@ -46,13 +49,21 @@ namespace CryptoExchange.Net.Sockets.Default.Routing
|
||||
/// </summary>
|
||||
public bool Handle(string typeIdentifier, string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object data, out CallResult? result)
|
||||
{
|
||||
var routeCollection = (_routingTable ?? throw new NullReferenceException("Routing table not build before handling")).GetRoutes(typeIdentifier);
|
||||
var routeCollection = GetRoutes(typeIdentifier);
|
||||
if (routeCollection == null)
|
||||
throw new InvalidOperationException($"No routes for {typeIdentifier} message type");
|
||||
|
||||
return routeCollection.Handle(topicFilter, connection, receiveTime, originalData, data, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the router has a matching route for the specified message type and topic
|
||||
/// </summary>
|
||||
public bool CanHandle(string typeIdentifier, string? topicFilter)
|
||||
{
|
||||
return GetRoutes(typeIdentifier)?.CanHandle(topicFilter) == true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a void handler
|
||||
/// </summary>
|
||||
|
||||
@@ -60,6 +60,15 @@ namespace CryptoExchange.Net.Sockets.Default.Routing
|
||||
return matchingTopicRoutes;
|
||||
}
|
||||
|
||||
public bool CanHandle(string? topicFilter)
|
||||
{
|
||||
if (_routesWithoutTopicFilter.Count > 0)
|
||||
return true;
|
||||
|
||||
return topicFilter != null
|
||||
&& GetRoutesWithMatchingTopicFilter(topicFilter) != null;
|
||||
}
|
||||
|
||||
public abstract bool Handle(string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object data, out CallResult? result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,9 +219,13 @@ namespace CryptoExchange.Net.Sockets.Default
|
||||
ConnectionInvocations++;
|
||||
TotalInvocations++;
|
||||
|
||||
if (SubscriptionQuery != null && !SubscriptionQuery.Completed && SubscriptionQuery.TimeoutBehavior == TimeoutBehavior.Succeed)
|
||||
if (SubscriptionQuery != null
|
||||
&& !SubscriptionQuery.Completed
|
||||
&& SubscriptionQuery.TimeoutBehavior == TimeoutBehavior.Succeed
|
||||
&& MessageRouter.CanHandle(typeIdentifier, topicFilter))
|
||||
{
|
||||
// The subscription query is one where it is successful if there is no error returned
|
||||
// The connection routes by type, so only a matching subscription route proves the query was successful
|
||||
// Since we've received a data update for the subscription we can assume the subscribe query was successful
|
||||
// Call timeout to complete
|
||||
SubscriptionQuery.Timeout();
|
||||
|
||||
@@ -282,8 +282,8 @@ namespace CryptoExchange.Net.Trackers.Klines
|
||||
KlineCount = klines.Count(),
|
||||
FirstOpenTime = klines.First().OpenTime,
|
||||
LastOpenTime = klines.Last().OpenTime,
|
||||
HighPrice = klines.Select(d => d.LowPrice).Max(),
|
||||
LowPrice = klines.Select(d => d.HighPrice).Min(),
|
||||
HighPrice = klines.Select(d => d.HighPrice).Max(),
|
||||
LowPrice = klines.Select(d => d.LowPrice).Min(),
|
||||
#pragma warning disable CS0618 // Type or member is obsolete | Temporary to maintain previous behavior
|
||||
Volume = klines.Select(d => d.Volume).Sum(),
|
||||
AverageVolume = Math.Round(klines.OrderByDescending(d => d.OpenTime).Skip(1).Select(d => d.Volume).DefaultIfEmpty().Average(), 8)
|
||||
|
||||
@@ -5,36 +5,37 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Binance.Net" Version="13.3.0" />
|
||||
<PackageReference Include="Bitfinex.Net" Version="11.3.0" />
|
||||
<PackageReference Include="BitMart.Net" Version="4.3.0" />
|
||||
<PackageReference Include="BloFin.Net" Version="3.3.0" />
|
||||
<PackageReference Include="Bybit.Net" Version="7.3.0" />
|
||||
<PackageReference Include="CoinEx.Net" Version="11.3.0" />
|
||||
<PackageReference Include="CoinW.Net" Version="3.3.0" />
|
||||
<PackageReference Include="CryptoCom.Net" Version="4.3.0" />
|
||||
<PackageReference Include="DeepCoin.Net" Version="4.3.0" />
|
||||
<PackageReference Include="GateIo.Net" Version="4.4.0" />
|
||||
<PackageReference Include="HyperLiquid.Net" Version="5.4.0" />
|
||||
<PackageReference Include="JK.BingX.Net" Version="4.3.0" />
|
||||
<PackageReference Include="JK.Bitget.Net" Version="4.3.2" />
|
||||
<PackageReference Include="JK.Mexc.Net" Version="6.4.0" />
|
||||
<PackageReference Include="JK.OKX.Net" Version="5.3.1" />
|
||||
<PackageReference Include="Jkorf.Aster.Net" Version="4.3.0" />
|
||||
<PackageReference Include="JKorf.BitMEX.Net" Version="4.3.0" />
|
||||
<PackageReference Include="JKorf.Coinbase.Net" Version="4.4.0" />
|
||||
<PackageReference Include="JKorf.HTX.Net" Version="9.3.0" />
|
||||
<PackageReference Include="JKorf.Lighter.Net" Version="1.4.0" />
|
||||
<PackageReference Include="JKorf.Upbit.Net" Version="3.3.0" />
|
||||
<PackageReference Include="KrakenExchange.Net" Version="8.3.0" />
|
||||
<PackageReference Include="Kucoin.Net" Version="9.3.0" />
|
||||
<PackageReference Include="LBank.Net" Version="1.0.0" />
|
||||
<PackageReference Include="Pionex.Net" Version="1.1.0" />
|
||||
<PackageReference Include="Binance.Net" Version="13.5.0" />
|
||||
<PackageReference Include="Bitfinex.Net" Version="11.4.0" />
|
||||
<PackageReference Include="BitMart.Net" Version="4.4.0" />
|
||||
<PackageReference Include="BloFin.Net" Version="3.4.0" />
|
||||
<PackageReference Include="Bybit.Net" Version="7.5.0" />
|
||||
<PackageReference Include="CoinEx.Net" Version="11.4.0" />
|
||||
<PackageReference Include="CoinW.Net" Version="3.4.0" />
|
||||
<PackageReference Include="CryptoCom.Net" Version="4.4.0" />
|
||||
<PackageReference Include="DeepCoin.Net" Version="4.4.0" />
|
||||
<PackageReference Include="GateIo.Net" Version="4.6.0" />
|
||||
<PackageReference Include="HyperLiquid.Net" Version="5.7.0" />
|
||||
<PackageReference Include="JK.BingX.Net" Version="4.5.0" />
|
||||
<PackageReference Include="JK.Bitget.Net" Version="4.5.0" />
|
||||
<PackageReference Include="JK.Mexc.Net" Version="6.6.0" />
|
||||
<PackageReference Include="JK.OKX.Net" Version="5.4.0" />
|
||||
<PackageReference Include="Jkorf.Aster.Net" Version="4.4.0" />
|
||||
<PackageReference Include="JKorf.BitMEX.Net" Version="4.4.0" />
|
||||
<PackageReference Include="JKorf.Coinbase.Net" Version="4.5.0" />
|
||||
<PackageReference Include="JKorf.HTX.Net" Version="9.5.0" />
|
||||
<PackageReference Include="JKorf.Lighter.Net" Version="1.5.0" />
|
||||
<PackageReference Include="JKorf.Upbit.Net" Version="3.4.0" />
|
||||
<PackageReference Include="KrakenExchange.Net" Version="8.4.0" />
|
||||
<PackageReference Include="Kucoin.Net" Version="9.5.0" />
|
||||
<PackageReference Include="LBank.Net" Version="1.1.0" />
|
||||
<PackageReference Include="Pionex.Net" Version="1.2.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="Toobit.Net" Version="4.3.0" />
|
||||
<PackageReference Include="Weex.Net" Version="2.3.0" />
|
||||
<PackageReference Include="WhiteBit.Net" Version="4.3.0" />
|
||||
<PackageReference Include="XT.Net" Version="4.3.0" />
|
||||
<PackageReference Include="Tapbit.Net" Version="1.0.0" />
|
||||
<PackageReference Include="Toobit.Net" Version="4.4.0" />
|
||||
<PackageReference Include="Weex.Net" Version="2.4.0" />
|
||||
<PackageReference Include="WhiteBit.Net" Version="4.4.0" />
|
||||
<PackageReference Include="XT.Net" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
@inject IMexcRestClient mexcClient
|
||||
@inject IOKXRestClient okxClient
|
||||
@inject IPionexRestClient pionexClient
|
||||
@inject ITapbitRestClient tapbitClient
|
||||
@inject IToobitRestClient toobitClient
|
||||
@inject IUpbitRestClient upbitClient
|
||||
@inject IWeexRestClient weexClient
|
||||
@@ -70,7 +71,8 @@
|
||||
var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync();
|
||||
var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt");
|
||||
|
||||
await Task.WhenAll(asterTask, binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bloFinTask, bitmexTask, bybitTask, coinexTask, coinWTask, deepCoinTask, gateioTask, htxTask, krakenTask, kucoinTask, lBankTask, mexcTask, okxTask, pionexTask);
|
||||
await Task.WhenAll(asterTask, binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bloFinTask, bitmexTask, bybitTask, coinexTask, coinWTask, deepCoinTask, gateioTask,
|
||||
htxTask, krakenTask, kucoinTask, lBankTask, mexcTask, okxTask, pionexTask, toobitTask, upbitTask, weexTask, whitebitTask, xtTask);
|
||||
|
||||
if (asterTask.Result.Success)
|
||||
_prices.Add("Aster", asterTask.Result.Data.LastPrice);
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace BlazorClient
|
||||
services.AddMexc();
|
||||
services.AddOKX();
|
||||
services.AddPionex();
|
||||
services.AddTapbit();
|
||||
services.AddToobit();
|
||||
services.AddUpbit();
|
||||
services.AddWeex();
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
@using OKX.Net.Interfaces.Clients;
|
||||
@using Pionex.Net.Interfaces.Clients;
|
||||
@using Upbit.Net.Interfaces.Clients;
|
||||
@using Tapbit.Net.Interfaces.Clients;
|
||||
@using Toobit.Net.Interfaces.Clients;
|
||||
@using Weex.Net.Interfaces.Clients
|
||||
@using WhiteBit.Net.Interfaces.Clients
|
||||
|
||||
@@ -59,6 +59,7 @@ Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider us
|
||||
||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)|-|-|
|
||||
||Tapbit|CEX|[JKorf/Tapbit.Net](https://github.com/JKorf/Tapbit.Net)|[](https://www.nuget.org/packages/Tapbit.Net)|[Link](https://www.tapbit.com/en/invite/SPBGMSK)|-|
|
||||
||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)|-|-|
|
||||
||Weex|CEX|[JKorf/Weex.Net](https://github.com/JKorf/Weex.Net)|[](https://www.nuget.org/packages/Weex.Net)|-|-|
|
||||
@@ -127,6 +128,12 @@ Various:
|
||||
* PlatformInfo now required support environment names in the constructor
|
||||
|
||||
## Release notes
|
||||
* Version 12.5.1 - 01 Sep 2026
|
||||
* Fixed KlineTracker reporting incorrect High/Low price on GetStats result
|
||||
* Fixed caching issue for auth requests
|
||||
* Fixed failed ISymbolOrderBook start potentially leaving a pending background task
|
||||
* Fixed subscription query completion potentially reporting success too early for topic-filtered routes
|
||||
|
||||
* Version 12.5.0 - 21 Aug 2026
|
||||
* Shared APIs
|
||||
* Added ISpotOrderManagementSocketClient for placing/canceling Spot orders via Shared websocket implementation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 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.5.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.5.1. 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user