Unfortunately there is no standard for exchanges when it comes to the API's they offer. Altough most of them offer roughly the same functionality each of them is structured differently and expects+ differently named and formatted parameters.
// Spot API common functionality rest client
var spotSharedRestClients = binanceRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = binanceSocketClient.SpotApi.SharedClient;
// USD-M Futures API common functionality rest client
var usdFuturesSharedRestClient = binanceRestClient.UsdFuturesApi.SharedClient;
// USD-M Futures API common functionality socket client
var usdFuturesSharedSocketClient = binanceSocketClient.UsdFuturesApi.SharedClient;
// Coin-M Futures API common functionality rest client
var coinFuturesSharedRestClient = binanceRestClient.CoinFuturesApi.SharedClient;
// Coin-M Futures API common functionality socket client
var coinFuturesSharedSocketClient = binanceSocketClient.CoinFuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = bingXRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = bingXSocketClient.SpotApi.SharedClient;
// Perpetual Futures API common functionality rest client
var usdFuturesSharedRestClient = bingXRestClient.PerpetualFuturesApi.SharedClient;
// Perpetual Futures API common functionality socket client
var usdFuturesSharedSocketClient = bingXSocketClient.PerpetualFuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = bitfinexRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = bitfinexSocketClient.SpotApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = bitgetRestClient.SpotApiV2.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = bitgetSocketClient.SpotApiV2.SharedClient;
// Perpetual Futures API common functionality rest client
var usdFuturesSharedRestClient = bitgetRestClient.FuturesApiV2.SharedClient;
// Perpetual Futures API common functionality socket client
var usdFuturesSharedSocketClient = bitgetSocketClient.FuturesApiV2.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = bitMartRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = bitMartSocketClient.SpotApi.SharedClient;
// USD Futures API common functionality rest client
var usdFuturesSharedRestClient = bitMartRestClient.UsdFuturesApi.SharedClient;
// USD Futures API common functionality socket client
var usdFuturesSharedSocketClient = bitMartSocketClient.UsdFuturesApi.SharedClient;
// Spot and Futures API common functionality rest client
var spotSharedRestClients = bybitRestClient.V5Api.SharedClient;
// Spot and Futures API common functionality socket client
var spotSharedSocketClient = bybitSocketClient.V5SpotApi.SharedClient;
// Linear Futures API common functionality socket client
var linearFuturesSharedSocketClient = bybitSocketClient.V5LinearApi.SharedClient;
// Inverse Futures API common functionality socket client
var inverseFuturesspotSharedSocketClient = bybitSocketClient.V5InverseApi.SharedClient;
// Private Futures API common functionality socket client
var privateFuturesSharedSocketClient = bybitSocketClient.V5PrivateApi.SharedClient;
// Advanced Trade API common functionality rest client
var spotSharedRestClients = coinbaseRestClient.AdvancedTradeApi.SharedClient;
// Advanced Trade API common functionality socket client
var spotSharedSocketClient = coinbaseSocketClient.AdvancedTradeApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = coinExRestClient.SpotApiV2.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = coinExSocketClient.SpotApiV2.SharedClient;
// Futures API common functionality rest client
var usdFuturesSharedRestClient = coinExRestClient.FuturesApi.SharedClient;
// Futures API common functionality socket client
var usdFuturesSharedSocketClient = coinExSocketClient.FuturesApi.SharedClient;
// Spot/Futures API common functionality rest client
var sharedRestClients = cryptoComRestClient.ExchangeApi.SharedClient;
// Spot/Futures API common functionality socket client
var sharedSocketClient = cryptoComSocketClient.ExchangeApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = gateioRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = gateioSocketClient.SpotApi.SharedClient;
// Perpetual Futures API common functionality rest client
var perpFuturesSharedRestClient = gateioRestClient.PerpetualFuturesApi.SharedClient;
// Perpetual Futures API common functionality socket client
var perpFuturesSharedSocketClient = gateioSocketClient.PerpetualFuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = htxRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = htxSocketClient.SpotApi.SharedClient;
// USDT Futures API common functionality rest client
var usdFuturesSharedRestClient = htxRestClient.UsdtFuturesApi.SharedClient;
// USDT Futures API common functionality socket client
var usdFuturesSharedSocketClient = htxSocketClient.UsdtFuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = hyperliquidRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = hyperliquidSocketClient.SpotApi.SharedClient;
// Perpetual Futures API common functionality rest client
var futuresSharedRestClient = hyperliquidRestClient.FuturesApi.SharedClient;
// Perpetual Futures API common functionality socket client
var futuresSharedSocketClient = hyperliquidSocketClient.FuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = krakenRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = krakenSocketClient.SpotApi.SharedClient;
// Futures API common functionality rest client
var usdFuturesSharedRestClient = krakenRestClient.FuturesApi.SharedClient;
// Futures API common functionality socket client
var usdFuturesSharedSocketClient = krakenSocketClient.FuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = kucoinRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = kucoinSocketClient.SpotApi.SharedClient;
// Futures API common functionality rest client
var usdFuturesSharedRestClient = kucoinRestClient.FuturesApi.SharedClient;
// Futures API common functionality socket client
var usdFuturesSharedSocketClient = kucoinSocketClient.FuturesApi.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = mexcRestClient.SpotApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = mexcSocketClient.SpotApi.SharedClient;
// Futures and Spot API common functionality rest client
var spotSharedRestClients = okxRestClient.UnifiedApi.SharedClient;
// Futures and Spot API common functionality socket client
var spotSharedSocketClient = okxSocketClient.UnifiedApi.SharedClient;
// Futures and Spot API common functionality rest client
var spotSharedRestClients = whitebitRestClient.V4Api.SharedClient;
// Futures and Spot API common functionality socket client
var spotSharedSocketClient = whitebitSocketClient.V4Api.SharedClient;
// Spot API common functionality rest client
var spotSharedRestClients = xtRestClient.SpotApi.SharedClient;
// Futures API common functionality rest client
var futuresSharedRestClients = xtRestClient.UsdtFuturesApi.SharedClient;
// Spot API common functionality socket client
var spotSharedSocketClient = xtSocketClient.SpotApi.SharedClient;
// Futures API common functionality socket client
var futuresSharedSocketClient = xtSocketClient.FuturesApi.SharedClient;
TradingMode
The shared client interfaces and functionality work with the TradingMode
enum. This enums determines what part of the API a request targets. Many exchanges support multiple trading modes, which might be split over different sub-clients:
Console.WriteLine(string.Join(", ", binanceClient.UsdFuturesApi.SharedClient.SupportedTradingModes));
// Output: DeliveryLinear, PerpetualLinear
Console.WriteLine(string.Join(", ", binanceClient.CoinFuturesApi.SharedClient.SupportedTradingModes));
// Output: DeliveryInverse, PerpetualInverse
Console.WriteLine(string.Join(", ", binanceClient.SpotApi.SharedClient.SupportedTradingModes));
// Output: Spot
Mode | Description |
TradingMode.Spot | Spot trading |
TradingMode.PerpetualLinear | Futures contract trading without delivery date using stablecoin as settlement |
TradingMode.DeliveryLinear | Futures contract trading with specific delivery date using stablecoin as settlement |
TradingMode.PerpetualInverse | Futures contract trading without delivery date using a crypto asset as settlement |
TradingMode.DeliveryInverse | Futures contract trading with specific delivery date using a crypto asset as settlement |
SharedSymbol
One of the main challenges of working with different exchanges is the different names and formats used for symbols. For example, the ETH/USDT spot trading pair is called ETHUSDT
on Binance, ETH-USDT
on Kucoin and ETH_USDT
on GateIo. With futures trading it's even worse, ranging from ETH-USD-SWAP
to ETHU24
To combat this problem the SharedSymbol
class was introduced. This class takes a TradingMode
, base asset name and quote asset name which are used to format the symbol according to the exchange it will be used by.
// Create a symbol reference for ETH/USDT spot trading
var symbol1 = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a symbol reference for BTC/USDC perpetual linear futures trading
var symbol2 = new SharedSymbol(TradingMode.PerpetualLinear, "BTC", "USDC");
// Create a symbol reference for BTC/USD perpetual inverse futures trading
var symbol3 = new SharedSymbol(TradingMode.PerpetualInverse, "BTC", "USD");
// Create a symbol reference for BTC/USDT linear futures trading with delivery on 27 Sept 2024
var symbol4 = new SharedSymbol(TradingMode.DeliveryLinear, "BTC", "USDT", new DateTime(2024, 9, 27));
// Create a symbol reference for ETH/USD inverse futures trading with delivery on 27 Sept 2024
var symbol5 = new SharedSymbol(TradingMode.DeliveryLinear, "ETH", "USD", new DateTime(2024, 9, 27));
With the SharedSymbol
functionality available we can reference symbols without having to think about how the exchange expects a symbol.
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
var tickerMexcResult = await mexcSharedRestClients.GetSpotTickerAsync(new GetTickerRequest(symbol));
Console.WriteLine($"{mexcSharedRestClients.Exchange} {tickerMexcResult.Data.Symbol} last price: {tickerMexcResult.Data.LastPrice}");
// Output: Mexc ETHUSDT last price: 2614,67
var tickerKucoinResult = await kucoinSharedRestClients.GetSpotTickerAsync(new GetTickerRequest(symbol));
Console.WriteLine($"{kucoinSharedRestClients.Exchange} {tickerKucoinResult.Data.Symbol} last price: {tickerKucoinResult.Data.LastPrice}");
// Output: Kucoin ETH-USDT last price: 2614,49
Some Rest history requests support pagination. These requests take a INextPageToken
as parameter and results include a NextPageToken
property on the result. These tokens can be used to automatically execute multiple paginated requests
var symbol = new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT");
INextPageToken? nextToken = null;
while (true)
{
// Execute a request specifying the next page token which was returned from by the previous request (or null for the first request)
var pageResult = await client.GetClosedFuturesOrdersAsync(new GetClosedOrdersRequest(symbol, limit: 2), nextToken);
Console.WriteLine($"{pageResult.Data.Count()} items");
// If NextPagToken is null there is no next page available
if (pageResult.NextPageToken == null)
break;
// Store next page token for the next request
nextToken = pageResult.NextPageToken;
}
Or simplify this by using the ExchangeHelpers.ExecutePages
helper function to stream each page as a result in the form of a IAsyncEnumerable
await foreach (var pageResult in ExchangeHelpers.ExecutePages(client.GetClosedFuturesOrdersAsync, new GetClosedOrdersRequest(symbol, limit: 2)))
Console.WriteLine($"{pageResult.Data.Count()} items");
Optional and exchange specific parameters
Not all exchanges require the same parameters. Some parameters are defined on the request model, but only required for specific exchanges. For example a listenKey
parameter when subscribing to a user stream. When not provided for an exchange that needs it an ArgumentError
will be returned.
Other parameters are not defined on the request model at all because they're very specific to a single exchange. For example the AccountId
parameter for Spot authenticated requests on HTX. To not pollute the request models with every single possible parameter for each single exchange there is an ExchangeParameters
structure to provide these values.
// Set as static parameter, automatically used if not overridden
ExchangeParameters.SetStaticParameter("HTX", "AccountId", 123123123);
var balances1 = await restClient.HTX.SpotApi.SharedClient.GetBalancesAsync(new GetBalancesRequest());
// Specify exchange parameters for a request
var exchangeParameters = new ExchangeParameters(new ExchangeParameter("HTX", "AccountId", 456456456));
var balances = await restClient.HTX.SpotApi.SharedClient.GetBalancesAsync(new GetBalancesRequest(exchangeParameters: exchangeParameters));
To determine which parameters are required for an exchange request either inspect the ArgmumentError when the call fails, or output the request info:
Console.WriteLine(restClient.HTX.SpotApi.SharedClient.GetBalancesOptions.ToString(Exchange.HTX));
// Output:
// HTX GetBalancesRequest
// Needs authentication: True
// Required exchange specific parameters: [Int64] AccountId: Account id of the user | example: 123123123
Available Interfaces
What interfaces are actually available and implemented on each exchange clients depends on what functionality is available in the specific API.
Available REST shared interfaces
Interface | Description |
IAssetsRestClient | For requesting withdrawal and deposit info for assets |
IKlineRestClient | For requesting public kline/candlestick data |
IOrderBookRestClient | For requesting public order book data |
IRecentTradeRestClient | For requesting the most recent public trades for a symbol |
ITradeHistoryRestClient | For requesting historic public trade data for a symbol |
ISpotSymbolRestClient | For requesting Spot symbols info |
ISpotTickerRestClient | For requesting Spot ticker information |
IFundingRateRestClient | For requesting funding rate history for a Futures symbol |
IFuturesSymbolRestClient | For requesting Futures symbols info |
IFuturesTickerRestClient | For requesting Futures ticker information |
IIndexPriceKlineRestClient | For requesting index price kline history for a Futures symbol |
IMarkPriceKlineRestClient | For requesting mark price kline history for a Futures symbol |
IOpenInterestRestClient | For requesting the open interest for a Futures symbol |
IBalanceRestClient | For requesting user asset balances |
IDepositRestClient | For requesting user deposit history |
IListenKeyRestClient | For managing the user listen key which can be used for subscribing to user data streams |
IWithdrawalRestClient | For requesting user withdrawal history |
IWithdrawRestClient | For requesting to withdraw funds from the exchange |
ISpotOrderRestClient | For placing and managing Spot orders |
IFuturesOrderRestClient | For placing and managing Futures orders |
ILeverageRestClient | For managing leverage for a Futures symbol |
IPositionHistoryRestClient | For requesting the user position closing history |
IPositionModeRestClient | For managing the position mode for the user |
IFeeRestClient | For requesting maker and taker trading fee percentages for the user |
Available Socket shared interfaces
Interface | Description |
IBookTickerSocketClient | For subscribing to book ticker (best bid/ask) updates for a symbol |
IKlineSocketClient | For subscribing to kline/candlestick updates for a symbol |
IOrderBookSocketClient | For subscribing to order book snapshot updates for a symbol |
ITickerSocketClient | For subscribing to ticker updates for a symbol |
ITickersSocketClient | For subscribing to ticker updates for all symbols |
ITradeSocketClient | For subscribing to public trade updates for a symbol |
IBalanceSocketClient | For subscribing to user balance updates |
IUserTradeSocketClient | For subscribing to user trade updates |
ISpotOrderSocketClient | For subscribing to user Spot order updates |
IFuturesOrderSocketClient | For subscribing to user Futures order updates |
IPositionSocketClient | For subscribing to user position updates |
CryptoClients.Net
The CryptoClients.Net
library has some additional tools for dynamically accessing different exchanges. Both the (I)ExchangeRestClient
and (I)ExchangeSocketClient
expose methods for executing requests and subscriptions on dynamically selected exchanges.
Rest
var restClient = new ExchangeRestClient();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Get ticker results from Binance and Kucoin
var tickers = await restClient.GetSpotTickerAsync(new GetTickerRequest(symbol), [Exchange.Binance, Exchange.Kucoin]);
// Get ticker results for all exchanges
var tickers = await restClient.GetSpotTickerAsync(new GetTickerRequest(symbol));
Socket
var socketClient = new ExchangeSocketClient();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Subscribe to ticker updates on Binance and Kucoin
var tickers = await socketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequest(symbol), update =>
{
Console.WriteLine($"{update.Exchange} update: {update.Data.LastPrice}");
},
[Exchange.Binance, Exchange.Kucoin]);
// Subscribe to ticker updates on all exchanges
var tickers = await socketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequest(symbol), update =>
{
Console.WriteLine($"{update.Exchange} update: {update.Data.LastPrice}");
});
Options & Authorization
Options for the clients can be provided in a couple of different ways. If no options are configured the default options will be used. For accessing private endpoints and streams API credentials have to be provided.
Authorization
For private endpoints and data streams the clients will need to know the API credentials of the user accessing the API. API credentials are a way of identifying the user and validating that the user is who he says he is. You can compare it to a username and password login.
API credentials van be provided via the client options, see next section on how to set these options. There are currently 2 variants of API credentials supported, HMAC and RSA.
HMAC
HMAC authentication involves 2 values, the API key and API secret. The combination of the two gives access to the account. HMAC is the default authentication method and can be configured as such:
options.ApiCredentials = new ApiCredentials("YOUR API KEY", "YOUR API SECRET");
RSA
RSA authentication involves generating a private and public key and then uploading the public key to the server. After using the private key to sign the request the server can validate the request by comparing the signature using the public key. Not every exchange supports this authentication method.
Depending on the version of dotnet used there are 2 ways of configuring the RSA authentication.
When running Dotnet version 3.0 or later the easiest way is to use the RsaPem type. This allows you to use the Private key directly. When running from an older Dotnet/.NET framework version you're forced to use the RsaXml type due to framework limitations. This means you'll have to convert the private key to XML format before using it.
RsaXml
// when using the .netstandard2.0 compiled version, from .NET framework or Dotnet core 2.2 or lower
// Private key should look something like this: <RSAKeyValue><Modulus>...</RSAKeyValue>
options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY", ApiCredentialsType.RsaXml);
RsaPem
// when using the .netstandard2.1 compiled version, from Dotnet core 3.0 or later
// Private key should look something like this: -----BEGIN PRIVATE KEY----- .. -----END PRIVATE KEY-----, or just a long random character string
options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY", ApiCredentialsType.RsaPem);
Setting options
Dependency injection
When adding a library to the service collection (see Dependency Injection) the options for the clients can be provided as argument to the calls or read from configuration.
builder.Services.AddCryptoClients(globalOptions => {
globalOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
// Exchange specific options can be provided as well
bybitRestOptions: bybitOptions => {
// Set options specific for the Bybit rest client here
});
builder.Services.AddBinance(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddBinance(builder.Configuration.GetSection("Binance"));
builder.Services.AddBingX(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddBingX(builder.Configuration.GetSection("BingX"));
builder.Services.AddBitfinex(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddBitfinex(builder.Configuration.GetSection("Bitfinex"));
builder.Services.AddBitget(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddBitget(builder.Configuration.GetSection("Bitget"));
builder.Services.AddBitMart(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddBitMart(builder.Configuration.GetSection("BitMart"));
builder.Services.AddBybit(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddBybit(builder.Configuration.GetSection("Bybit"));
builder.Services.AddCoinbase(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddCoinbase(builder.Configuration.GetSection("Coinbase"));
builder.Services.AddCoinGecko(
options => {
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddCoinGecko(builder.Configuration.GetSection("CoinGecko"));
builder.Services.AddCoinEx(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddCoinEx(builder.Configuration.GetSection("CoinEx"));
builder.Services.AddCryptoCom(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddCryptoCom(builder.Configuration.GetSection("CryptoCom"));
builder.Services.AddGateIo(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddGateIo(builder.Configuration.GetSection("GateIo"));
builder.Services.AddHTX(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddHTX(builder.Configuration.GetSection("HTX"));
builder.Services.AddHyperLiquid(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddHyperLiquid(builder.Configuration.GetSection("HyperLiquid"));
builder.Services.AddKraken(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddKraken(builder.Configuration.GetSection("Kraken"));
builder.Services.AddKucoin(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddKucoin(builder.Configuration.GetSection("Kucoin"));
builder.Services.AddMexc(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddMexc(builder.Configuration.GetSection("Mexc"));
builder.Services.AddOKX(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddOKX(builder.Configuration.GetSection("OKX"));
builder.Services.AddWhiteBit(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));
builder.Services.AddXT(
options => {
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
});
// OR
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
builder.Services.AddXT(builder.Configuration.GetSection("XT"));
Client constructor
When creating a client via the constructor options can be provided as parameters
var exchangeRestClient = new ExchangeRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var binanceRestClient = new BinanceRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BingXRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BitfinexRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BitgetRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BitMartRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BybitRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CoinbaseRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CoinGeckoRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CoinExRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CryptoComRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new GateIoRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new HTXRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new HyperLiquidRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new KrakenRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new KucoinRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new MexcRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new OKXRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new WhiteBitRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new XTRestClient(opts =>
{
opts.RequestTimeout = TimeSpan.FromSeconds(30);
});
SetDefaultOptions
The options can be defined using the static SetDefaultOptions method on the client BEFORE creating the client. Any client created after this call will use the specified options
BinanceRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BinanceRestClient();
BingXRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BingXRestClient();
BitfinexRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BitfinexRestClient();
BitgetRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BitgetRestClient();
BitMartRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BitMartRestClient();
BybitRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new BybitRestClient();
CoinbaseClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CoinbaseRestClient();
CoinGeckoRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CoinGeckoRestClient();
CoinExRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CoinExRestClient();
CryptoComRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new CryptoComRestClient();
GateIoRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new GateIoRestClient();
HTXRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new HTXRestClient();
HyperLiquidRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new HyperLiquidRestClient();
KrakenRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new KrakenRestClient();
KucoinRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new KucoinRestClient();
MexcRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new MexcRestClient();
OKXRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new OKXRestClient();
WhiteBitRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new WhiteBitRestClient();
XTRestClient.SetDefaultOptions(options =>
{
options.RequestTimeout = TimeSpan.FromSeconds(30);
});
var client = new XTRestClient();
Option definitions
General options
Options available for all clients
Option | Description | Default value |
RequestTimeout |
The time to wait for an answer from the server on a request |
TimeSpan.FromSeconds(20) |
ApiCredentials |
The credentials to use for private endpoints and streams. See Authorization for more info. For CryptoClients this option allows the setting of ApiCredentials for all exchanges. |
null |
Proxy |
The proxy to use for connecting to the API |
null |
OutputOriginalData |
When enabled the originally received string data will be available as well as the deserialized object. For REST API client calls the data will be in the WebCallResult.OriginalData property, for Websocket API client subscriptions the data will be available in the DataEvent.OriginalData property when receiving an update. |
false |
RateLimiterEnabled |
Whether or not client side rate limiting should be applied. Note that not all libraries have ratelimiting implemented, if it's not implemented this flag does nothing |
true |
RateLimitingBehaviour |
What should happen when a rate limit is reached. RateLimitingBehaviour.Wait: the request waits until it can be send while staying within the limits, RateLimitingBehaviour.Fail: the request will return an error |
RateLimitingBehaviour.Wait |
Environment |
The environment the library should connect to. Some exchanges have testnet/sandbox environments which can be used instead of the real exchange. The environment option can be used to switch between different trade environments |
Live environment |
REST client options
Options available for REST clients
Option | Description | Default value |
AutoTimestamp |
Whether or not the client should attempt to sync the time between the client and server. If the time between server and client is not in sync authentication errors might occur. This option should be disabled when the client time sure is to be in sync |
true |
TimestampRecalculationInterval |
The interval of how often the time synchronization between client and server should be executed |
TimeSpan.FromHours(1) |
CachingEnabled |
Whether or not client side caching should be enabled for GET requests, see Caching |
false |
CachingMaxAge |
The max age of data to return from the cache. If the same data is requested and the data is available in the client side cache and not older than this value the cached value is returned, else a new request will be done |
TimeSpan.FromSeconds(5) |
[API].ApiCredentials |
Same as the in the base options, allows overriding per sub-API |
null |
[API].OutputOriginalData |
Same as the in the base options, allows overriding per sub-API |
null |
[API].AutoTimestamp |
Same as the in the base REST options, allows overriding per sub-API |
null |
[API].TimestampRecalculationInterval |
Same as the in the base REST options, allows overriding per sub-API |
null |
Websocket client options
Options available for websocket clients
Option | Description | Default value |
ReconnectPolicy |
The reconnect policy to execute when the connection is lost |
ReconnectPolicy.FixedDelay |
ReconnectInterval |
The time to wait between connection tries when reconnecting, only applied when reconnect policy is FixedDelay |
TimeSpan.FromSeconds(5) |
SocketNoDataTimeout |
If no data is received during this timespan the connection is assumed to be dropped. This is mainly used for API's which have some sort of ping/keepalive system. For example; the Bitfinex API will sent a heartbeat message every 15 seconds, so the `SocketNoDataTimeout` could be set to 20 seconds. On API's without such a mechanism this might not work because there just might not be any update while still being fully connected |
default(TimeSpan) |
SocketSubscriptionsCombineTarget |
The number of subscriptions that should be made on a single socket connection before setting up a new connection. Not all exchanges support multiple subscriptions on a single socket and some have limits on the amount of connections. Setting this to a higher number increases subscription speed because not every subscription needs to connect to the server, but having more subscriptions on a single connection will also increase the amount of traffic on that single connection, potentially leading to delays in updates if the data isn't handled quickly enough |
Dependent on the library |
MaxConcurrentResubscriptionsPerSocket |
The maximum number of concurrent resubscriptions per socket when resubscribing after reconnecting |
5 |
MaxSocketConnections |
The maximum number of distinct socket connections |
null |
DelayAfterConnect |
The time to wait before sending messages after connecting to the server |
null |
[API].SocketNoDataTimeout |
Same as the in the base websocket client options, allows overriding per sub-API |
null |
[API].MaxSocketConnections |
Same as the in the base websocket client options, allows overriding per sub-API |
null |
Orderbooks
Each client library provides an local orderbook implementation. These implementations will provide a client side orderbook, take care of synchronization with the server, and handle reconnecting and resynchronizing in case of a dropped connection.
Orderbook implementations use the following naming convention: [ExchangeName][(Type)]SymbolOrderBook
Creation and starting
The order book implementations can be created directly, or can be instantiated via the I[Exchange]OrderBookFactory
factory. After creation the synchronization can be started by calling the StartAync
method
// Assuming IExchangeOrderBookFactory is injected as bookFactoryClient
var book = bookFactoryClient.Binance.Spot.Create("ETH", "USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new BinanceSpotSymbolOrderBook("ETHUSDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new BingXSpotSymbolOrderBook("ETH-USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new BitfinexSymbolOrderBook("tETHUSD");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new BitgetSpotSymbolOrderBook("ETHUSDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new BitMartSymbolOrderBook("ETH_USDT", Category.Spot);
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new BybitSymbolOrderBook("ETHUSDT", Category.Spot);
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new CoinbaseSymbolOrderBook("ETH-USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new CoinExSpotSymbolOrderBook("ETHUSDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new GateIoSpotSymbolOrderBook("ETH_USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new CryptoComSpotSymbolOrderBook("ETH_USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new HTXSpotSymbolOrderBook("ethusdt");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new HyperLiquidSymbolOrderBook("HYPE/USDC");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new KrakenSpotSymbolOrderBook("ETH/USD");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new KucoinSpotSymbolOrderBook("ETH-USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new MexcSpotSymbolOrderBook("ETHUSDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new OKXSymbolOrderBook("ETH-USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new WhiteBitSymbolOrderBook("ETH_USDT");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
var book = new XTSymbolOrderBook("eth_usdt");
var startResult = await book.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Book has successfully started and synchronized
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await book.StopAsync();
The order book object
The order book implementations for each client use a common base class and interface, which means you can use books from different exchanges in a common function.
// Create order books for different exchanges
var books = new List<ISymbolOrderBook>();
books.Add(new BinanceSpotSymbolOrderBook("ETHUSDT"));
books.Add(new BitgetSpotSymbolOrderBook("ETHUSDT"));
books.Add(new KrakenSpotSymbolOrderBook("ETH/USD"));
books.Add(new KucoinSpotSymbolOrderBook("ETH-USDT"));
books.Add(new OKXSymbolOrderBook("ETH-USDT"));
// Start the books
var results = await Task.WhenAll(books.Select(b => b.StartAsync()));
// Output the current best ask/bid for each exchange
foreach (var book in books.Where(b => b.Status == OrderBookStatus.Synced))
{
Console.WriteLine(book.Id);
Console.WriteLine($"{book.BestAsk} - {book.BestBid}");
}
The following properties and events are exposed by the order books:
Field | Description |
Status |
The current status of the order book. Note that the book is only acurate and up to date when the status is Synced |
Id |
Identifier for the book, referencing the exchange it's for |
Symbol |
The symbol the book is for |
LastSequenceNumber |
The last sequence number that was processed. Order book update messages typically have sequence numbers to correctly sync the book |
UpdateTime |
Timestamp of the last update that was processed |
AskCount |
The current number of asks in the book |
BidCount |
The current number of bids in the book |
Asks |
Returns a snapshot of the current list of asks. This creates a copy of the list values at that moment |
Bids |
Returns a snapshot of the current list of bids. This creates a copy of the list values at that moment |
Book |
Returns a snapshot of the both the asks and bids. This makes sure the snapshot of the bids and asks are of the same exact time |
BestAsk |
The best ask at that moment |
BestBid |
The best bid at that moment |
BestOffers |
The best bid and best ask at that moment |
OnStatusChange |
Event called when the status of the order book changes |
OnOrderBookUpdate |
Event called whenever the book changes. Note that this event can trigger a lot on large/high liquidity markets |
OnBestOffersChanged |
Event called whenever the best ask or bid changes in the order book |
Trackers
Trackers offer a way to keep track of live data. This data can than be aggregated into statistics and different time slices can be compared to get realtime insights.
The basic workings of the trackers are simple, an initial request is made for a snapshot of the history (or a partial snapshot depending on what the API supports). At the same time a websocket subscription is set up to provide the tracker with new data.
Currently there are 2 different trackers available, the TradeTracker
and the KlineTracker
.
Creation and starting
The example uses the TradeTracker
, but the same logic can be applied to the KlineTracker
.
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Assuming IExchangeTrackerFactory is injected as trackerFactory
// Create tracker dynamically by exchange name
var tracker = trackerFactory.CreateTradeTracker("Binance", symbol);
// OR by directly referencing the specific exchange
tracker = trackerFactory.Binance.CreateTradeTracker(symbol);
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IBinanceTrackerFactory interface
var factory = new BinanceTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IBingXTrackerFactory interface
var factory = new BingXTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IBitfinexTrackerFactory interface
var factory = new BitfinexTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IBitgetTrackerFactory interface
var factory = new BitgetTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IBitMartTrackerFactory interface
var factory = new BitMartTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IBybitTrackerFactory interface
var factory = new BybitTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the ICoinbaseTrackerFactory interface
var factory = new CoinbaseTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the ICoinExTrackerFactory interface
var factory = new CoinExTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IGateIoTrackerFactory interface
var factory = new GateIoTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the ICryptoComTrackerFactory interface
var factory = new CryptoComTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IHTXTrackerFactory interface
var factory = new HTXTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IHyperLiquidTrackerFactory interface
var factory = new HyperLiquidTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "HYPE", "USDC");
// Create a tracker for HYPE/USDC keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IKrakenTrackerFactory interface
var factory = new KrakenTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USD");
// Create a tracker for ETH/USD keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IKucoinTrackerFactory interface
var factory = new KucoinTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IMexcTrackerFactory interface
var factory = new MexcTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IOKXTrackerFactory interface
var factory = new OKXTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IWhiteBitTrackerFactory interface
var factory = new WhiteBitTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
// Either create a new factory or inject the IXTTrackerFactory interface
var factory = new XTTrackerFactory();
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
var startResult = await tracker.StartAsync();
if (!startResult.Success)
{
// Handle error, error info available in startResult.Error
}
// Tracker has successfully started
// Note that it might not be fully synced yet, check tracker.Status for this.
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
await tracker.StopAsync();
Stats and comparing data
Using the tracker.GetData(fromTime, toTime)
method the trackers exposes the data, or a subset of the data, can be retrieved:
// Get all the data currently tracked:
var data = tracker.GetData();
// Get the data for the last minute:
var data = tracker.GetData(DateTime.UtcNow.AddMinutes(-1));
// Get the data for the second last minute:
var data = tracker.GetData(DateTime.UtcNow.AddMinutes(-2), DateTime.UtcNow.AddMinutes(-1));
In a similar way statistics about the full data, or a subset of it, can be retrieved using the tracker.GetStats(fromTime, toTime)
method:
// Get statistics on all the data:
var stats = tracker.GetStats();
// Get statistics for the last minute:
var stats = tracker.GetStats(DateTime.UtcNow.AddMinutes(-1));
// Get statistics for the second last minute:
var stats = tracker.GetStats(DateTime.UtcNow.AddMinutes(-2), DateTime.UtcNow.AddMinutes(-1));
See below for an overview of what these stats include.
Stats can also be compared to eachother to see how much values have changed using the stats.CompareTo(otherStats)
method:
var statsSecondLastMinute = tracker.GetStats(compareTime.AddMinutes(-2), compareTime.AddMinutes(-1));
var statsLastMinute = tracker.GetStats(compareTime.AddMinutes(-1), compareTime);
var comparison = statsLastMinute.CompareTo(statsSecondLastMinute);
Console.WriteLine($"The volume of last minute compared to the minute before that: {comparison.VolumeDif?.Difference} ({comparison.VolumeDif?.PercentageDifference}%)");
// Output: The volume of last minute compared to the minute before that: -1261,57350000 (-85,2572%)
The TradeTracker
object
The following properties and events are exposed by the TradeTracker
object:
Field | Description |
Count |
The total number of trades currently tracked |
Exchange |
The name of the exchange |
SymbolName |
The name of the symbol being tracked |
Symbol |
The symbol as passed in the constructor |
Limit |
The max number of results tracked |
Period |
The max age of results tracked |
SyncedFrom |
The timestamp from which on the trades are registered |
Status |
The current synchronization status. Note the PartiallySynced means that the connection is active, but the data set is not yet complete. For example if the tracker is set to track 5 minutes of trades, it could be that currently only 3 minutes of data is tracked and it will take 2 more minutes to be fully synced. |
Last |
The current last trade |
OnAdded |
Event for when a new trade is added |
OnRemoved |
Event for when a trade is removed from the tracker due to not being within the set tracking period/limit anymore |
OnStatusChanged |
Event for when the status of the tracker changes |
The following properties are available in the TradesStats
object:
Field | Description |
TradeCount |
The number trades in this data set |
FirstTradeTime |
The timestamp of the first trade in this data set |
LastTradeTime |
The timestamp of the last trade in this data set |
AveragePrice |
The average price of the trades in this dataset |
VolumeWeightedAveragePrice |
The volume weighted average price for trades in this dataset |
Volume |
The total volume of all trades in this set |
QuoteVolume |
The total volume of all trades in this set denoted in the quote asset |
BuySellRatio |
The buy sell ratio of all trades in this data set. The factor of how much of the trades were a buy. |
Complete |
Whether the data set is complete. A set is not complete when not all data is available. For example, when only 1 hour of data is available in the tracker but stats are requested for the last 2 hours. |
The KlineTracker
object
The following properties and events are exposed by the KlineTracker
object:
Field | Description |
Count |
The total number of klines currently tracked |
Exchange |
The name of the exchange |
SymbolName |
The name of the symbol being tracked |
Symbol |
The symbol as passed in the constructor |
Limit |
The max number of results tracked |
Period |
The max age of results tracked |
SyncedFrom |
The timestamp from which on the klines are registered |
Status |
The current synchronization status. Note the PartiallySynced means that the connection is active, but the data set is not yet complete. For example if the tracker is set to track 10 hours of klines, it could be that currently only 9 hours of data is tracked and it will take another hour to be fully synced. |
Last |
The current last kline |
OnAdded |
Event for when a new kline is added |
OnRemoved |
Event for when a kline is removed from the tracker due to not being within the set tracking period/limit anymore |
OnStatusChanged |
Event for when the status of the tracker changes |
The following properties are available on the KlinesStats
object:
Field | Description |
KlineCount |
The number klines in this data set |
FirstOpenTime |
The open time of the first kline in this data set |
LastOpenTime |
The open time of the last kline in this data set |
LowPrice |
The lowest price for all klines in this set |
HighPrice |
The highest price for all kline in this set |
Volume |
The total volume of all klines in this set |
AverageVolume |
The average volume per kline for all klines in this set |
Complete |
Whether the data set is complete. A set is not complete when not all data is available. For example, when only 1 hour of data is available in the tracker but stats are requested for the last 2 hours. |
Logging
The library provides extensive logging, which depends on the dotnet `Microsoft.Extensions.Logging.ILogger` interface. This should provide ease of use when connecting the library logging to your existing logging implementation.
When using the Dotnet dependency injection the logging configuration will de determined by the ILogger configuration.
External log providers
External logging libraries can be configured as expected. For example Serilog:
using Binance.Net;
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddBinance();
builder.Host.UseSerilog();
var app = builder.Build();
// startup
app.Run();
Logging without dependency injection
A LoggerFactory instance can be provided to the client to configure the logging
var logFactory = new LoggerFactory();
logFactory.AddProvider(new ConsoleLoggerProvider());
var binanceClient = new BinanceRestClient(new HttpClient(), logFactory, options => { });
Ratelimiting
The client libraries have build in support for rate limiting. Rate limiting in this case means that requests are throttled (or failed before sending based on configuration) when the client detects a server rate limit will be exceeded. Whether or not rate limiting is applied can be configured in the DI registration or client options. Not all libraries currently have rate limiting configured.
What to do when a limit is reached can be configured with the RateLimitingBehaviour
client options, either Fail
for returning an error or Wait
to wait until the request can safely be send.
Client side rate limiting can only correctly work if there is only a single program talking to the exchange. When multiple different application send requests at the same time it's impossible for the client side to keep track of the rate limits. When using multiple concurrent applications it is advised to turn off rate limiting. Also note that when requests are rate limited with RateLimitBehaviour.Wait that the order of the requests being send is not guarenteed.
Client side rate limiting is currently implemented for the following libraries:
services.AddCryptoClients(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static Exchanges.RateLimiter
exposes an event which triggers when a rate limit is reached
BinanceExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
// Output: Limit triggered: RateLimitEvent { ApiLimit = Spot Socket, LimitDescription = Limit of 6000 per 00:01:00, RequestDefinition = GET 1, Host = wss://ws-api.binance.com, Current = 5752, RequestWeight = 250, Limit = 6000, TimePeriod = 00:01:00, DelayTime = 00:00:38.7784145, Behaviour = Wait }
services.AddBinance(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static BinanceExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
BinanceExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
// Output: Limit triggered: RateLimitEvent { ApiLimit = Spot Socket, LimitDescription = Limit of 6000 per 00:01:00, RequestDefinition = GET 1, Host = wss://ws-api.binance.com, Current = 5752, RequestWeight = 250, Limit = 6000, TimePeriod = 00:01:00, DelayTime = 00:00:38.7784145, Behaviour = Wait }
services.AddBingX(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static BingXExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
BingXExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddBitget(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static BitgetExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
BitgetExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddBitMart(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static BitMartExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
BitMartExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddCoinbase(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static CoinbaseExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
CoinbaseExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddCryptoCom(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static CryptoComExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
CryptoComExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddGateIo(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static GateIoExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
GateIoExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddHTX(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static HTXExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
HTXExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddHyperLiquid(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static HyperLiquidExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
HyperLiquidExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddKraken(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static KrakenExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
KrakenExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
// Output: Limit triggered: RateLimitEvent { ApiLimit = Spot Rest, LimitDescription = Limit of 15 with a decay rate of 0,33, RequestDefinition = POST 0/private/TradesHistory authenticated, Host = api.kraken.com, Current = 14, RequestWeight = 2, Limit = 15, TimePeriod = 00:00:01, DelayTime = 00:00:04, Behaviour = Wait }
Kraken applies different rate limits based on the account verification tier. By default the rate limit is set to the most conservative Starter
tier. To change the rate limit tier call the Configure
method
KrakenExchange.RateLimiter.Configure(Kraken.Net.Enums.RateLimitTier.Pro);
services.AddKucoin(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static KucoinExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
KucoinExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
// Output: Limit triggered: RateLimitEvent { ApiLimit = Public Rest, LimitDescription = Limit of 2000 per 00:00:30, RequestDefinition = GET api/v1/market/stats, Host = https://api.kucoin.com/, Current = 1995, RequestWeight = 15, Limit = 2000, TimePeriod = 00:00:30, DelayTime = 00:00:19.8111238, Behaviour = Wait }
Kucoin applies different rate limits based on the account VIP level. By default the rate limit is set to the most conservative VIP0
tier. To change the rate limit tier call the Configure
method
KucoinExchange.RateLimiter.Configure(Kucoin.Net.Enums.VipLevel.Vip5);
services.AddMexc(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static MexcExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
MexcExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddOKX(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static OKXExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
OKXExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddWhiteBit(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static WhiteBitExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
WhiteBitExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
services.AddXT(x =>
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
}, x =>
{
x.RatelimiterEnabled = true;
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
});
To be notified of when a rate limit is hit the static XTExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
XTExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
Caching
Every REST API client based on the CryptoExchange.Net base library automatically supports caching of GET HTTP requests. A few advantages of caching:
- Performance improvement, data response will be much faster as no roundtrip to the server is needed
- Reduced resource usage, returning data from the cache uses less resources than reading the server response, though there is some memory overhead
- Prevent rate limiting, the cache can be queried as many times as you like without having to worry about getting rate limited by the server
Caching is only applied for successful GET requests as GET requests by definition should not change state. Other HTTP methods (POST, DELETE, etc) generally do change state, so caching those call would prevent an action being executed.
To enable caching for GET requests set CachingEnabled
to true
in the client options. Optionally set the CachingMaxAge
option to the desired value (default is 5 seconds).
To determine whether a request has gotten the data from the server or from the local cache the DataSource
property on the call result can inspected:
var result = await bitfinexRestClient.SpotApi.Account.Get30DaySummaryAndFeesAsync();
var responseSource = result.DataSource;
Examples
See also the Examples folder in the source
Get Symbols
Get a list of supported symbols on the exchange and information about the symbols
// This example uses Binance, but can be any supported exchange. For example Bybit, Kraken, Kucoin etc
// Directly reference the Exchange API:
await exchangeRestClient.Binance.SpotApi.ExchangeData.GetExchangeInfoAsync();
// Or make it fully dynamic, either request on a single exchange:
await exchangeRestClient.GetSpotSymbolClient("Binance")!.GetSpotSymbolsAsync(new GetSymbolsRequest());
// Or request multiple exchanges at the same time:
await exchangeRestClient.GetSpotSymbolsAsync(new GetSymbolsRequest(), ["Binance", "Kraken", "OKX"]);
await binanceClient.SpotApi.ExchangeData.GetExchangeInfoAsync();
await bingXClient.SpotApi.ExchangeData.GetSymbolsAsync();
await bitfinexClient.SpotApi.ExchangeData.GetSymbolsAsync();
await bitgetClient.SpotApiV2.ExchangeData.GetSymbolsAsync();
await bitMartClient.SpotApi.ExchangeData.GetSymbolsAsync();
await bybitClient.V5Api.ExchangeData.GetSpotSymbolsAsync();
await coinbaseClient.AdvancedTradeApi.ExchangeData.GetSymbolsAsync();
await coinExClient.SpotApiV2.ExchangeData.GetSymbolsAsync();
await cryptoComClient.ExchangeApi.ExchangeData.GetSymbolsAsync();
await gateIoClient.SpotApi.ExchangeData.GetSymbolsAsync();
await htxClient.SpotApi.ExchangeData.GetSymbolsAsync();
await hyperLiquidClient.SpotApi.ExchangeData.GetExchangeInfoAsync();
await krakenClient.SpotApi.ExchangeData.GetSymbolsAsync();
await kucoinClient.SpotApi.ExchangeData.GetSymbolsAsync();
await mexcClient.SpotApi.ExchangeData.GetExchangeInfoAsync();
await okxClient.UnifiedApi.ExchangeData.GetSymbolsAsync(OKXInstrumentType.Spot);
await whitebitClient.V4Api.ExchangeData.GetSymbolsAsync();
await xtClient.SpotApi.ExchangeData.GetSymbolsAsync();
Getting Ticker
Get ticker/price statistics for a specific asset pair
// This example uses Binance, but can be any supported exchange. For example Bybit, Kraken, Kucoin etc
// Directly reference the Exchange API:
await exchangeRestClient.Binance.SpotApi.ExchangeData.GetTickerAsync("ETHUSDT");
// Or make it fully dynamic, either request on a single exchange:
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
await exchangeRestClient.GetSpotTickerClient("Binance")!.GetSpotTickerAsync(new GetTickerRequest(symbol));
// Or request multiple exchanges at the same time:
await exchangeRestClient.GetSpotTickerAsync(new GetTickerRequest(symbol), ["Binance", "Kraken", "OKX"]);
await binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
await bingXClient.SpotApi.ExchangeData.GetTickersAsync("BTC-USDT");
await bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUST");
await bitgetClient.SpotApiV2.ExchangeData.GetTickersAsync("BTCUSDT");
await bitMartClient.SpotApi.ExchangeData.GetTickerAsync("BTC_USDT");
await bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT");
// Symbol endpoint and ticker endpoints are combined for Coinbase
await coinbaseClient.AdvancedTradeApi.ExchangeData.GetSymbolAsync("BTC-USDT");
await coinExClient.SpotApiV2.ExchangeData.GetTickersAsync(new[] { "BTCUSDT" });
await cryptoComClient.ExchangeApi.ExchangeData.GetTickersAsync("BTC_USDT");
await gateioClient.SpotApi.ExchangeData.GetTickersAsync("BTC_USDT");
await htxClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
// HyperLiquid API doesn't offer a symbol filter, so we have to filter client side
var tickersResult = await hyperLiquidClient.SpotApi.ExchangeData.GetExchangeInfoAndTickersAsync();
var ticker = tickersResult.Data.Tickers.Single(x => x.Symbol == "HYPE/USDC");
await krakenClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
await kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
await mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
await okxClient.UnifiedApi.ExchangeData.GetTickerAsync("BTC-USDT");
// WhiteBit API doesn't offer a symbol filter, so we have to filter client side
var tickersResult = await whitebitClient.V4Api.ExchangeData.GetTickersAsync();
var ticker = tickersResult.Data.Single(x => x.Symbol == "BTC_USDT");
await xtClient.SpotApi.ExchangeData.GetTickersAsync("btc-usdt");
Get Balances
Get balance information. Requires API credentials to be set in the client options
// This example uses Binance, but can be any supported exchange. For example Bybit, Kraken, Kucoin etc
// Directly reference the Exchange API:
await exchangeRestClient.Binance.SpotApi.Account.GetBalancesAsync();
// Or make it fully dynamic, either request on a single exchange:
await exchangeRestClient.GetBalancesClient(TradingMode.Spot, "Binance")!.GetBalancesAsync(new GetBalancesRequest());
// Or request multiple exchanges at the same time:
await exchangeRestClient.GetBalancesAsync(new GetBalancesRequest(TradingMode.Spot), ["Binance", "Kraken", "OKX"]);
await binanceClient.SpotApi.Account.GetBalancesAsync();
await bingXClient.SpotApi.Account.GetBalancesAsync();
await bitfinexClient.SpotApi.Account.GetBalancesAsync();
await bitgetClient.SpotApiV2.Account.GetSpotBalancesAsync();
await bitMartClient.SpotApi.Account.GetSpotBalancesAsync();
await bybitClient.V5Api.Account.GetBalancesAsync(AccountType.Spot);
await coinbaseClient.AdvancedTradeApi.Account.GetAccountsAsync();
await coinExClient.SpotApiV2.Account.GetBalancesAsync();
await cryptoComClient.ExchangeApi.Account.GetBalancesAsync();
await gateioClient.SpotApi.Account.GetBalancesAsync();
// Need an account id, you probably want to already have done this before placing the order
var accounts = await htxClient.SpotApi.Account.GetAccountsAsync();
var account = accounts.Data.Single(a => a.Type == AccountType.Spot);
var result = await htxClient.SpotApi.Account.GetBalancesAsync();
await hyperLiquidClient.SpotApi.Account.GetBalancesAsync();
await krakenClient.SpotApi.Account.GetBalancesAsync();
await kucoinClient.SpotApi.Account.GetAccountsAsync();
await mexcClient.SpotApi.Account.GetAccountInfoAsync();
await okxClient.UnifiedApi.Account.GetAccountBalanceAsync();
await whitebitClient.V4Api.Account.GetSpotBalancesAsync();
await xtClient.SpotApi.Account.GetBalancesAsync();
Placing Order
Place a limit buy order for 0.1 BTC at a price of 50.000 USDT. Requires API credentials to be set in the client options
// This example uses Binance, but can be any supported exchange. For example Bybit, Kraken, Kucoin etc
// Directly reference the Exchange API:
await exchangeRestClient.Binance.SpotApi.Trading.PlaceOrderAsync("BTCUSDT", Binance.Net.Enums.OrderSide.Buy, Binance.Net.Enums.SpotOrderType.Limit, 0.1m, price: 50000, timeInForce: Binance.Net.Enums.TimeInForce.GoodTillCanceled);
// Or make it fully dynamic:
await exchangeRestClient.GetSpotOrderClient("Binance")!.PlaceSpotOrderAsync(new PlaceSpotOrderRequest(new SharedSymbol(TradingMode.Spot, "ETH", "USDT"), SharedOrderSide.Buy, SharedOrderType.Limit, 0.1m, price: 50000));
await binanceClient.SpotApi.Trading.PlaceOrderAsync("BTCUSDT", OrderSide.Buy, SpotOrderType.Limit, 0.1m, price: 50000, timeInForce: TimeInForce.GoodTillCanceled);
await bingXClient.SpotApi.Trading.PlaceOrderAsync("BTC-USDT", OrderSide.Buy, OrderType.Limit, 0.1m, price: 50000);
await bitfinexClient.SpotApi.Trading.PlaceOrderAsync("tBTCUST", OrderSide.Buy, OrderType.Limit, 0.1m, 50000);
await bitgetRestClient.SpotApiV2.Trading.PlaceOrderAsync("BTCUSDT_SPBL", OrderSide.Buy, OrderType.Limit, 0.1m, timeInForce: TimeInForce.GoodTillCanceled, price: 50000);
await bitMartClient.SpotApi.Trading.PlaceOrderAsync("BTC_USDT", OrderSide.Buy, OrderType.Limit, 0.1m, price: 50000);
await bybitClient.V5Api.Trading.PlaceOrderAsync(Category.Spot, "BTCUSDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, price: 50000);
await coinbaseClient.AdvancedTradeApi.Trading.PlaceOrderAsync("BTC-USDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, price: 50000);
await coinExClient.SpotApiV2.Trading.PlaceOrderAsync("BTCUSDT", AccountType.Spot, OrderSide.Buy, OrderTypeV2.Limit, 0.1m, 50000);
await cryptoComClient.ExchangeApi.Trading.PlaceOrderAsync("BTC_USDT", OrderSide.Buy, OrderType.Limit, 0.1m, price: 50000, timeInForce: TimeInForce.GoodTillCancel);
await gateIoClient.SpotApi.Trading.PlaceOrderAsync("BTC_USDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, 50000, timeInForce: TimeInForce.GoodTillCancel);
// Need an account id, you probably want to already have done this before placing the order
var accounts = await htxClient.SpotApi.Account.GetAccountsAsync();
var account = accounts.Data.Single(a => a.Type == AccountType.Spot);
var result = await htxClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCUSDT", OrderSide.Buy, OrderType.Limit, 0.1m, price: 50000);
// BTC not support on HyperLiquid Spot trading, example uses HYPE/USDC Pair
await hyperLiquidClient.SpotApi.Trading.PlaceOrderAsync("HYPE/USDC",OrderSide.Buy, OrderType.Limit, 1m, 20);
await krakenClient.SpotApi.Trading.PlaceOrderAsync("BTCUSDT",OrderSide.Buy, OrderType.Limit, 0.1m, 50000);
await kucoinClient.SpotApi.Trading.PlaceOrderAsync("BTC-USDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, 50000);
await mexcClient.SpotApi.Trading.PlaceOrderAsync("BTCUSDT", Mexc.Net.Enums.OrderSide.Buy, Mexc.Net.Enums.OrderType.Limit, 0.1m, price: 50000);
await okxClient.UnifiedApi.Trading.PlaceOrderAsync("BTC-USDT", OKXOrderSide.Buy, OKXOrderType.LimitOrder, 0.1m, 50000);
await whitebitClient.V4Api.Trading.PlaceSpotOrderAsync("BTC_USDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, price: 50000);
await xtClient.SpotApi.Trading.PlaceOrderAsync("eth_usdt", OrderSide.Buy, OrderType.Limit, TimeInForce.GoodTillCanceled, BusinessType.Spot, 0.1m, price: 50000);
Subscribe Ticker Updates
Subscribe to the websocket ticker update stream
// This example uses Binance, but can be any supported exchange. For example Bybit, Kraken, Kucoin etc
// Directly reference the Exchange API:
await exchangeSocketClient.Binance.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHUSDT", data => {
// Handle update
});
// Or make it fully dynamic, either subscribe on a single exchange:
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
await exchangeSocketClient.GetTickerClient(TradingMode.Spot, "Binance")!.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequest(symbol), data => {
// Handle update
});
// Or subscribe on multiple exchanges at the same time:
await exchangeSocketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequest(symbol), data => {
// Handle update
}, ["Binance", "Kraken", "OKX"]);
await binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHUSDT", data => {
// Handle update
});
await bingXSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-USDT", data => {
// Handle update
});
await bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHUST", data => {
// Handle update
});
await bitgetSocketClient.SpotApiV2.SubscribeToTickerUpdatesAsync("ETHUSDT", data => {
// Handle update
});
await bitmartSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
// Handle update
});
await bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHUSDT", data => {
// Handle update
});
await coinbaseSocketClient.AdvancedTradeApi.SubscribeToTickerUpdatesAsync("ETH-USDT", data => {
// Handle update
});
await coinExSocketClient.SpotApiV2.SubscribeToTickerUpdatesAsync(new[] { "ETHUSDT" }, data => {
// Handle update
});
await cryptoComSocketClient.ExchangeApi.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
// Handle update
});
await gateioSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
// Handle update
});
await hyperLiquidSocketClient.SpotApi.SubscribeToSymbolUpdatesAsync("HYPE/USDC", data => {
// Handle update
});
await htxSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethusdt", data => {
// Handle update
});
await krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHUSD", data => {
// Handle update
});
await kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-USDT", data => {
// Handle update
});
await mexcSocketClient.SpotApi.SubscribeToMiniTickerUpdatesAsync("ETHUSDT", data => {
// Handle update
});
await okxSocketClient.UnifiedApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHUSDT", data => {
// Handle update
});
await whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
// Handle update
});
await xtSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("eth_usdt", data => {
// Handle update
});
Subscribe Order Updates
Subscribe to the websocket authenticated user order update stream
// This example uses Binance, but can be any exchange support. For example Bybit, Kraken, Kucoin etc
// Binance requires a listenkey to start the user stream
//Directly reference the Exchange API:
var listenKey = await exchangeRestClient.Binance.SpotApi.Account.StartUserStreamAsync();
await exchangeSocketClient.Binance.SpotApi.Account.SubscribeToUserDataUpdatesAsync(listenKey.Data, data =>
{
// Handle update
}, null, null, null);
// Or make it fully dynamic:
string? listenKey = null;
var listenkeyClient = exchangeRestClient.GetListenKeyClient(TradingMode.Spot, "Binance");
if (listenkeyClient != null)
{
// Exchange needs a listenkey; so request that
var listenKeyResult = await listenkeyClient.StartListenKeyAsync(new StartListenKeyRequest());
listenKey = listenKeyResult.Data;
}
await exchangeSocketClient.GetSpotOrderClient("Binance")!.SubscribeToSpotOrderUpdatesAsync(new SubscribeSpotOrderRequest(listenKey: listenKey), data =>
{
// Handle update
});
// Retrieve the listen key
var listenKey = await binanceClient.SpotApi.Account.StartUserStreamAsync();
// Subscribe using the key
await binanceSocketClient.SpotApi.Account.SubscribeToUserDataUpdatesAsync(listenKey.Data, data => {
// Handle update
}, null, null, null);
// The listen key will stay valid for 60 minutes, after this no updates will be send anymore
// To extend the life time of the listen key it is recommended to call the KeepAliveUserStreamAsync method every 30 minutes
_ = Task.Run(async () => {
while (true)
{
await Task.Delay(Timespan.FromMinutes(30));
await binanceClient.SpotApi.Account.KeepAliveUserStreamAsync(listenKey.Data);
}
});
// Retrieve the listen key
var listenKey = await bingXRestClient.SpotApi.Account.StartUserStreamAsync();
// Subscribe using the key
await bingXSocketClient.SpotApi.SubscribeToBalanceUpdatesAsync(listenKey.Data, data => {
// Handle update
});
// The listen key will stay valid for 60 minutes, after this no updates will be send anymore
// To extend the life time of the listen key it is recommended to call the KeepAliveUserStreamAsync method every 30 minutes
_ = Task.Run(async () => {
while (true)
{
await Task.Delay(Timespan.FromMinutes(30));
await bingXClient.SpotApi.Account.KeepAliveUserStreamAsync(listenKey.Data);
}
});
await bitfinexSocketClient.SpotApi.SubscribeToUserUpdatesAsync(orderHandler: data => {
// Handle update
});
await bitgetSocketClient.SpotApiV2.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});
await bitMartSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});
await bybitSocketClient.V5PrivateApi.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});
await coinbaseSocketClient.AdvancedTradeApi.SubscribeToUserUpdatesAsync(data => {
// Handle update
});
await coinExSocketClient.SpotApiV2.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});
await cryptoComSocketClient.ExchangeApi.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});
await gateioSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});
await htxSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(onOrderMatched: data => {
// Handle update
});
await hyperLiquidSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(null, data => {
// Handle update
});
// Retrieve the token
var token = await krakenClient.SpotApi.Account.GetWebsocketTokenAsync();
// Subscribe using the token
await krakenSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(token.Data.Token, data => {
// Handle update
});
await kucoinSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(data => {
// Handle update
}, null, null);
// Retrieve the listen key
var token = await mexcClient.SpotApi.Account.StartUserStreamAsync();
// Subscribe using the key
await mexcSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(token.Data, data => {
// Handle update
});
// The listen key will stay valid for 60 minutes, after this the connection is closed and reconnecting with the same listen key will fail
// To extend the life time of the listen key it is recommended to call the KeepAliveUserStreamAsync method every 30 minutes
_ = Task.Run(async () => {
while (true)
{
await Task.Delay(Timespan.FromMinutes(30));
await mexcClient.SpotApi.Account.KeepAliveUserStreamAsync(token.Data);
}
});
await okxSocketClient.UnifiedApi.Trading.SubscribeToOrderUpdatesAsync(OKXInstrumentType.Spot, null, null, data => {
// Handle update
});
// It's required for the WhiteBit API to specify which symbols to subscribe to
await whitebitSocketClient.V4Api.SubscribeToOpenOrderUpdatesAsync(["ETH_USDT", "BTC_USDT"], data => {
// Handle update
});
// Retrieve the token
var listenKey = await xtRestClient.SpotApi.Account.GetWebsocketTokenAsync();
// Subscribe using the key
await xtSocketClient.SpotApi.SubscribeToBalanceUpdatesAsync(listenKey.Data, data => {
// Handle update
});
// The listen key will stay valid for 48 hours, after this no updates will be send anymore
// To extend the life time of the token it is recommended to call the GetWebsocketTokenAsync method at a set interval which will extend the lifetime
_ = Task.Run(async () => {
while (true)
{
await Task.Delay(Timespan.FromHours(4));
await xtRestClient.SpotApi.Account.GetWebsocketTokenAsync();
}
});
Minimal API
A minimal API example allowing the caller to retrieve ticker information for a specific exchange and asset pair. This is using the CryptoClient.Net
Nuget package.
This API returns ticker information for the following path
/Ticker/[Exchange]/[BaseAsset]/[QuoteAsset]
for example
/Ticker/Kraken/ETH/BTC
using CryptoExchange.Net.Interfaces;
using CryptoClients.Net.Enums;
using CryptoClients.Net.Interfaces;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCryptoClients();
var app = builder.Build();
app.MapGet("Ticker/{exchange}/{baseAsset}/{quoteAsset}", async ([FromServices] IExchangeRestClient client, Exchange exchange, string baseAsset, string quoteAsset) =>
{
var spotClient = client.GetSpotTickerClient(exchange)!;
var result = await spotClient.GetSpotTickerAsync(new GetTickerRequest(new SharedSymbol(TradingMode.Spot, baseAsset, quoteAsset)));
return result.Data;
});
app.Run();
Glossary
Definition | Synonyms | Meaning |
Symbol |
Market, Pair |
An asset pair on which can be traded, for example BTC-ETH |
Asset |
Currency, Coin |
A coin for which you can hold balance and which makes up Symbols. For example both BTC , ETH or USD |
Trade |
Execution, Fill |
The (partial) execution of an order. Orders can have multiple trades |
Quantity |
Amount, Size |
The amount of asset |
QuoteQuantity |
Value |
The amount of quote asset |
Fee |
Commission |
The fee paid for an order, trade or withdrawal |
Kline |
Candlestick, OHLC |
K-line data, used for candlestick charts. Contains Open/High/Low/Close/Volume |
KlineInterval |
Period |
The time period of a single kline |
Open order |
Active order, Unexecuted order |
An order which has not yet been fully filled |
Closed order |
Completed order, executed order |
An order which is no longer active. Can be canceled or fully filled |
Network |
Chain |
The network of an asset. For example ETH allows multiple networks like ERC20 and BEP2 |
Orderbook |
Market depth |
A list of (the top rows of) the current best bids and asks |
Ticker |
Stats |
Statistics over the last 24 hours |
FAQ
I occasionally get a NullReferenceException, what's wrong?
You probably don't check the result status of a call and just assume the data is always there. NullReferenceExecption
will happen when you have code like this var symbol = client.GetTickersAync().Result.Data.Symbol
because the Data
property is null when the call fails. Instead check if the call is successful:
var tickerResult = await client.SpotApi.ExchangeData.GetTickersAync();
if(!tickerResult.Success)
{
// Handle error
}
else
{
// Handle result, it is now safe to access the Data property
var symbol = tickerResult.Data.Symbol;
}
The socket client stops sending updates after a little while
You probably didn't keep a reference to the socket client and it got disposed.
// WRONG
private void SomeMethod()
{
var socketClient = new BinanceSocketClient();
socketClient.Spot.SubscribeToOrderBookUpdatesAsync("BTCUSDT", data => {
// Handle data
});
}
// RIGHT
private BinanceSocketClient _socketClient = new BinanceSocketClient();
// .. rest of the class
private void SomeMethod()
{
_socketClient.Spot.SubscribeToOrderBookUpdates("BTCUSDT", data => {
// Handle data
});
}
Can I use the TestNet/US/other API with this library?
Yes, generally these are all supported and can be configured by setting the Environment in the client options. Some known environments should be available in the [Exchange]Environment class:
// Change environment to test
var client = new BinanceRestClient(options =>
{
options.Environment = BinanceEnvironment.Testnet;
});
How are timestamps handled?
Exchange API's treat all timestamps as UTC, both incoming and outgoing. The client libraries do no conversion so received timestamps are always in UTC. When sending requests make sure to use UTC time as well.