mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-10-04 10:21:13 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9ae1bf379 | |||
| 59acbd648e | |||
| 7a11bd0c5e | |||
| 9502db6d58 | |||
| e6ab899654 | |||
| a8517d2c76 |
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) },
|
||||||
|
|||||||
@@ -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)) },
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
# CryptoExchange.Net Shared API V2 map
|
||||||
|
|
||||||
|
This is a compact map for choosing Shared API V2 types. All listed types are in `CryptoExchange.Net.SharedApis`. Exchange libraries implement subsets; the map describes available abstractions, not guaranteed exchange support.
|
||||||
|
|
||||||
|
## Entry points
|
||||||
|
|
||||||
|
| Need | Use |
|
||||||
|
| --- | --- |
|
||||||
|
| Known exchange and API surface | Its typed `.SharedApi` property |
|
||||||
|
| Runtime selection within one exchange | Exchange-wide `I[Exchange]SharedApiClient` |
|
||||||
|
| Strongly typed runtime lookup key | `SharedCapabilities` |
|
||||||
|
| One preferred match | `GetCapability(...)` |
|
||||||
|
| Every matching surface/transport in one exchange | `GetCapabilities(...)` |
|
||||||
|
| V1 compatibility during migration | `.SharedClient` |
|
||||||
|
|
||||||
|
Prefer a direct typed `.SharedApi` when the API surface is known. Dynamic lookup returns `SharedCapabilityResolution<T>?`; handle `null`.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
IGetTickerRest ticker = restClient.SpotApi.SharedApi;
|
||||||
|
var result = await ticker.GetTickerAsync(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var match = sharedClient.GetCapability(
|
||||||
|
SharedCapabilities.Tickers.GetTicker.Rest,
|
||||||
|
TradingMode.Spot);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interface suffixes and results
|
||||||
|
|
||||||
|
| Interface shape | Transport | Typical result |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Operation with no suffix, e.g. `IPlaceSpotOrder` | selected/preferred | `IExchangeCallResult<T>` |
|
||||||
|
| `...Rest` | REST | `HttpResult<T>` |
|
||||||
|
| socket command, e.g. `IPlaceSpotOrderSocket` | WebSocket | `QueryResult<T>` |
|
||||||
|
| subscription, e.g. `ISubscribeTickerSocket` | WebSocket | `WebSocketResult<UpdateSubscription>` |
|
||||||
|
|
||||||
|
When a `SharedCapabilities` entry supports multiple transports, use the base entry for preferred transport, `.Rest` for REST, or `.Socket` for socket. Exchange-wide preferred transport normally defaults to REST.
|
||||||
|
|
||||||
|
## Market data
|
||||||
|
|
||||||
|
| Operation | Capability interface | Lookup reference |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Get one ticker | `IGetTickerRest` | `SharedCapabilities.Tickers.GetTicker.Rest` |
|
||||||
|
| Get all tickers | `IGetAllTickersRest` | `SharedCapabilities.Tickers.GetAllTickers.Rest` |
|
||||||
|
| Subscribe to one ticker | `ISubscribeTickerSocket` | `SharedCapabilities.Tickers.SubscribeTicker` |
|
||||||
|
| Subscribe to all tickers | `ISubscribeAllTickersSocket` | `SharedCapabilities.Tickers.SubscribeAllTickers` |
|
||||||
|
| Subscribe to book ticker | `ISubscribeBookTickerSocket` | `SharedCapabilities.Tickers.SubscribeBookTicker` |
|
||||||
|
| Get order book | `IGetOrderBookRest` | `SharedCapabilities.OrderBooks.GetOrderBook.Rest` |
|
||||||
|
| Get book ticker | `IGetBookTickerRest` | `SharedCapabilities.OrderBooks.GetBookTicker.Rest` |
|
||||||
|
| Subscribe to order book | `ISubscribeOrderBookSocket` | `SharedCapabilities.OrderBooks.SubscribeOrderBook` |
|
||||||
|
| Subscribe to incremental book | `ISubscribeIncrementalOrderBookSocket` | `SharedCapabilities.OrderBooks.SubscribeIncrementalOrderBook` |
|
||||||
|
| Get klines | `IGetKlinesRest` | `SharedCapabilities.Klines.GetKlines.Rest` |
|
||||||
|
| Subscribe to klines | `ISubscribeKlinesSocket` | `SharedCapabilities.Klines.SubscribeKlines` |
|
||||||
|
| Get recent trades | `IGetRecentTradesRest` | `SharedCapabilities.Trades.GetRecentTrades.Rest` |
|
||||||
|
| Subscribe to trades | `ISubscribeTradesSocket` | `SharedCapabilities.Trades.SubscribeTrades` |
|
||||||
|
| Get spot symbols | `IGetSpotSymbolsRest` | `SharedCapabilities.Symbols.GetSpotSymbols.Rest` |
|
||||||
|
| Get futures symbols | `IGetFuturesSymbolsRest` | `SharedCapabilities.Symbols.GetFuturesSymbols.Rest` |
|
||||||
|
|
||||||
|
Ticker V2 methods are `GetTickerAsync` and `GetAllTickersAsync`; both use `SharedTicker` for spot and futures. Use separate mark-price, index-price, funding, or open-interest capabilities for derivatives-specific data.
|
||||||
|
|
||||||
|
## Spot orders
|
||||||
|
|
||||||
|
| Operation | Capability family | Lookup reference |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Place | `IPlaceSpotOrder`, `IPlaceSpotOrderRest`, `IPlaceSpotOrderSocket` | `SharedCapabilities.Orders.Spot.PlaceOrder[.Rest/.Socket]` |
|
||||||
|
| Edit | `IEditSpotOrder`, `IEditSpotOrderRest`, `IEditSpotOrderSocket` | `SharedCapabilities.Orders.Spot.EditOrder[.Rest/.Socket]` |
|
||||||
|
| Cancel | `ICancelSpotOrder`, `ICancelSpotOrderRest`, `ICancelSpotOrderSocket` | `SharedCapabilities.Orders.Spot.CancelOrder[.Rest/.Socket]` |
|
||||||
|
| Get one | `IGetSpotOrderRest` | `SharedCapabilities.Orders.Spot.GetOrder.Rest` |
|
||||||
|
| Get open | `IGetOpenSpotOrdersRest` | `SharedCapabilities.Orders.Spot.GetOpenOrders.Rest` |
|
||||||
|
| Get closed | `IGetClosedSpotOrdersRest` | `SharedCapabilities.Orders.Spot.GetClosedOrders.Rest` |
|
||||||
|
| Subscribe to updates | `ISubscribeSpotOrdersSocket` | `SharedCapabilities.Orders.Spot.SubscribeOrders` |
|
||||||
|
|
||||||
|
Client-order-id variants exist for edit, cancel, and get. Batch placement and cancel-all capabilities also exist; resolve them only when the application needs those operations and the exchange supports them.
|
||||||
|
|
||||||
|
## Futures orders
|
||||||
|
|
||||||
|
| Operation | Capability family | Lookup reference |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Place | `IPlaceFuturesOrder`, `IPlaceFuturesOrderRest`, `IPlaceFuturesOrderSocket` | `SharedCapabilities.Orders.Futures.PlaceOrder[.Rest/.Socket]` |
|
||||||
|
| Edit | `IEditFuturesOrder`, `IEditFuturesOrderRest`, `IEditFuturesOrderSocket` | `SharedCapabilities.Orders.Futures.EditOrder[.Rest/.Socket]` |
|
||||||
|
| Cancel | `ICancelFuturesOrder`, `ICancelFuturesOrderRest`, `ICancelFuturesOrderSocket` | `SharedCapabilities.Orders.Futures.CancelOrder[.Rest/.Socket]` |
|
||||||
|
| Get one | `IGetFuturesOrderRest` | `SharedCapabilities.Orders.Futures.GetOrder.Rest` |
|
||||||
|
| Get open | `IGetOpenFuturesOrdersRest` | `SharedCapabilities.Orders.Futures.GetOpenOrders.Rest` |
|
||||||
|
| Get closed | `IGetClosedFuturesOrdersRest` | `SharedCapabilities.Orders.Futures.GetClosedOrders.Rest` |
|
||||||
|
| Subscribe to updates | `ISubscribeFuturesOrdersSocket` | `SharedCapabilities.Orders.Futures.SubscribeOrders` |
|
||||||
|
|
||||||
|
Specify `TradingMode.PerpetualLinear`, `PerpetualInverse`, or the appropriate delivery mode when multiple futures APIs can match.
|
||||||
|
|
||||||
|
## Account and derivatives
|
||||||
|
|
||||||
|
Use the following capability families for common account workflows:
|
||||||
|
|
||||||
|
| Area | Common interfaces |
|
||||||
|
| --- | --- |
|
||||||
|
| Balances | `IGetBalancesRest`, `ISubscribeBalancesSocket` |
|
||||||
|
| Positions | `IGetPositionsRest`, `ISubscribePositionsSocket`, `IGetPositionHistoryRest` |
|
||||||
|
| User trades | `IGetSpotUserTradeHistoryRest`, `IGetFuturesUserTradeHistoryRest`, `ISubscribeUserTradesSocket` |
|
||||||
|
| Fees | `IGetFeesRest` |
|
||||||
|
| Deposits | `IGetDepositAddressesRest`, `IGetDepositHistoryRest` |
|
||||||
|
| Withdrawals | `IWithdrawRest`, `IGetWithdrawalHistoryRest` |
|
||||||
|
| Transfers | `ITransferRest`, `IGetTransferHistoryRest` |
|
||||||
|
| Leverage | `IGetLeverageRest`, `ISetLeverageRest`, `IGetLeverageTiersRest` |
|
||||||
|
| Funding | `IGetFundingInfoRest`, `IGetFundingRateHistoryRest`, `IGetUserFundingHistoryRest` |
|
||||||
|
| Mark/index data | `IGetMarkPriceRest`, `IGetIndexPriceRest` and their all-market/subscription variants |
|
||||||
|
| Open interest | `IGetOpenInterestRest` |
|
||||||
|
|
||||||
|
The `SharedCapabilities` catalog groups these under matching plural categories such as `Balances`, `Positions`, `Funding`, `Leverage`, `MarkPrices`, and `IndexPrices`.
|
||||||
|
|
||||||
|
## Parameter support map
|
||||||
|
|
||||||
|
Every successful runtime resolution includes `Options`:
|
||||||
|
|
||||||
|
| Property | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `RequestParameterRules` | Shared request fields that are required, optional, or unsupported |
|
||||||
|
| `ExchangeParameterRules` | Exchange-specific fields accepted through `ExchangeParameters` |
|
||||||
|
| `SupportedTradingModes` | Modes supported by this capability implementation |
|
||||||
|
| `NeedsAuthentication` | Whether credentials are required |
|
||||||
|
|
||||||
|
Capability presence is not proof that all properties on its request model are accepted. Inspect rules for dynamically generated requests.
|
||||||
|
|
||||||
|
## Common V1 to V2 mappings
|
||||||
|
|
||||||
|
| V1 call | V2 capability call |
|
||||||
|
| --- | --- |
|
||||||
|
| `ISpotTickerRestClient.GetSpotTickerAsync` | `IGetTickerRest.GetTickerAsync` |
|
||||||
|
| `ISpotTickerRestClient.GetSpotTickersAsync` | `IGetAllTickersRest.GetAllTickersAsync` |
|
||||||
|
| `ISpotOrderRestClient.PlaceSpotOrderAsync` | `IPlaceSpotOrderRest.PlaceSpotOrderAsync` |
|
||||||
|
| `ISpotOrderRestClient.CancelSpotOrderAsync` | `ICancelSpotOrderRest.CancelSpotOrderAsync` |
|
||||||
|
| `IFuturesOrderRestClient.PlaceFuturesOrderAsync` | `IPlaceFuturesOrderRest.PlaceFuturesOrderAsync` |
|
||||||
|
| `IFuturesOrderRestClient.CancelFuturesOrderAsync` | `ICancelFuturesOrderRest.CancelFuturesOrderAsync` |
|
||||||
|
| `IFuturesOrderRestClient.GetPositionsAsync` | `IGetPositionsRest.GetPositionsAsync` |
|
||||||
|
|
||||||
|
V1 uses `.SharedClient`; V2 uses `.SharedApi`. Migrate operation by operation.
|
||||||
|
|
||||||
|
## Selection checklist
|
||||||
|
|
||||||
|
1. Use the exchange-native API if portability is unnecessary.
|
||||||
|
2. For portable code, identify the single operation capability required.
|
||||||
|
3. Use the typed `.SharedApi` directly when the surface is known.
|
||||||
|
4. Otherwise resolve through the exchange-wide shared client and handle `null`.
|
||||||
|
5. Specify trading mode and transport when ambiguity matters.
|
||||||
|
6. Inspect parameter rules for dynamic requests.
|
||||||
|
7. Check `Success` before using `Data`.
|
||||||
|
|
||||||
|
For migration edge cases and CryptoClients.Net cross-exchange lookup, see `SHARED_API_V2_MIGRATION.md`.
|
||||||
+323
@@ -0,0 +1,323 @@
|
|||||||
|
# CryptoExchange.Net — full AI context
|
||||||
|
|
||||||
|
Library: CryptoExchange.Net
|
||||||
|
Library version: 13.1.0
|
||||||
|
CryptoExchange.Net package version: 13.1.0
|
||||||
|
Language: C#/.NET
|
||||||
|
Targets: netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
CryptoExchange.Net is the base library used by 28+ exchange-specific clients. It provides shared REST and WebSocket infrastructure, authentication and result conventions, rate limiting, order-book support, and exchange-agnostic Shared APIs.
|
||||||
|
|
||||||
|
CryptoExchange.Net does not provide exchange endpoints by itself. Install the exchange libraries required by the application, such as Binance.Net, Bybit.Net, JK.OKX.Net, Kraken.Net, or Coinbase.Net. Install `CryptoClients.Net` when one package should provide the full exchange bundle.
|
||||||
|
|
||||||
|
Use an exchange's native API for code tied to that exchange. Use Shared API V2 for portable code that performs the same operation against multiple exchanges.
|
||||||
|
|
||||||
|
## Shared API V2 mental model
|
||||||
|
|
||||||
|
Shared API V2 models each operation as a fine-grained capability. A typed exchange API surface exposes implemented capabilities through `.SharedApi`. Examples include:
|
||||||
|
|
||||||
|
- `IGetTickerRest`
|
||||||
|
- `IGetOrderBookRest`
|
||||||
|
- `IGetKlinesRest`
|
||||||
|
- `IPlaceSpotOrderRest`
|
||||||
|
- `ICancelFuturesOrderRest`
|
||||||
|
- `ISubscribeTickerSocket`
|
||||||
|
- `ISubscribeTradesSocket`
|
||||||
|
|
||||||
|
This replaces the V1 assumption that one broad interface represents a complete feature group. An exchange may support order placement without editing, or REST placement without socket placement. Write services against only the capabilities they require.
|
||||||
|
|
||||||
|
V1 aggregate interfaces remain available through `.SharedClient` for incremental migration. Use `.SharedApi` for new code.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install only the exchange packages needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package Binance.Net
|
||||||
|
dotnet add package JK.OKX.Net
|
||||||
|
dotnet add package Bybit.Net
|
||||||
|
```
|
||||||
|
|
||||||
|
Or install the combined package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package CryptoClients.Net
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not add CryptoExchange.Net alone and expect to create an exchange client.
|
||||||
|
|
||||||
|
## Direct typed capability usage
|
||||||
|
|
||||||
|
When the exchange and API surface are known, assign `.SharedApi` to the operation interface. The API surface's compile-time type exposes only the capabilities implemented there.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Binance.Net.Clients;
|
||||||
|
using OKX.Net.Clients;
|
||||||
|
using CryptoExchange.Net.SharedApis;
|
||||||
|
|
||||||
|
IGetTickerRest binance = new BinanceRestClient().SpotApi.SharedApi;
|
||||||
|
IGetTickerRest okx = new OKXRestClient().UnifiedApi.SharedApi;
|
||||||
|
|
||||||
|
var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||||
|
var result = await binance.GetTickerAsync(new GetTickerRequest(symbol));
|
||||||
|
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[{result.Exchange}] {result.Error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"[{result.Exchange}] {result.Data!.LastPrice}");
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `SharedSymbol` rather than exchange-native strings. Each exchange implementation converts base asset, quote asset, and trading mode into its own symbol format.
|
||||||
|
|
||||||
|
Common trading modes include `Spot`, `PerpetualLinear`, and `PerpetualInverse`. Use the mode that corresponds to the intended exchange API surface.
|
||||||
|
|
||||||
|
## Multi-exchange concurrency
|
||||||
|
|
||||||
|
Shared capabilities allow uniform concurrent calls:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var clients = new IGetTickerRest[]
|
||||||
|
{
|
||||||
|
new BinanceRestClient().SpotApi.SharedApi,
|
||||||
|
new OKXRestClient().UnifiedApi.SharedApi,
|
||||||
|
new BybitRestClient().V5Api.SharedApi,
|
||||||
|
};
|
||||||
|
|
||||||
|
var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||||
|
var tasks = clients.Select(client =>
|
||||||
|
client.GetTickerAsync(new GetTickerRequest(symbol)));
|
||||||
|
|
||||||
|
var results = await Task.WhenAll(tasks);
|
||||||
|
foreach (var ticker in results.Where(x => x.Success))
|
||||||
|
Console.WriteLine($"{ticker.Exchange}: {ticker.Data!.LastPrice}");
|
||||||
|
```
|
||||||
|
|
||||||
|
Independent requests should normally run concurrently. Reuse long-lived clients or dependency-injected clients instead of creating clients for each request.
|
||||||
|
|
||||||
|
## Dynamic capability resolution
|
||||||
|
|
||||||
|
Each exchange library registers an exchange-wide `I[Exchange]SharedApiClient`. Use this client when the API surface, trading mode, or transport is selected at runtime.
|
||||||
|
|
||||||
|
`SharedCapabilities` is a catalog of strongly typed references. A reference identifies the interface to find; it does not guarantee support and does not perform an exchange request.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var resolution = sharedClient.GetCapability(
|
||||||
|
SharedCapabilities.Orders.Futures.PlaceOrder.Rest,
|
||||||
|
TradingMode.PerpetualLinear);
|
||||||
|
|
||||||
|
if (resolution is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Futures REST placement is unavailable.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await resolution.Capability.PlaceFuturesOrderAsync(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
`GetCapability` returns one `SharedCapabilityResolution<T>` or `null`. The resolution exposes:
|
||||||
|
|
||||||
|
- `Capability`: the callable typed interface.
|
||||||
|
- `Options`: capability metadata and request rules.
|
||||||
|
- `Exchange`: the exchange name.
|
||||||
|
- `Transport`: REST or socket.
|
||||||
|
|
||||||
|
`GetCapabilities` returns every matching implementation on that exchange-wide client. Pass a `TradingMode` when an exchange can expose multiple matching API surfaces, especially separate linear and inverse futures APIs.
|
||||||
|
|
||||||
|
Use the typed `.SharedApi` property when the desired surface is already known. Use dynamic resolution only when the choice or support is genuinely runtime-dependent.
|
||||||
|
|
||||||
|
## Transport selection
|
||||||
|
|
||||||
|
Some operations have transport-agnostic, REST-specific, and socket-specific variants. Order placement is representative:
|
||||||
|
|
||||||
|
- `IPlaceSpotOrder`: transport-agnostic, returns `IExchangeCallResult<SharedId>`.
|
||||||
|
- `IPlaceSpotOrderRest`: REST-specific, returns `HttpResult<SharedId>`.
|
||||||
|
- `IPlaceSpotOrderSocket`: socket-specific, returns `QueryResult<SharedId>`.
|
||||||
|
|
||||||
|
Subscription interfaces such as `ISubscribeTickerSocket` return `WebSocketResult<UpdateSubscription>`.
|
||||||
|
|
||||||
|
Use a capability reference without a suffix when either transport is acceptable:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var preferred = sharedClient.GetCapability(
|
||||||
|
SharedCapabilities.Orders.Spot.PlaceOrder);
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `.Rest` or `.Socket` when the transport or concrete result type matters:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var rest = sharedClient.GetCapability(
|
||||||
|
SharedCapabilities.Orders.Spot.PlaceOrder.Rest);
|
||||||
|
|
||||||
|
var socket = sharedClient.GetCapability(
|
||||||
|
SharedCapabilities.Orders.Spot.PlaceOrder.Socket);
|
||||||
|
```
|
||||||
|
|
||||||
|
Exchange-wide transport-agnostic lookup follows the configured `SharedApi.PreferredTransport`, which normally defaults to REST. Do not rely on that preference when application semantics require a particular transport.
|
||||||
|
|
||||||
|
## Result handling
|
||||||
|
|
||||||
|
All result types expose `Success`, `Data`, and `Error`. Check `Success` before reading `Data`.
|
||||||
|
|
||||||
|
- REST capability: `HttpResult<T>`
|
||||||
|
- socket command capability: `QueryResult<T>`
|
||||||
|
- socket subscription capability: `WebSocketResult<UpdateSubscription>`
|
||||||
|
- transport-agnostic capability: `IExchangeCallResult<T>`
|
||||||
|
|
||||||
|
Shared results carry the exchange name, which should be included in logs and aggregation records.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
logger.LogWarning(
|
||||||
|
"{Exchange} ticker request failed: {Error}",
|
||||||
|
result.Exchange,
|
||||||
|
result.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Request parameter discovery
|
||||||
|
|
||||||
|
A capability can exist even when an exchange does not accept every property on the shared request. Inspect the selected capability's `Options` before building fully dynamic requests:
|
||||||
|
|
||||||
|
- `RequestParameterRules`: each shared request field is `Required`, `Optional`, or `NotSupported`.
|
||||||
|
- `ExchangeParameterRules`: exchange-specific required or optional parameters supplied through the request's `ExchangeParameters`.
|
||||||
|
- `SupportedTradingModes`: modes supported by this particular implementation.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var placement = sharedClient.GetCapability(
|
||||||
|
SharedCapabilities.Orders.Futures.PlaceOrder.Rest,
|
||||||
|
TradingMode.PerpetualLinear);
|
||||||
|
|
||||||
|
var leverageRule = placement?.Options.RequestParameterRules
|
||||||
|
.FirstOrDefault(rule =>
|
||||||
|
rule.Name == nameof(PlaceFuturesOrderRequest.Leverage));
|
||||||
|
|
||||||
|
if (leverageRule?.Support == RequestParameterSupport.NotSupported)
|
||||||
|
Console.WriteLine("Set leverage with a separate capability.");
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not infer field support from the request model alone. Handle a missing capability, unsupported trading mode, required field, and required exchange parameter before submitting a trading request.
|
||||||
|
|
||||||
|
## Dependency injection
|
||||||
|
|
||||||
|
Each exchange library provides a `services.Add[Exchange](...)` extension. Registration includes native clients, V1 shared facades, V2 operation capabilities, and the exchange-wide shared client.
|
||||||
|
|
||||||
|
Direct capability injection is appropriate when one implementation is intended:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public sealed class TickerService
|
||||||
|
{
|
||||||
|
private readonly IGetTickerRest _ticker;
|
||||||
|
|
||||||
|
public TickerService(IGetTickerRest ticker)
|
||||||
|
=> _ticker = ticker;
|
||||||
|
|
||||||
|
public Task<HttpResult<SharedTicker>> GetAsync(
|
||||||
|
SharedSymbol symbol,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
=> _ticker.GetTickerAsync(new GetTickerRequest(symbol), ct);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If multiple exchanges or multiple API surfaces register the same capability interface, inject the exchange-specific shared client and select a typed API property or use `GetCapability`. Resolving one unqualified capability does not express which registered implementation is desired.
|
||||||
|
|
||||||
|
`CryptoClients.Net` registers `IExchangeSharedApiClient` for V2 lookup across exchanges. Its `GetCapabilities` returns one preferred matching implementation per exchange, while `GetImplementations` returns every matching transport and API surface.
|
||||||
|
|
||||||
|
## Common capability families
|
||||||
|
|
||||||
|
Frequently used market-data capabilities:
|
||||||
|
|
||||||
|
- Tickers: `IGetTickerRest`, `IGetAllTickersRest`, `ISubscribeTickerSocket`, `ISubscribeAllTickersSocket`, `ISubscribeBookTickerSocket`.
|
||||||
|
- Order books: `IGetOrderBookRest`, `IGetBookTickerRest`, `ISubscribeOrderBookSocket`, `ISubscribeIncrementalOrderBookSocket`.
|
||||||
|
- Klines and trades: `IGetKlinesRest`, `ISubscribeKlinesSocket`, `IGetRecentTradesRest`, `ISubscribeTradesSocket`.
|
||||||
|
- Symbols: `IGetSpotSymbolsRest`, `IGetFuturesSymbolsRest`.
|
||||||
|
|
||||||
|
Frequently used trading and account capabilities:
|
||||||
|
|
||||||
|
- Spot and futures order placement, editing, cancellation, retrieval, open/closed orders, and order-update subscriptions.
|
||||||
|
- Balances, positions, user trades, fees, deposits, withdrawals, and transfers.
|
||||||
|
- Funding, open interest, leverage, mark price, and index price operations for derivatives.
|
||||||
|
|
||||||
|
Use `docs/ai-api-map.md` or the typed API surface for names. Do not assume every exchange implements every family.
|
||||||
|
|
||||||
|
## V2 ticker behavior
|
||||||
|
|
||||||
|
V2 uses `GetTickerAsync` and `GetAllTickersAsync` for both spot and futures and returns `SharedTicker`. Futures-specific mark price, index price, and funding data have separate capabilities rather than being assumed to exist on the common ticker.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
IGetTickerRest ticker = restClient.SpotApi.SharedApi;
|
||||||
|
var result = await ticker.GetTickerAsync(
|
||||||
|
new GetTickerRequest(
|
||||||
|
new SharedSymbol(TradingMode.Spot, "ETH", "USDT")));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Order and subscription behavior
|
||||||
|
|
||||||
|
V2 exposes REST and socket order commands under the same operation capability when both are supported. Choose the transport-specific interface when the caller needs `HttpResult<T>` or `QueryResult<T>`.
|
||||||
|
|
||||||
|
Order-update subscriptions use `SharedSpotOrderUpdate` and `SharedFuturesOrderUpdate`. REST order retrieval returns `SharedSpotOrder` or `SharedFuturesOrder`. Keep update-only stream semantics out of ordinary order snapshots.
|
||||||
|
|
||||||
|
`ICloseFullPosition` closes the entire position and has no quantity parameter. It is not a replacement for partial close logic; use a suitable order capability when an exchange supports partial reduction.
|
||||||
|
|
||||||
|
## V1 migration essentials
|
||||||
|
|
||||||
|
V1 remains available through `.SharedClient`; V2 is exposed through `.SharedApi`. Both are views over the exchange API, so migration can happen operation by operation.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||||
|
var request = new GetTickerRequest(symbol);
|
||||||
|
|
||||||
|
// V1 compatibility facade
|
||||||
|
var oldResult = await restClient.SpotApi.SharedClient
|
||||||
|
.GetSpotTickerAsync(request);
|
||||||
|
|
||||||
|
// V2 capability
|
||||||
|
var newResult = await restClient.SpotApi.SharedApi
|
||||||
|
.GetTickerAsync(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
Representative mappings:
|
||||||
|
|
||||||
|
- `ISpotTickerRestClient.GetSpotTickerAsync` -> `IGetTickerRest.GetTickerAsync`
|
||||||
|
- `ISpotOrderRestClient.PlaceSpotOrderAsync` -> `IPlaceSpotOrderRest.PlaceSpotOrderAsync`
|
||||||
|
- `ISpotOrderRestClient.CancelSpotOrderAsync` -> `ICancelSpotOrderRest.CancelSpotOrderAsync`
|
||||||
|
- `IFuturesOrderRestClient.PlaceFuturesOrderAsync` -> `IPlaceFuturesOrderRest.PlaceFuturesOrderAsync`
|
||||||
|
- `IFuturesOrderRestClient.GetPositionsAsync` -> `IGetPositionsRest.GetPositionsAsync`
|
||||||
|
|
||||||
|
Transport-agnostic V2 methods return `IExchangeCallResult<T>`. Select `...Rest` or `...Socket` if old code depends on a concrete transport result. See `docs/SHARED_API_V2_MIGRATION.md` for detailed migration examples and CryptoClients.Net behavior.
|
||||||
|
|
||||||
|
## Code-generation rules
|
||||||
|
|
||||||
|
- Use `.SharedApi` and fine-grained V2 interfaces for new portable code.
|
||||||
|
- Use `.SharedClient` only when maintaining or incrementally migrating V1 code.
|
||||||
|
- Use `SharedSymbol` instead of native symbol strings.
|
||||||
|
- Resolve capability support rather than assuming it.
|
||||||
|
- Inspect capability options when requests are generated dynamically.
|
||||||
|
- Select REST or socket explicitly when transport semantics matter.
|
||||||
|
- Check `Success` before accessing `Data`.
|
||||||
|
- Reuse clients through DI and keep calls asynchronous.
|
||||||
|
- Run independent exchange calls concurrently.
|
||||||
|
- Keep exchange-native models out of cross-exchange service contracts.
|
||||||
|
|
||||||
|
## Implementing an exchange library
|
||||||
|
|
||||||
|
Exchange implementations derive from the shared REST and socket client bases, provide their authentication and option types, and expose typed Shared API classes. Implement the fine-grained capability interfaces actually supported by each API surface and publish accurate `CapabilityOptions`, including trading modes and request/exchange parameter rules. Register the Shared APIs and exchange-wide shared client in the exchange library's DI extension.
|
||||||
|
|
||||||
|
Use established exchange libraries as references. Do not advertise a capability merely because a similar native endpoint exists; its shared request and result semantics must be implemented correctly.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Source: https://github.com/JKorf/CryptoExchange.Net
|
||||||
|
- Documentation: https://cryptoexchange.jkorf.dev/
|
||||||
|
- Shared API V2 migration: https://github.com/JKorf/CryptoExchange.Net/blob/master/docs/SHARED_API_V2_MIGRATION.md
|
||||||
|
- AI API map: https://github.com/JKorf/CryptoExchange.Net/blob/master/docs/ai-api-map.md
|
||||||
|
- CryptoClients.Net: https://github.com/JKorf/CryptoClients.Net
|
||||||
|
- CryptoManager.Net: https://github.com/JKorf/CryptoManager.Net
|
||||||
|
- NuGet: https://www.nuget.org/packages/CryptoExchange.Net
|
||||||
|
- Discord: https://discord.gg/MSpeEtSY8t
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user