1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-10-14 15:11:44 +00:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Jkorf c9ae1bf379 Updated to version 13.1.0 2026-09-28 11:23:36 +02:00
Jkorf 59acbd648e Added UnsubscribeAllAsync method to Shared APIs ISharedSubscription and IShaerdApiClientBase 2026-09-28 11:11:41 +02:00
Jkorf 7a11bd0c5e Added WaitAllAsync extension method 2026-09-25 09:33:45 +02:00
Jkorf 9502db6d58 Added SubscribeAllAsync extension methods for Shared API resolved capabilities 2026-09-25 08:32:57 +02:00
Jkorf e6ab899654 Updated examples 2026-09-24 11:41:44 +02:00
19 changed files with 300 additions and 103 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.0.0</PackageVersion> <PackageVersion>13.1.0</PackageVersion>
<AssemblyVersion>13.0.0</AssemblyVersion> <AssemblyVersion>13.1.0</AssemblyVersion>
<FileVersion>13.0.0</FileVersion> <FileVersion>13.1.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>
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis namespace CryptoExchange.Net.SharedApis
{ {
@@ -104,6 +105,11 @@ namespace CryptoExchange.Net.SharedApis
SharedCapabilityResolution<T>? GetCapability<T>(SharedCapabilityReference<T> capability, TradingMode tradingMode) SharedCapabilityResolution<T>? GetCapability<T>(SharedCapabilityReference<T> capability, TradingMode tradingMode)
where T : ISharedApiCapability; where T : ISharedApiCapability;
/// <summary>
/// Unsubscribes all subscriptions created by the underlying socket API
/// client and closes its subscription connections.
/// </summary>
Task UnsubscribeAllAsync();
} }
/// <summary> /// <summary>
@@ -209,6 +215,15 @@ namespace CryptoExchange.Net.SharedApis
return GetCapabilities<T>(tradingMode); return GetCapabilities<T>(tradingMode);
} }
/// <inheritdoc />
public Task UnsubscribeAllAsync()
{
return Task.WhenAll(
_sharedApis
.OfType<ISharedSubscription>()
.Select(x => x.UnsubscribeAllAsync()));
}
object? ISharedApiClientResolver.GetCapability(Type capabilityType) object? ISharedApiClientResolver.GetCapability(Type capabilityType)
{ {
return _sharedApis return _sharedApis
@@ -1,10 +1,12 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis namespace CryptoExchange.Net.SharedApis
{ {
@@ -624,5 +626,198 @@ namespace CryptoExchange.Net.SharedApis
.ParallelEnumerateAsync(); .ParallelEnumerateAsync();
} }
/// <summary>
/// Subscribe to trade updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeTradesSocket>> capabilities,
SubscribeTradeRequest request,
Action<DataEvent<SharedTrade[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToTradeUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to balance updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeBalancesSocket>> capabilities,
SubscribeBalancesRequest request,
Action<DataEvent<SharedBalance[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToBalanceUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to index price updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeIndexPriceSocket>> capabilities,
SubscribeIndexPriceRequest request,
Action<DataEvent<SharedIndexPrice>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToIndexPriceUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to kline updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeKlinesSocket>> capabilities,
SubscribeKlineRequest request,
Action<DataEvent<SharedKline>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToKlineUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to mark price updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeMarkPriceSocket>> capabilities,
SubscribeMarkPriceRequest request,
Action<DataEvent<SharedMarkPrice>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToMarkPriceUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to order book updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeOrderBookSocket>> capabilities,
SubscribeOrderBookRequest request,
Action<DataEvent<SharedOrderBook>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToOrderBookUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to incremental order book updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeIncrementalOrderBookSocket>> capabilities,
SubscribeOrderBookRequest request,
Action<DataEvent<SharedOrderBook>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToOrderBookUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to futures order updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeFuturesOrdersSocket>> capabilities,
SubscribeFuturesOrderRequest request,
Action<DataEvent<SharedFuturesOrderUpdate[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToFuturesOrderUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to spot order updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeSpotOrdersSocket>> capabilities,
SubscribeSpotOrderRequest request,
Action<DataEvent<SharedSpotOrderUpdate[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToSpotOrderUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to position updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribePositionsSocket>> capabilities,
SubscribePositionRequest request,
Action<DataEvent<SharedPosition[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToPositionUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to ticker updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeTickerSocket>> capabilities,
SubscribeTickerRequest request,
Action<DataEvent<SharedTicker>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToTickerUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to all ticker updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeAllTickersSocket>> capabilities,
SubscribeAllTickersRequest request,
Action<DataEvent<SharedTicker[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToAllTickersUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Subscribe to user trade updates for all capabilities in parallel and return results as they arrive
/// </summary>
public static IAsyncEnumerable<WebSocketResult<UpdateSubscription>> SubscribeAllAsync(
this IEnumerable<SharedCapabilityResolution<ISubscribeUserTradesSocket>> capabilities,
SubscribeUserTradeRequest request,
Action<DataEvent<SharedUserTrade[]>> onData,
CancellationToken ct = default)
{
return capabilities
.Select(x => x.Capability.SubscribeToUserTradeUpdatesAsync(request, onData, ct))
.ParallelEnumerateAsync();
}
/// <summary>
/// Wait for all results from an IAsyncEnumerable and return them as an array
/// </summary>
public static async Task<T[]> WaitAllAsync<T>(this IAsyncEnumerable<T> enumerable)
{
var result = new List<T>();
await foreach (var itemResult in enumerable.ConfigureAwait(false))
result.Add(itemResult);
return result.ToArray();
}
} }
} }
@@ -1,13 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis namespace CryptoExchange.Net.SharedApis
{ {
/// <summary> /// <summary>
/// Shared WebSocket API subscription capabitlity /// Shared WebSocket API subscription capability
/// </summary> /// </summary>
public interface ISharedSubscription : ISharedSocket public interface ISharedSubscription : ISharedSocket
{ {
/// <summary>
/// Unsubscribes all subscriptions created by the underlying socket API
/// client and closes its subscription connections.
/// </summary>
Task UnsubscribeAllAsync();
} }
} }
+29 -30
View File
@@ -5,37 +5,36 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="13.5.0" /> <PackageReference Include="Binance.Net" Version="13.6.0" />
<PackageReference Include="Bitfinex.Net" Version="11.4.0" /> <PackageReference Include="Bitfinex.Net" Version="11.6.0" />
<PackageReference Include="BitMart.Net" Version="4.4.0" /> <PackageReference Include="BitMart.Net" Version="4.5.0" />
<PackageReference Include="BloFin.Net" Version="3.4.0" /> <PackageReference Include="BloFin.Net" Version="3.5.0" />
<PackageReference Include="Bybit.Net" Version="7.5.0" /> <PackageReference Include="Bybit.Net" Version="7.6.0" />
<PackageReference Include="CoinEx.Net" Version="11.4.0" /> <PackageReference Include="CoinEx.Net" Version="11.5.0" />
<PackageReference Include="CoinW.Net" Version="3.4.0" /> <PackageReference Include="CoinW.Net" Version="3.5.0" />
<PackageReference Include="CryptoCom.Net" Version="4.4.0" /> <PackageReference Include="CryptoCom.Net" Version="4.5.0" />
<PackageReference Include="DeepCoin.Net" Version="4.4.0" /> <PackageReference Include="DeepCoin.Net" Version="4.5.0" />
<PackageReference Include="GateIo.Net" Version="4.6.0" /> <PackageReference Include="GateIo.Net" Version="4.7.0" />
<PackageReference Include="HyperLiquid.Net" Version="5.7.0" /> <PackageReference Include="HyperLiquid.Net" Version="5.8.0" />
<PackageReference Include="JK.BingX.Net" Version="4.5.0" /> <PackageReference Include="JK.BingX.Net" Version="4.6.0" />
<PackageReference Include="JK.Bitget.Net" Version="4.5.0" /> <PackageReference Include="JK.Bitget.Net" Version="4.6.0" />
<PackageReference Include="JK.Mexc.Net" Version="6.6.0" /> <PackageReference Include="JK.Mexc.Net" Version="6.7.0" />
<PackageReference Include="JK.OKX.Net" Version="5.4.0" /> <PackageReference Include="JK.OKX.Net" Version="5.5.0" />
<PackageReference Include="Jkorf.Aster.Net" Version="4.4.0" /> <PackageReference Include="Jkorf.Aster.Net" Version="4.5.0" />
<PackageReference Include="JKorf.BitMEX.Net" Version="4.4.0" /> <PackageReference Include="JKorf.Coinbase.Net" Version="4.6.0" />
<PackageReference Include="JKorf.Coinbase.Net" Version="4.5.0" /> <PackageReference Include="JKorf.HTX.Net" Version="9.6.0" />
<PackageReference Include="JKorf.HTX.Net" Version="9.5.0" /> <PackageReference Include="JKorf.Lighter.Net" Version="1.6.0" />
<PackageReference Include="JKorf.Lighter.Net" Version="1.5.0" /> <PackageReference Include="JKorf.Upbit.Net" Version="3.5.0" />
<PackageReference Include="JKorf.Upbit.Net" Version="3.4.0" /> <PackageReference Include="KrakenExchange.Net" Version="8.5.0" />
<PackageReference Include="KrakenExchange.Net" Version="8.4.0" /> <PackageReference Include="Kucoin.Net" Version="9.6.0" />
<PackageReference Include="Kucoin.Net" Version="9.5.0" /> <PackageReference Include="LBank.Net" Version="1.2.0" />
<PackageReference Include="LBank.Net" Version="1.1.0" /> <PackageReference Include="Pionex.Net" Version="1.3.0" />
<PackageReference Include="Pionex.Net" Version="1.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Tapbit.Net" Version="1.0.0" /> <PackageReference Include="Tapbit.Net" Version="1.1.0" />
<PackageReference Include="Toobit.Net" Version="4.4.0" /> <PackageReference Include="Toobit.Net" Version="4.5.0" />
<PackageReference Include="Weex.Net" Version="2.4.0" /> <PackageReference Include="Weex.Net" Version="2.5.0" />
<PackageReference Include="WhiteBit.Net" Version="4.4.0" /> <PackageReference Include="WhiteBit.Net" Version="4.5.0" />
<PackageReference Include="XT.Net" Version="4.5.0" /> <PackageReference Include="XT.Net" Version="4.6.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+1 -6
View File
@@ -5,7 +5,6 @@
@inject IBitfinexRestClient bitfinexClient @inject IBitfinexRestClient bitfinexClient
@inject IBitgetRestClient bitgetClient @inject IBitgetRestClient bitgetClient
@inject IBitMartRestClient bitmartClient @inject IBitMartRestClient bitmartClient
@inject IBitMEXRestClient bitmexClient
@inject IBloFinRestClient bloFinClient @inject IBloFinRestClient bloFinClient
@inject IBybitRestClient bybitClient @inject IBybitRestClient bybitClient
@inject ICoinbaseRestClient coinbaseClient @inject ICoinbaseRestClient coinbaseClient
@@ -47,7 +46,6 @@
var bitfinexTask = bitfinexClient.ExchangeApi.ExchangeData.GetTickerAsync("tBTCUSD"); var bitfinexTask = bitfinexClient.ExchangeApi.ExchangeData.GetTickerAsync("tBTCUSD");
var bitgetTask = bitgetClient.SpotApiV2.ExchangeData.GetTickersAsync("BTCUSDT"); var bitgetTask = bitgetClient.SpotApiV2.ExchangeData.GetTickersAsync("BTCUSDT");
var bitmartTask = bitmartClient.SpotApi.ExchangeData.GetTickerAsync("BTC_USDT"); var bitmartTask = bitmartClient.SpotApi.ExchangeData.GetTickerAsync("BTC_USDT");
var bitmexTask = bitmexClient.ExchangeApi.ExchangeData.GetSymbolsAsync("XBT_USDT");
var bloFinTask = bloFinClient.FuturesApi.ExchangeData.GetTickersAsync("BTC-USDT"); var bloFinTask = bloFinClient.FuturesApi.ExchangeData.GetTickersAsync("BTC-USDT");
var bybitTask = bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT"); var bybitTask = bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT");
var coinbaseTask = coinbaseClient.AdvancedTradeApi.ExchangeData.GetSymbolAsync("BTC-USDT"); var coinbaseTask = coinbaseClient.AdvancedTradeApi.ExchangeData.GetSymbolAsync("BTC-USDT");
@@ -71,7 +69,7 @@
var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync(); var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync();
var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt"); var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt");
await Task.WhenAll(asterTask, binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bloFinTask, bitmexTask, bybitTask, coinexTask, coinWTask, deepCoinTask, gateioTask, await Task.WhenAll(asterTask, binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bloFinTask, bybitTask, coinexTask, coinWTask, deepCoinTask, gateioTask,
htxTask, krakenTask, kucoinTask, lBankTask, mexcTask, okxTask, pionexTask, toobitTask, upbitTask, weexTask, whitebitTask, xtTask); htxTask, krakenTask, kucoinTask, lBankTask, mexcTask, okxTask, pionexTask, toobitTask, upbitTask, weexTask, whitebitTask, xtTask);
if (asterTask.Result.Success) if (asterTask.Result.Success)
@@ -92,9 +90,6 @@
if (bitmartTask.Result.Success) if (bitmartTask.Result.Success)
_prices.Add("BitMart", bitmartTask.Result.Data.LastPrice); _prices.Add("BitMart", bitmartTask.Result.Data.LastPrice);
if (bitmexTask.Result.Success)
_prices.Add("BitMEX", bitmexTask.Result.Data.First().LastPrice);
if (bloFinTask.Result.Success) if (bloFinTask.Result.Success)
_prices.Add("BloFin", bloFinTask.Result.Data.First().LastPrice); _prices.Add("BloFin", bloFinTask.Result.Data.First().LastPrice);
@@ -5,7 +5,6 @@
@inject IBitfinexSocketClient bitfinexSocketClient @inject IBitfinexSocketClient bitfinexSocketClient
@inject IBitgetSocketClient bitgetSocketClient @inject IBitgetSocketClient bitgetSocketClient
@inject IBitMartSocketClient bitmartSocketClient @inject IBitMartSocketClient bitmartSocketClient
@inject IBitMEXSocketClient bitmexSocketClient
@inject IBloFinSocketClient bloFinSocketClient @inject IBloFinSocketClient bloFinSocketClient
@inject IBybitSocketClient bybitSocketClient @inject IBybitSocketClient bybitSocketClient
@inject ICoinbaseSocketClient coinbaseSocketClient @inject ICoinbaseSocketClient coinbaseSocketClient
@@ -56,7 +55,6 @@
bitfinexSocketClient.ExchangeApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)), bitfinexSocketClient.ExchangeApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
bitgetSocketClient.SpotApiV2.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bitget", data.Data.First().LastPrice)), bitgetSocketClient.SpotApiV2.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bitget", data.Data.First().LastPrice)),
bitmartSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("BitMart", data.Data.LastPrice)), bitmartSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("BitMart", data.Data.LastPrice)),
bitmexSocketClient.ExchangeApi.SubscribeToSymbolUpdatesAsync("ETH_XBT", data => UpdateData("BitMEX", data.Data.LastPrice ?? 0)),
// BloFin doesn't support the ETH/BTC pair // BloFin doesn't support the ETH/BTC pair
//bloFinSocketClient.FuturesApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("BloFin", data.Data.LastPrice)), //bloFinSocketClient.FuturesApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("BloFin", data.Data.LastPrice)),
bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)), bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)),
@@ -7,7 +7,6 @@
@using Bitfinex.Net.Interfaces @using Bitfinex.Net.Interfaces
@using Bitget.Net.Interfaces; @using Bitget.Net.Interfaces;
@using BitMart.Net.Interfaces; @using BitMart.Net.Interfaces;
@using BitMEX.Net.Interfaces;
@using BloFin.Net.Interfaces; @using BloFin.Net.Interfaces;
@using Bybit.Net.Interfaces @using Bybit.Net.Interfaces
@using CoinEx.Net.Interfaces @using CoinEx.Net.Interfaces
@@ -40,7 +39,6 @@
@inject IBitfinexOrderBookFactory bitfinexFactory @inject IBitfinexOrderBookFactory bitfinexFactory
@inject IBitgetOrderBookFactory bitgetFactory @inject IBitgetOrderBookFactory bitgetFactory
@inject IBitMartOrderBookFactory bitmartFactory @inject IBitMartOrderBookFactory bitmartFactory
@inject IBitMEXOrderBookFactory bitmexFactory
@inject IBloFinOrderBookFactory bloFinFactory @inject IBloFinOrderBookFactory bloFinFactory
@inject IBybitOrderBookFactory bybitFactory @inject IBybitOrderBookFactory bybitFactory
@inject ICoinbaseOrderBookFactory coinbaseFactory @inject ICoinbaseOrderBookFactory coinbaseFactory
@@ -103,7 +101,6 @@
{ "Bitfinex", bitfinexFactory.Create("tETHBTC") }, { "Bitfinex", bitfinexFactory.Create("tETHBTC") },
{ "Bitget", bitgetFactory.CreateSpot("ETHBTC") }, { "Bitget", bitgetFactory.CreateSpot("ETHBTC") },
{ "BitMart", bitmartFactory.CreateSpot("ETH_BTC", null) }, { "BitMart", bitmartFactory.CreateSpot("ETH_BTC", null) },
{ "BitMEX", bitmexFactory.Create("ETH_XBT") },
{ "BloFin", bloFinFactory.CreateFutures("ETH-USDT") }, { "BloFin", bloFinFactory.CreateFutures("ETH-USDT") },
{ "Bybit", bybitFactory.Create("ETHBTC", Bybit.Net.Enums.Category.Spot) }, { "Bybit", bybitFactory.Create("ETHBTC", Bybit.Net.Enums.Category.Spot) },
{ "Coinbase", coinbaseFactory.Create("ETH-BTC", null) }, { "Coinbase", coinbaseFactory.Create("ETH-BTC", null) },
+2 -2
View File
@@ -1,7 +1,7 @@
@page "/SpotClient" @page "/SpotClient"
@using CryptoExchange.Net.SharedApis @using CryptoExchange.Net.SharedApis
@using System.Diagnostics @using System.Diagnostics
@inject IEnumerable<ISpotTickerRestClient> restClients @inject IEnumerable<IGetTicker> restClients
<h3>ETH-BTC prices:</h3> <h3>ETH-BTC prices:</h3>
@foreach(var price in _prices.OrderBy(p => p.Key)) @foreach(var price in _prices.OrderBy(p => p.Key))
@@ -15,7 +15,7 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "BTC"); var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "BTC");
var tasks = restClients.Select(x => x.GetSpotTickerAsync(new GetTickerRequest(symbol))); var tasks = restClients.Where(x => x.SupportedTradingModes.Contains(TradingMode.Spot)).Select(x => x.GetTickerAsync(new GetTickerRequest(symbol)));
await foreach (var ticker in ParallelEnumerateAsync(tasks)) await foreach (var ticker in ParallelEnumerateAsync(tasks))
{ {
if (ticker.Success) if (ticker.Success)
@@ -6,7 +6,6 @@
@using BingX.Net.Interfaces @using BingX.Net.Interfaces
@using Bitfinex.Net.Interfaces @using Bitfinex.Net.Interfaces
@using Bitget.Net.Interfaces; @using Bitget.Net.Interfaces;
@using BitMEX.Net.Interfaces;
@using BitMart.Net.Interfaces; @using BitMart.Net.Interfaces;
@using BloFin.Net.Interfaces @using BloFin.Net.Interfaces
@using Bybit.Net.Interfaces @using Bybit.Net.Interfaces
@@ -40,7 +39,6 @@
@inject IBitfinexTrackerFactory bitfinexFactory @inject IBitfinexTrackerFactory bitfinexFactory
@inject IBitgetTrackerFactory bitgetFactory @inject IBitgetTrackerFactory bitgetFactory
@inject IBitMartTrackerFactory bitmartFactory @inject IBitMartTrackerFactory bitmartFactory
@inject IBitMEXTrackerFactory bitmexFactory
@inject IBloFinTrackerFactory bloFinFactory @inject IBloFinTrackerFactory bloFinFactory
@inject IBybitTrackerFactory bybitFactory @inject IBybitTrackerFactory bybitFactory
@inject ICoinbaseTrackerFactory coinbaseFactory @inject ICoinbaseTrackerFactory coinbaseFactory
@@ -96,7 +94,6 @@
{ bitfinexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) }, { bitfinexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
{ bitgetFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) }, { bitgetFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
{ bitmartFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) }, { bitmartFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
{ bitmexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
{ bloFinFactory.CreateTradeTracker(futuresSymbol, period: TimeSpan.FromMinutes(5)) }, { bloFinFactory.CreateTradeTracker(futuresSymbol, period: TimeSpan.FromMinutes(5)) },
{ bybitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) }, { bybitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
{ coinbaseFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) }, { coinbaseFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
-1
View File
@@ -39,7 +39,6 @@ namespace BlazorClient
services.AddBitfinex(); services.AddBitfinex();
services.AddBitget(); services.AddBitget();
services.AddBitMart(); services.AddBitMart();
services.AddBitMEX();
services.AddBloFin(); services.AddBloFin();
services.AddBybit(); services.AddBybit();
services.AddCoinbase(); services.AddCoinbase();
-1
View File
@@ -14,7 +14,6 @@
@using Bitfinex.Net.Interfaces.Clients; @using Bitfinex.Net.Interfaces.Clients;
@using Bitget.Net.Interfaces.Clients; @using Bitget.Net.Interfaces.Clients;
@using BitMart.Net.Interfaces.Clients; @using BitMart.Net.Interfaces.Clients;
@using BitMEX.Net.Interfaces.Clients;
@using BloFin.Net.Interfaces.Clients; @using BloFin.Net.Interfaces.Clients;
@using Bybit.Net.Interfaces.Clients; @using Bybit.Net.Interfaces.Clients;
@using Coinbase.Net.Interfaces.Clients; @using Coinbase.Net.Interfaces.Clients;
+2 -2
View File
@@ -8,8 +8,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="12.13.0" /> <PackageReference Include="Binance.Net" Version="13.6.0" />
<PackageReference Include="Bybit.Net" Version="6.13.0" /> <PackageReference Include="Bybit.Net" Version="7.6.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+26 -34
View File
@@ -25,18 +25,22 @@ var exchanges = new Dictionary<string, ExchangeClients>(StringComparer.OrdinalIg
{ {
["binance"] = new ExchangeClients( ["binance"] = new ExchangeClients(
"Binance", "Binance",
binanceRest.SpotApi.SharedClient, binanceRest.SpotApi.SharedApi,
binanceRest.SpotApi.SharedClient, binanceRest.SpotApi.SharedApi,
binanceRest.SpotApi.SharedClient, binanceRest.SpotApi.SharedApi,
binanceRest.SpotApi.SharedClient, binanceRest.SpotApi.SharedApi,
binanceSocket.SpotApi.SharedClient), binanceRest.SpotApi.SharedApi,
binanceRest.SpotApi.SharedApi,
binanceSocket.SpotApi.SharedApi),
["bybit"] = new ExchangeClients( ["bybit"] = new ExchangeClients(
"Bybit", "Bybit",
bybitRest.V5Api.SharedClient, bybitRest.V5Api.SharedApi,
bybitRest.V5Api.SharedClient, bybitRest.V5Api.SharedApi,
bybitRest.V5Api.SharedClient, bybitRest.V5Api.SharedApi,
bybitRest.V5Api.SharedClient, bybitRest.V5Api.SharedApi,
bybitSocket.V5SpotApi.SharedClient) bybitRest.V5Api.SharedApi,
bybitRest.V5Api.SharedApi,
bybitSocket.V5SpotApi.SharedApi)
}; };
PrintHelp(); PrintHelp();
@@ -98,7 +102,7 @@ async Task PrintPricesAsync(string[] args)
var symbol = GetSymbol(args, 1); var symbol = GetSymbol(args, 1);
var tasks = exchanges.Values.Select(async exchange => var tasks = exchanges.Values.Select(async exchange =>
{ {
var result = await exchange.TickerRest.GetSpotTickerAsync(new GetTickerRequest(symbol)); var result = await exchange.TickerRest.GetTickerAsync(new GetTickerRequest(symbol));
if (!result.Success) if (!result.Success)
{ {
Console.WriteLine($"{exchange.Name,-8} error: {result.Error}"); Console.WriteLine($"{exchange.Name,-8} error: {result.Error}");
@@ -171,7 +175,7 @@ async Task PrintOpenOrdersAsync(string[] args)
var exchange = GetExchange(args, 1); var exchange = GetExchange(args, 1);
var symbol = GetSymbol(args, 2); var symbol = GetSymbol(args, 2);
var result = await exchange.OrderRest.GetOpenSpotOrdersAsync(new GetOpenOrdersRequest(symbol)); var result = await exchange.GetOpenOrdersRest.GetOpenSpotOrdersAsync(new GetOpenOrdersRequest(symbol));
if (!result.Success) if (!result.Success)
{ {
Console.WriteLine($"Open orders request failed: {result.Error}"); Console.WriteLine($"Open orders request failed: {result.Error}");
@@ -203,23 +207,9 @@ async Task PlaceLimitOrderAsync(string[] args)
SharedQuantity.Base(quantity), SharedQuantity.Base(quantity),
price, price,
SharedTimeInForce.GoodTillCanceled, SharedTimeInForce.GoodTillCanceled,
exchange.OrderRest.GenerateClientOrderId()); exchange.PlaceOrderRest.GenerateClientOrderId());
var validationError = exchange.OrderRest.PlaceSpotOrderOptions.ValidateRequest( var result = await exchange.PlaceOrderRest.PlaceSpotOrderAsync(request);
exchange.Name,
request,
TradingMode.Spot,
exchange.OrderRest.SupportedTradingModes,
exchange.OrderRest.SpotSupportedOrderTypes,
exchange.OrderRest.SpotSupportedTimeInForce,
exchange.OrderRest.SpotSupportedOrderQuantity);
if (validationError != null)
{
Console.WriteLine($"Order request is not valid for {exchange.Name}: {validationError}");
return;
}
var result = await exchange.OrderRest.PlaceSpotOrderAsync(request);
Console.WriteLine(result.Success Console.WriteLine(result.Success
? $"Order placed. Id: {result.Data.Id}" ? $"Order placed. Id: {result.Data.Id}"
: $"Order failed: {result.Error}"); : $"Order failed: {result.Error}");
@@ -233,7 +223,7 @@ async Task CancelOrderAsync(string[] args)
if (string.IsNullOrWhiteSpace(orderId)) if (string.IsNullOrWhiteSpace(orderId))
throw new ArgumentException("Missing order id. Example: cancel-order binance BTC USDT 123456"); throw new ArgumentException("Missing order id. Example: cancel-order binance BTC USDT 123456");
var result = await exchange.OrderRest.CancelSpotOrderAsync(new CancelOrderRequest(symbol, orderId)); var result = await exchange.CancelOrderRest.CancelSpotOrderAsync(new CancelOrderRequest(symbol, orderId));
Console.WriteLine(result.Success Console.WriteLine(result.Success
? $"Order canceled. Id: {result.Data.Id}" ? $"Order canceled. Id: {result.Data.Id}"
: $"Cancel failed: {result.Error}"); : $"Cancel failed: {result.Error}");
@@ -296,8 +286,10 @@ static void PrintHelp()
internal record ExchangeClients( internal record ExchangeClients(
string Name, string Name,
ISpotTickerRestClient TickerRest, IGetTickerRest TickerRest,
IOrderBookRestClient OrderBookRest, IGetOrderBookRest OrderBookRest,
IBalanceRestClient BalanceRest, IGetBalancesRest BalanceRest,
ISpotOrderRestClient OrderRest, IPlaceSpotOrderRest PlaceOrderRest,
ITickerSocketClient TickerSocket); IGetOpenSpotOrdersRest GetOpenOrdersRest,
ICancelSpotOrderRest CancelOrderRest,
ISubscribeTickerSocket TickerSocket);
+9 -9
View File
@@ -5,13 +5,13 @@ using CryptoExchange.Net.SharedApis;
using OKX.Net.Clients; using OKX.Net.Clients;
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT"); var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
var binanceSpotRestClient = new BinanceRestClient().SpotApi.SharedClient; var binanceSpotRestClient = new BinanceRestClient().SpotApi.SharedApi;
var okxSpotRestClient = new OKXRestClient().UnifiedApi.SharedClient; var okxSpotRestClient = new OKXRestClient().UnifiedApi.SharedApi;
var bitmartSpotRestClient = new BitMartRestClient().SpotApi.SharedClient; var bitmartSpotRestClient = new BitMartRestClient().SpotApi.SharedApi;
var binanceSpotSocketClient = new BinanceSocketClient().SpotApi.SharedClient; var binanceSpotSocketClient = new BinanceSocketClient().SpotApi.SharedApi;
var okxSpotSocketClient = new OKXSocketClient().UnifiedApi.SharedClient; var okxSpotSocketClient = new OKXSocketClient().UnifiedApi.SharedApi;
var bitmartSpotSocketClient = new BitMartSocketClient().SpotApi.SharedClient; var bitmartSpotSocketClient = new BitMartSocketClient().SpotApi.SharedApi;
await GetLastTradePriceAsync(binanceSpotRestClient, symbol); await GetLastTradePriceAsync(binanceSpotRestClient, symbol);
await GetLastTradePriceAsync(okxSpotRestClient, symbol); await GetLastTradePriceAsync(okxSpotRestClient, symbol);
@@ -31,9 +31,9 @@ Console.ReadLine();
foreach (var subscription in subscriptions) foreach (var subscription in subscriptions)
await subscription.CloseAsync(); await subscription.CloseAsync();
async Task GetLastTradePriceAsync(ISpotTickerRestClient client, SharedSymbol symbol) async Task GetLastTradePriceAsync(IGetTickerRest client, SharedSymbol symbol)
{ {
var result = await client.GetSpotTickerAsync(new GetTickerRequest(symbol)); var result = await client.GetTickerAsync(new GetTickerRequest(symbol));
if (!result.Success) if (!result.Success)
{ {
Console.WriteLine($"Failed to get ticker: {result.Error}"); Console.WriteLine($"Failed to get ticker: {result.Error}");
@@ -43,7 +43,7 @@ async Task GetLastTradePriceAsync(ISpotTickerRestClient client, SharedSymbol sym
Console.WriteLine($"{client.Exchange} {result.Data.Symbol}: {result.Data.LastPrice}"); Console.WriteLine($"{client.Exchange} {result.Data.Symbol}: {result.Data.LastPrice}");
} }
async Task SubscribeTickerUpdatesAsync(ITickerSocketClient client, SharedSymbol symbol, ICollection<UpdateSubscription> subscriptions) async Task SubscribeTickerUpdatesAsync(ISubscribeTickerSocket client, SharedSymbol symbol, ICollection<UpdateSubscription> subscriptions)
{ {
var result = await client.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequest(symbol), update => var result = await client.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequest(symbol), update =>
{ {
+3 -3
View File
@@ -8,9 +8,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="12.13.0" /> <PackageReference Include="Binance.Net" Version="13.6.0" />
<PackageReference Include="BitMart.Net" Version="3.11.2" /> <PackageReference Include="BitMart.Net" Version="4.5.0" />
<PackageReference Include="JK.OKX.Net" Version="4.14.0" /> <PackageReference Include="JK.OKX.Net" Version="5.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+5
View File
@@ -127,6 +127,11 @@ 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.1.0 - 28 Sep 2026
* Added UnsubscribeAllAsync method to Shared APIs ISharedSubscription and ISharedApiClientBase
* Added WaitAllAsync extension method for waiting on an IAsyncEnumerable to finish
* Added SubscribeAllAsync extension methods for Shared API resolved capabilities
* Version 13.0.0 - 23 Sep 2026 * Version 13.0.0 - 23 Sep 2026
* Shared APIs * Shared APIs
* Added Shared API V2 with fine-grained capability interfaces for individual REST requests, WebSocket requests and subscriptions * Added Shared API V2 with fine-grained capability interfaces for individual REST requests, WebSocket requests and subscriptions
+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.0.0 Library version: 13.1.0
CryptoExchange.Net package version: 13.0.0 CryptoExchange.Net package version: 13.1.0
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.0.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.1.0. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, and net10.0; Native AOT is supported.
## Shared API V2 ## Shared API V2